Section 6/181 menit

6. EntityQuery: Resolusi Entitas dari Shortcuts

6. EntityQuery: Resolusi Entitas dari Shortcuts

EntityQuery mendefinisikan bagaimana sistem menemukan dan memfilter entities ketika user memilih parameter di Shortcuts atau Siri.

Basic EntityQuery

swift
struct TaskEntityQuery: EntityQuery {
    // Lookup by ID — dipanggil saat Shortcuts me-restore saved workflow
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        let tasks = try await TaskRepository.shared.fetch(ids: identifiers)
        return tasks.map { TaskEntity(task: $0) }
    }
    
    // Semua entities untuk picker — dipanggil saat user tap parameter di Shortcuts
    func suggestedEntities() async throws -> [TaskEntity] {
        let tasks = try await TaskRepository.shared.fetchAll()
        return tasks.map { TaskEntity(task: $0) }
    }
}

EnumerableEntityQuery: Untuk Dataset Kecil yang Bisa Diambil Sekaligus

swift
struct TagEntityQuery: EnumerableEntityQuery {
    // Semua entities sekaligus — cocok untuk dataset <100 item
    func allEntities() async throws -> [TagEntity] {
        let tags = try await TagRepository.shared.fetchAll()
        return tags.map { TagEntity(tag: $0) }
    }
}

EntityStringQuery: Dengan Search Support

swift
struct ContactEntityQuery: EntityQuery, EntityStringQuery {
    func entities(for identifiers: [String]) async throws -> [ContactEntity] {
        return try await ContactService.shared.fetch(ids: identifiers)
            .map { ContactEntity(contact: $0) }
    }
    
    func suggestedEntities() async throws -> [ContactEntity] {
        // Tampilkan recent contacts sebagai suggestion
        let recentContacts = try await ContactService.shared.fetchRecent(limit: 10)
        return recentContacts.map { ContactEntity(contact: $0) }
    }
    
    // Search saat user ketik di Shortcuts parameter picker
    func entities(matching string: String) async throws -> [ContactEntity] {
        let results = try await ContactService.shared.search(query: string)
        return results.map { ContactEntity(contact: $0) }
    }
}

EntityPropertyQuery: Filter Canggih

swift
// Memungkinkan user filter entities berdasarkan property (seperti Shortcuts "Filter" block)
struct TaskEntityQuery: EntityQuery, EntityPropertyQuery {
    // Definisi property yang bisa difilter
    static var properties = EntityQueryProperties<TaskEntity, NSPredicate> {
        Property(\TaskEntity.$title) {
            EqualToComparator { NSPredicate(format: "title == %@", $0) }
            ContainsComparator { NSPredicate(format: "title CONTAINS[cd] %@", $0) }
        }
        Property(\TaskEntity.$isCompleted) {
            EqualToComparator { NSPredicate(format: "isCompleted == %@", NSNumber(value: $0)) }
        }
        Property(\TaskEntity.$priority) {
            EqualToComparator { NSPredicate(format: "priority == %@", $0.rawValue) }
        }
    }
    
    // Definisi sort options yang tersedia
    static var sortingOptions = SortingOptions {
        SortableBy(\TaskEntity.$title)
        SortableBy(\TaskEntity.$dueDate)
    }
    
    func entities(
        matching comparators: [NSPredicate],
        mode: ComparatorMode,
        sortedBy: [Sort<TaskEntity>],
        limit: Int?
    ) async throws -> [TaskEntity] {
        let compound = mode == .and
            ? NSCompoundPredicate(andPredicateWithSubpredicates: comparators)
            : NSCompoundPredicate(orPredicateWithSubpredicates: comparators)
        
        return try await TaskRepository.shared.fetch(
            predicate: compound,
            sortDescriptors: sortedBy.map { $0.nsSortDescriptor },
            limit: limit
        ).map { TaskEntity(task: $0) }
    }
    
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        try await TaskRepository.shared.fetch(ids: identifiers).map { TaskEntity(task: $0) }
    }
    
    func suggestedEntities() async throws -> [TaskEntity] {
        try await TaskRepository.shared.fetchPending().map { TaskEntity(task: $0) }
    }
}