@FetchRequest trong SwiftUI — nó là gì, truy xuất Core Data và cơ chế

Tác giả: IT Sectr Đã đăng: 2026-06-25 Thời gian đọc: 8 phút

@FetchRequest trong SwiftUI — một property wrapper để tự động truy xuất dữ liệu từ Core Data với cập nhật giao diện phản ứng. Wrapper nhận NSFetchRequest với các vị từ lọc và sắp xếp, và kết quả — một mảng NSManagedObject — tự động cập nhật khi có bất kỳ thay đổi nào trong ngữ cảnh Core Data. Theo Apple Developer Documentation (2025), @FetchRequest yêu cầu managedObjectContext trong môi trường SwiftUI và hỗ trợ các vị từ động, cung cấp kết nối phản ứng giữa Core Data và giao diện người dùng.

Những điểm chính

  • @FetchRequest — property wrapper để truy xuất Core Data phản ứng trong SwiftUI
  • Tự động cập nhật giao diện khi thêm, xóa hoặc sửa bản ghi trong Core Data
  • Vị từ và sắp xếp được đặt trong bộ khởi tạo hoặc qua tham số property wrapper
  • managedObjectContext phải có sẵn qua @Environment trong hệ phân cấp View
  • Vị từ động cho phép thay đổi điều kiện lọc trong thời gian chạy

@FetchRequest trong SwiftUI là gì?

@FetchRequest là một property wrapper SwiftUI tạo và quản lý FetchRequest từ Core Data. Khi khởi tạo, wrapper chấp nhận một loại thực thể, một mảng NSSortDescriptor để sắp xếp và một NSPredicate tùy chọn để lọc. Kết quả được lưu trữ dưới dạng mảng các đối tượng thuộc loại NSManagedObject được chỉ định và tự động cập nhật khi có thay đổi trong lớp lưu trữ.

Tính năng chính của @FetchRequest là khả năng phản ứng. Khi dữ liệu trong Core Data thay đổi (save, insert, delete, update), SwiftUI tự động thực hiện truy xuất lại và vẽ lại tất cả các View sử dụng fetch request này. Nhà phát triển không cần gọi thủ công refresh, đăng ký NSManagedObjectContextDidSaveNotification hoặc tải lại dữ liệu. SwiftUI quản lý toàn bộ chu kỳ đồng bộ hóa.

Để @FetchRequest hoạt động, cần có NSManagedObjectContext trong môi trường SwiftUI qua @Environment(\.managedObjectContext). Ngữ cảnh thường được truyền từ gốc ứng dụng, nơi NSPersistentContainer được tạo. Mô hình tiêu chuẩn là sử dụng ViewModel hoặc App struct để cấu hình ngăn xếp Core Data và truyền ngữ cảnh qua bộ sửa đổi .environment().

Sắp xếp và vị từ của FetchRequest

@FetchRequest chấp nhận một số tham số để cấu hình truy xuất. SortDescriptors — một mảng NSSortDescriptor xác định thứ tự bản ghi. Predicate — NSPredicate để lọc, ví dụ: chỉ truy xuất tác vụ có trạng thái “hoàn thành”. Animation — hoạt ảnh để cập nhật danh sách khi dữ liệu thay đổi.

Các vị từ Core Data sử dụng định dạng biểu thức giống SQL: NSPredicate(format: “status == %@”, “completed”). Có thể kết hợp nhiều điều kiện qua AND, OR và NOT. SortDescriptors xác định thứ tự: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). Để sắp xếp theo nhiều trường, một mảng các bộ mô tả được truyền — cái đầu tiên được áp dụng làm chính, những cái còn lại làm phụ.

swift
struct TaskListView: View {
    @Environment(\.managedObjectContext) var viewContext

    @FetchRequest(
        sortDescriptors: [
            NSSortDescriptor(keyPath: \.Task.dueDate, ascending: true),
            NSSortDescriptor(keyPath: \.Task.priority, ascending: false)
        ],
        predicate: NSPredicate(format: "isCompleted == NO"),
        animation: .default
    )
    var tasks: FetchedResults<Task>

    var body: some View {
        List(tasks) { task in
            Text(task.title ?? "")
        }
    }
}

Trong ví dụ, @FetchRequest truy xuất tất cả tác vụ chưa hoàn thành (isCompleted == NO), được sắp xếp trước theo ngày (tăng dần), sau đó theo mức độ ưu tiên (giảm dần). FetchedResults<Task> là một bộ sưu tập tự động cập nhật hoạt động như một mảng cho ForEach và List. Hoạt ảnh .default đảm bảo sự xuất hiện/biến mất mượt mà của các bản ghi khi có thay đổi.

@FetchRequest với List và dữ liệu động

@FetchRequest kết hợp hoàn hảo với List và ForEach để hiển thị danh sách Core Data động. Kết quả truy xuất (FetchedResults) tuân thủ giao thức RandomAccessCollection, do đó có thể được sử dụng trực tiếp trong List và ForEach. Khi thêm, xóa hoặc sửa bản ghi, danh sách tự động được vẽ lại với hoạt ảnh.

Để xóa bản ghi, phương thức ngữ cảnh delete được sử dụng. Để thêm bản ghi, một NSManagedObject mới được tạo và save được gọi. Tất cả thay đổi được thực hiện qua viewContext, cùng với @FetchRequest đảm bảo tính nhất quán dữ liệu. Để chỉnh sửa, @ObservedObject hoặc @Bindable được sử dụng cho một đối tượng Core Data cụ thể.

swift
struct TaskListWithActions: View {
    @Environment(\.managedObjectContext) var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \.Task.createdAt,
                                               ascending: false)]
    )
    var tasks: FetchedResults<Task>

    var body: some View {
        List {
            ForEach(tasks) { task in
                HStack {
                    Text(task.title ?? "")
                    Spacer()
                    Image(systemName: task.isCompleted
                               ? "checkmark.circle.fill"
                               : "circle")
                }
                .onTapGesture { toggleTask(task) }
            }
            .onDelete(perform: deleteTasks)
        }
        .toolbar {
            Button(systemImage: "plus") { addTask() }
        }
    }

    private func addTask() {
        let newTask = Task(context: viewContext)
        newTask.title = "New task \(tasks.count + 1)"
        newTask.createdAt = Date()
        try? viewContext.save()
    }

    private func toggleTask(_ task: Task) {
        task.isCompleted.toggle()
        try? viewContext.save()
    }

    private func deleteTasks(at offsets: IndexSet) {
        for index in offsets {
            viewContext.delete(tasks[index])
        }
        try? viewContext.save()
    }
}

Ví dụ này minh họa CRUD đầy đủ với @FetchRequest. Thêm: tạo Task qua ngữ cảnh và save(). Xóa: delete() trên ngữ cảnh và save(). Cập nhật: chuyển đổi thuộc tính và save(). Sau mỗi save(), @FetchRequest tự động truy xuất lại dữ liệu và List được cập nhật với hoạt ảnh. Các bộ sửa đổi onDelete và onTapGesture cung cấp tương tác iOS tiêu chuẩn.

Ví dụ mã @FetchRequest

Hãy xem xét một ví dụ ứng dụng nhật ký đầy đủ với @FetchRequest để truy xuất bản ghi. Biểu mẫu tìm kiếm sử dụng vị từ động: khi nhập văn bản, FetchRequest tự động cập nhật kết quả, lọc bản ghi theo tiêu đề và nội dung. Sắp xếp theo ngày tạo, từ mới nhất đến cũ nhất.

swift
struct JournalEntryView: View {
    @Environment(\.managedObjectContext) var viewContext
    @State private var searchText = ""

    private var searchPredicate: NSPredicate? {
        guard !searchText.isEmpty else { return nil }
        return NSPredicate(format: "title CONTAINS[c] %@", searchText)
    }

    @FetchRequest var entries: FetchedResults<JournalEntry>

    init(searchText: String) {
        let sort = [NSSortDescriptor(keyPath: \.JournalEntry.date,
                                           ascending: false)]
        let pred: NSPredicate? = searchText.isEmpty
            ? nil
            : NSPredicate(format: "title CONTAINS[c] %@ OR content CONTAINS[c] %@",
                          searchText, searchText)
        self._entries = FetchRequest(
            sortDescriptors: sort,
            predicate: pred,
            animation: .default
        )
    }

    var body: some View {
        List(entries) { entry in
            VStack(alignment: .leading) {
                Text(entry.title ?? "").font(.headline)
                Text(entry.content ?? "").font(.subheadline)
                    .lineLimit(2)
            }
        }
        .searchable(text: $searchText)
    }
}

Nhật ký có tìm kiếm: khi nhập văn bản vào trường searchable, một NSPredicate được tạo với CONTAINS[c] (tìm kiếm không phân biệt chữ hoa chữ thường) trên các trường title và content. Init chấp nhận searchText và tạo FetchRequest với vị từ tương ứng. Bộ sửa đổi searchable tự động cập nhật searchText, và @FetchRequest phản ứng với sự thay đổi vị từ và truy xuất lại dữ liệu.

Đối với các tình huống phức tạp hơn với nhiều bộ lọc, hãy kết hợp NSCompoundPredicate với logic AND/OR. @FetchRequest hỗ trợ các vị từ ở bất kỳ độ phức tạp nào, bao gồm các truy vấn con lồng nhau (SUBQUERY) để lọc theo relationship. Tuy nhiên, hãy nhớ: các vị từ phức tạp ảnh hưởng đến hiệu suất — đối với tập dữ liệu lớn (hơn 10.000 bản ghi), hãy sử dụng lập chỉ mục Core Data và giới hạn truy xuất qua fetchLimit.

Vị từ động trong thời gian chạy

@FetchRequest hỗ trợ vị từ động qua tham số predicate, có thể được thay đổi trong thời gian chạy. Để làm điều này, hãy tạo @FetchRequest với một vị từ biến và truyền nó qua Binding. Khi người dùng thay đổi bộ lọc — ví dụ: chọn một danh mục tác vụ — vị từ được cập nhật và việc truy xuất tự động khởi động lại.

Các vị từ động được triển khai qua thuộc tính @State hoặc @Published, được truyền dưới dạng Binding tới bộ khởi tạo @FetchRequest. SwiftUI theo dõi các thay đổi của Binding này và tạo lại NSFetchRequest với vị từ mới, tự động cập nhật danh sách.

Câu hỏi thường gặp

Làm thế nào để truyền managedObjectContext vào @FetchRequest?

Thêm @Environment(\.managedObjectContext) var viewContext trong View gốc và truyền nó qua bộ sửa đổi .environment(\.managedObjectContext, context). Tất cả các View con sử dụng @FetchRequest sẽ tự động nhận ngữ cảnh từ môi trường.

Có thể cập nhật @FetchRequest mà không cần tải lại View không?

Có, hãy sửa đổi ngữ cảnh Core Data: thêm, xóa hoặc cập nhật một đối tượng và gọi save(). @FetchRequest sẽ tự động thực hiện truy xuất lại và cập nhật giao diện. Để cập nhật cưỡng bức mà không cần save(), hãy sử dụng viewContext.refreshAllObjects().

Sự khác biệt giữa @FetchRequest và @SectionedFetchRequest là gì?

@FetchRequest trả về một mảng phẳng các đối tượng. @SectionedFetchRequest nhóm kết quả theo một key path được chỉ định thành các phần, tương tự UITableView với sectionNameKeyPath. Sử dụng @SectionedFetchRequest cho danh sách được nhóm với tiêu đề phần.

Làm thế nào để sử dụng @FetchRequest với Relationships?

Để lọc theo relationship, sử dụng ký hiệu dấu chấm: NSPredicate(format: “category.name == %@”, “Work”). @FetchRequest tự động thực hiện JOIN trong SQLite. Để truy cập relationship qua một đối tượng đã chọn, sử dụng các thuộc tính NSManagedObject thông thường.

@FetchRequest có hỗ trợ phân trang không?

@FetchRequest không hỗ trợ phân trang trực tiếp, vì nó được thiết kế để hiển thị tất cả bản ghi với cập nhật phản ứng. Để phân trang, hãy sử dụng NSFetchRequest với fetchLimit/fetchOffset và tải dữ liệu thủ công khi cuộn danh sách.

Tổng kết

  • @FetchRequest — property wrapper để tự động truy xuất Core Data phản ứng trong SwiftUI
  • Sắp xếp được đặt bởi mảng NSSortDescriptor, lọc bởi NSPredicate
  • Tự động cập nhật giao diện khi bất kỳ thay đổi dữ liệu nào trong ngữ cảnh Core Data
  • managedObjectContext được truyền qua @Environment trong hệ phân cấp View
  • Vị từ động được triển khai qua init tùy chỉnh với tham số vị từ
  • Thao tác CRUD được thực hiện qua viewContext với đồng bộ hóa danh sách tự động
  • Sử dụng @FetchRequest để hiển thị Core Data, @SectionedFetchRequest cho danh sách nhóm

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm