1

I'm currently developing an application using SwiftUI.

I want to make a function to get some data in CoreData model giving an id in the same CoreData model.


This is an example function I want to make (doesn't work, just for explanation)

func getDataById(id:UUID) -> String { // give an id in model
    return task                       // return an task in model which has the same id of the argument
}

if I have this CoreData model:

id (UUID) | task (String)


001 | meeting

002 | shopping

003 | cooking

And if I use the function like below:

let task = getDataById(id:001)

print(task) // meeting

It works like this.


My goal is to display some task data as a list in WIdget using id data when the Widget get fetch data.

(Like values for display in a list and tags to give a value to selection when we use picker )

Here is the code:

IntentHandler.swift

import WidgetKit
import SwiftUI
import CoreData
import Intents

class IntentHandler: INExtension,ConfigurationIntentHandling {
    
    var moc = PersistenceController.shared.managedObjectContext

    func provideNameOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {

        // let request = NSFetchRequest<TimerEntity>(entityName: "TodoEntity")
        let request = NSFetchRequest<TodoEntity>(entityName: "TodoEntity")
        var nameIdentifiers:[NSString] = []

        do{
            let results = try moc.fetch(request)

            for result in results{
                nameIdentifiers.append(NSString(string: result.id?.uuidString ?? "")) // I want display task data using id data here.
            }
        }
        catch let error as NSError{
            print("Could not fetch.\(error.userInfo)")
        }

        let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
        completion(allNameIdentifiers,nil)
    }
    
    override func handler(for intent: INIntent) -> Any {
        return self
    }
}

- UPDATED -

Persistence.swift (Host App)

import CoreData
import Foundation

class PersistenceController {
    static let shared = PersistenceController()

    private init() {}

    private let persistentContainer: NSPersistentContainer = {
        let storeURL = FileManager.appGroupContainerURL.appendingPathComponent("TodoEntity")

        let container = NSPersistentContainer(name: "Todo")
        container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
        container.loadPersistentStores(completionHandler: { storeDescription, error in
            if let error = error as NSError? {
                print(error.localizedDescription)
            }
        })
        return container
    }()
}

extension PersistenceController {
    var managedObjectContext: NSManagedObjectContext {
        persistentContainer.viewContext
    }
}

extension PersistenceController {
    var workingContext: NSManagedObjectContext {
        let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        context.parent = managedObjectContext
        
        return context
    }
}

extension FileManager {
    static let appGroupContainerURL = FileManager.default
        .containerURL(forSecurityApplicationGroupIdentifier: "group.com.sample.Todo")!
}

Xcode: Version 12.0.1

iOS: 14.0

Tio
  • 944
  • 3
  • 15
  • 35

1 Answers1

1

What you need is a relation between two entities.

Solution #1 - Core Data

You can create a relation between Todo and Task directly at the Core Data level. You didn't post enough details in your question to expand more on this solution, however, you can follow these answers:

Then, you'd be able to fetch the Task associated with a Todo item and then you'd just retrieve necessary properties directly from the Task object.

Solution #2 - Map

Another possible solution is to fetch all the Task objects in the IntentHandler and then map your Todo objects to the fetched tasks.

You can try the following (just an example as the code is not testable):

class IntentHandler: INExtension, ConfigurationIntentHandling {
    
    var moc = PersistenceController.shared.managedObjectContext

    func provideNameOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
        var nameIdentifiers: [NSString] = []

        do {
            // fetch todos
            let todosRequest = NSFetchRequest<TodoEntity>(entityName: "TodoEntity")
            let todos = try moc.fetch(todosRequest)

            // fetch tasks
            let tasksRequest = NSFetchRequest<TaskEntity>(entityName: "TaskEntity")
            let tasks = try moc.fetch(tasksRequest)
            let tasksDict = Dictionary(grouping: tasks, by: \.id)

            // map `todos` to `tasks`
            nameIdentifiers = todos
                .compactMap { todo in
                    guard let id = todo.id,
                        let task = tasksDict[id]?.first?.task
                    else { 
                        return nil 
                    }
                    return NSString(string: task)
                }
        }
        catch let error as NSError {
            print("Could not fetch.\(error.userInfo)")
        }

        let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
        completion(allNameIdentifiers, nil)
    }
    
    override func handler(for intent: INIntent) -> Any {
        return self
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • Thank you for your answer, unfortunately, there was a miswriting of the code... and I just updated now `let request = NSFetchRequest(entityName: "TodoEntity")` in IntentHandler.swift. is collected. So my `CoreData` has only one `Entity`(TodoEntity). Could you please advise me on how should solve it in this case? I'm so sorry for my mistake. – Tio Jan 10 '21 at 03:00
  • Finally I could display `task` value using `Dictionary()` and `compactMap ` by `id` in the same `Entity`, Thank you for your help! – Tio Jan 10 '21 at 05:07