Section 6/181 menit

6. Kueri Data dengan @Query

6. Kueri Data dengan @Query

@Query hanya tersedia di SwiftUI view. Ia secara otomatis mengobservasi perubahan dan me-refresh view.

swift
// Fetch semua books, urutkan by title
struct BookListView: View {
    @Query(sort: \Book.title) private var books: [Book]

    var body: some View {
        List(books) { book in
            Text(book.title)
        }
    }
}

// Fetch dengan filter dan sort
struct UnreadBooksView: View {
    @Query(
        filter: #Predicate<Book> { !$0.isRead },
        sort: [SortDescriptor(\Book.addedAt, order: .reverse)]
    )
    private var unreadBooks: [Book]

    var body: some View {
        List(unreadBooks) { book in
            BookRowView(book: book)
        }
    }
}

@Query dengan Parameter Dinamis

Parameter filter di @Query bisa diubah saat runtime dengan cara passing nilai melalui initializer view child:

swift
// Parent view mengontrol filter
struct BookShelfView: View {
    @State private var filterByAuthor: String = ""
    @State private var showOnlyUnread: Bool = false

    var body: some View {
        VStack {
            FilterControls(
                authorFilter: $filterByAuthor,
                showUnread: $showOnlyUnread
            )
            FilteredBookList(
                authorFilter: filterByAuthor,
                showOnlyUnread: showOnlyUnread
            )
        }
    }
}

// Child view dengan @Query dinamis
struct FilteredBookList: View {
    @Query private var books: [Book]

    init(authorFilter: String, showOnlyUnread: Bool) {
        let predicate = #Predicate<Book> { book in
            (authorFilter.isEmpty || book.author.localizedStandardContains(authorFilter))
            && (!showOnlyUnread || !book.isRead)
        }
        _books = Query(filter: predicate, sort: \Book.title)
    }

    var body: some View {
        List(books) { book in
            BookRowView(book: book)
        }
    }
}

FetchDescriptor untuk Fetch Manual

Di luar SwiftUI (UIKit, background tasks), gunakan FetchDescriptor bersama ModelContext.fetch():

swift
// Fetch dengan limit dan offset (pagination)
var descriptor = FetchDescriptor<Book>()
descriptor.fetchLimit = 20
descriptor.fetchOffset = page * 20
descriptor.sortBy = [SortDescriptor(\Book.publishedYear, order: .reverse)]
let pagedBooks = try context.fetch(descriptor)

// Count tanpa fetch semua object
let totalCount = try context.fetchCount(FetchDescriptor<Book>())

// Fetch hanya IDs (ringan untuk lazy loading)
descriptor.includePendingChanges = false
let ids = try context.fetchIdentifiers(descriptor)