@FetchRequest در SwiftUI — property wrapper برای واکشی خودکار دادهها از Core Data با بهروزرسانی واکنشگرای رابط کاربری. Wrapper یک NSFetchRequest با predicateهای فیلتر و مرتبسازی میپذیرد و نتیجه — آرایهای از NSManagedObject — به طور خودکار با هر تغییری در بافت Core Data بهروزرسانی میشود. بر اساس Apple Developer Documentation (2025), @FetchRequest به managedObjectContext در محیط SwiftUI نیاز دارد و از predicateهای پویا پشتیبانی میکند و ارتباط واکنشگرای بین Core Data و رابط کاربری را تضمین میکند.
نکات اصلی
@FetchRequest یک property wrapper SwiftUI است که یک FetchRequest از Core Data ایجاد و مدیریت میکند. در زمان مقداردهی، wrapper نوع entity، آرایهای از NSSortDescriptor برای مرتبسازی و یک NSPredicate اختیاری برای فیلتر دریافت میکند. نتیجه به عنوان آرایهای از اشیاء NSManagedObject از نوع مشخص شده ذخیره میشود و به طور خودکار با تغییرات در لایه persistence بهروزرسانی میشود.
ویژگی کلیدی @FetchRequest واکنشگرایی است. وقتی دادهها در Core Data تغییر میکنند (save، insert، delete، update)، SwiftUI به طور خودکار واکشی مجدد را انجام میدهد و تمام Viewهایی که از این fetch request استفاده میکنند را دوباره ترسیم میکند. توسعهدهنده نیازی به فراخوانی دستی refresh، اشتراک در NSManagedObjectContextDidSaveNotification یا بارگذاری مجدد دادهها ندارد. SwiftUI کل چرخه همگامسازی را مدیریت میکند.
برای کار با @FetchRequest به NSManagedObjectContext نیاز است که در محیط SwiftUI از طریق @Environment(\.managedObjectContext) قابل دسترسی است. بافت معمولاً از ریشه برنامه ارسال میشود، جایی که NSPersistentContainer ایجاد میشود. الگوی استاندارد استفاده از ViewModel یا App struct برای تنظیم Core Data stack و ارسال بافت از طریق modifier .environment() است.
@FetchRequest چندین پارامتر برای تنظیم واکشی دریافت میکند. SortDescriptors — آرایهای از NSSortDescriptor که ترتیب رکوردها را تعیین میکند. Predicate — NSPredicate برای فیلتر، مثلاً انتخاب فقط وظایف با وضعیت «تکمیل شده». Animation — انیمیشن برای بهروزرسانی لیست هنگام تغییر دادهها.
Predicateهای Core Data از فرمت عبارات SQL-like استفاده میکنند: NSPredicate(format: "status == %@", "completed"). میتوان چندین شرط را از طریق AND، OR و NOT ترکیب کرد. SortDescriptors ترتیب را تعیین میکنند: NSSortDescriptor(keyPath: \Task.dueDate, ascending: true). برای مرتبسازی بر اساس چندین فیلد، آرایهای از descriptorها ارسال میشود — اولی به عنوان اصلی و بقیه به عنوان ثانویه اعمال میشوند.
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 ?? "")
}
}
}
در مثال، @FetchRequest تمام وظایف تکمیلنشده (isCompleted == NO) را که ابتدا بر اساس تاریخ (صعودی) و سپس بر اساس اولویت (نزولی) مرتب شدهاند انتخاب میکند. FetchedResults<Task> یک مجموعه خودکار بهروزرسانیشونده است که مانند یک آرایه برای ForEach و List رفتار میکند. انیمیشن .default ظاهر/ناپدید شدن روان رکوردها را هنگام تغییرات تضمین میکند.
@FetchRequest به طور ایدهآل با List و ForEach برای نمایش لیستهای پویای Core Data ترکیب میشود. نتیجه واکشی (FetchedResults) با پروتکل RandomAccessCollection مطابقت دارد، بنابراین میتوان آن را مستقیماً در List و ForEach استفاده کرد. هنگام افزودن، حذف یا تغییر رکوردها، لیست به طور خودکار با انیمیشن دوباره ترسیم میشود.
برای حذف رکوردها از متد delete بافت استفاده میشود. برای افزودن — ایجاد یک NSManagedObject جدید و فراخوانی save. تمام تغییرات از طریق viewContext انجام میشوند که همراه با @FetchRequest سازگاری دادهها را تضمین میکند. برای ویرایش از @ObservedObject یا @Bindable برای شیء خاص Core Data استفاده میشود.
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: "افزودن") { 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()
}
}
این مثال CRUD کامل با @FetchRequest را نشان میدهد. افزودن: ایجاد Task از طریق بافت و save(). حذف: delete() روی بافت و save(). بهروزرسانی: toggle ویژگی و save(). پس از هر save()، @FetchRequest به طور خودکار دادهها را دوباره واکشی میکند و List با انیمیشن بهروزرسانی میشود. modifierهای onDelete و onTapGesture تعاملات استاندارد iOS را فراهم میکنند.
بیایید یک مثال کامل از برنامه دفترچه یادداشت با @FetchRequest برای واکشی رکوردها را بررسی کنیم. فرم جستجو از predicate پویا استفاده میکند: هنگام وارد کردن متن، FetchRequest به طور خودکار نتایج را بهروزرسانی میکند و رکوردها را بر اساس عنوان و محتوا فیلتر میکند. مرتبسازی بر اساس تاریخ ایجاد، از جدید به قدیم.
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)
}
}
دفترچه یادداشت با جستجو: هنگام وارد کردن متن در فیلد searchable، یک NSPredicate با CONTAINS[c] (جستجوی غیرحساس به بزرگی/کوچکی حروف) در فیلدهای title و content ایجاد میشود. Init مقدار searchText را دریافت میکند و FetchRequest را با predicate مربوطه ایجاد میکند. modifier Searchable به طور خودکار searchText را بهروزرسانی میکند و @FetchRequest به تغییر predicate واکنش نشان داده و دادهها را دوباره واکشی میکند.
برای سناریوهای پیچیدهتر با فیلترهای متعدد، NSCompoundPredicate را با منطق AND/OR ترکیب کنید. @FetchRequest از predicateهای هر پیچیدگی، از جمله زیرپرسشهای تو در تو (SUBQUERY) برای فیلتر بر اساس relationship پشتیبانی میکند. اما به یاد داشته باشید: predicateهای پیچیده بر عملکرد تأثیر میگذارند — برای مجموعه دادههای بزرگ (بیش از ۱۰۰۰۰ رکورد) از نمایهسازی Core Data استفاده کنید و واکشی را از طریق fetchLimit محدود کنید.
@FetchRequest از predicateهای پویا از طریق پارامتر predicate پشتیبانی میکند که میتوان در زمان اجرا تغییر داد. برای این کار باید یک @FetchRequest با متغیر predicate ایجاد کرد و آن را از طریق Binding ارسال کرد. وقتی کاربر فیلتر را تغییر میدهد — مثلاً دستهبندی وظایف را انتخاب میکند — predicate بهروزرسانی میشود و واکشی به طور خودکار دوباره اجرا میشود.
Predicateهای پویا از طریق ویژگی @State یا @Published پیادهسازی میشوند که به عنوان Binding به مقداردهنده @FetchRequest ارسال میشود. SwiftUI تغییر این Binding را دنبال میکند و NSFetchRequest را با predicate جدید دوباره ایجاد میکند و به طور خودکار لیست را بهروزرسانی میکند.
سوالات متداول
@Environment(\.managedObjectContext) var viewContext را در View ریشه اضافه کنید و آن را از طریق modifier .environment(\.managedObjectContext, context) ارسال کنید. تمام Viewهای فرزند که از @FetchRequest استفاده میکنند به طور خودکار بافت را از محیط دریافت میکنند.
بله، بافت Core Data را تغییر دهید: یک شیء را اضافه، حذف یا بهروزرسانی کنید و save() را فراخوانی کنید. @FetchRequest به طور خودکار واکشی مجدد را انجام داده و UI را بهروزرسانی میکند. برای بهروزرسانی اجباری بدون save() از viewContext.refreshAllObjects() استفاده کنید.
@FetchRequest یک آرایه مسطح از اشیاء برمیگرداند. @SectionedFetchRequest نتایج را بر اساس key path مشخص شده به بخشهایی گروهبندی میکند، مشابه UITableView با sectionNameKeyPath. از @SectionedFetchRequest برای لیستهای گروهبندی شده با عنوان بخش استفاده کنید.
برای فیلتر بر اساس relationship از نقطهگذاری استفاده کنید: NSPredicate(format: "category.name == %@", "Work"). @FetchRequest به طور خودکار JOIN را در SQLite اجرا میکند. برای دسترسی به relationship از طریق شیء انتخاب شده، از ویژگیهای معمول NSManagedObject استفاده کنید.
@FetchRequest به طور مستقیم از صفحهبندی پشتیبانی نمیکند، زیرا برای نمایش همه رکوردها با بهروزرسانی واکنشگرا طراحی شده است. برای صفحهبندی از NSFetchRequest با fetchLimit/fetchOffset استفاده کنید و دادهها را به صورت دستی هنگام اسکرول لیست بارگذاری کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید