iphaaw
iphaaw

Reputation: 7204

SwiftUI: How to add CoreData record from Siri Intent

I am trying to create an Intent that saves a record to a CoreData database. The record will be created if I run the code from the main app, but not in the Intent.

Here is the code:

import Intents
import CoreData
import SwiftUI

let persistenceController = PersistenceController.shared

class IntentHandler: INExtension, DiaryIntentHandling
{
    var moc = PersistenceController.shared.context

    override func handler(for intent: INIntent) -> Any?
    {
        guard intent is DiaryIntent else
        {
            fatalError("Unknwonwn intent type: \(intent)")
        }
        return self
    }

    func handle(intent: DiaryIntent, completion: @escaping (DiaryIntentResponse) -> Void)
    {
        guard let message = message
        else
        {
            completion(DiaryIntentResponse(code: .failure, userActivity: nil))
            return
        }
         
        completion(DiaryIntentResponse.success(message: message))
        let context = PersistenceController.shared.container.viewContext

        let myRecord = MyRecord(context: context)
        myRecord.timestamp = Date()
        myRecord.message = message
        do {
            try context.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }

    }
    
    func resolveMessage(for intent: DiaryIntent, with completion: @escaping (INStringResolutionResult) -> Void)
    {
        if let message = intent.message
        {
            completion(INStringResolutionResult.success(with: message))
        }
        else
        {
            completion(INStringResolutionResult.needsValue())
        }

    }
    
    public func confirm(intent: DiaryIntent, completion: @escaping (DiaryIntentResponse) -> Void) {
        completion(DiaryIntentResponse(code: .ready, userActivity: nil))
    }
    
}

Do I need to share access to the CoreData database? How do I create the record?

Upvotes: 2

Views: 387

Answers (1)

Tom Harrington
Tom Harrington

Reputation: 70956

App extensions work like separate apps, so you need to set up an app "group" to share data between them. It gives you a directory that's not part of your app's sandbox that your app and your app extensions can share. Using one requires some setup work:

  1. Turn on app groups by adding the group entitlement. Apple has some documentation on this. I also have a somewhat old blog post that's still accurate as far as setting up the group.

  2. Set up your persistent container use the group directory for Core Data. Normally it saves data in your app's sandbox, but you can tell it to use the app group directory. To do that,

    1. Get a file URL for the directory using FileManager's function containerURL(forSecurityApplicationGroupIdentifier:). The argument is the same as your app group identifier.

    2. Make sure this directory exists! It doesn't get created automatically. Use FileManager.default.fileExists(atPath:) to check if it exists, and if not, use FileManager.default.createDirectory(at:withIntermediateDirectories:attributes:) to create it.

    3. Use a NSPersistentStoreDescription to tell your persistent container to use that URL for Core Data. That would be something like

      let persistentContainer = NSPersistentContainer(name: containerName)
      
      let persistentStoreDescription = NSPersistentStoreDescription(url: persistentStoreUrl)
      persistentStoreDescription.type = NSSQLiteStoreType
      
      persistentContainer.persistentStoreDescriptions = [ persistentStoreDescription ]
      
  3. After the previous step, the persistent container won't be able to find any data that's currently in Core Data. So:

    1. If your app has not already been released, delete it from your test devices and simulators and then rebuild. You'll get a new persistent store in the app group directory.
    2. If your app has already been released, add code to copy the persistent store from the current location to the new location. Do this before loading the persistent store, and only do it if the copy in the app group doesn't already exist (so you don't re-copy old data). The best way to do that is with the migratePersistentStore(_:to:options:withType:) function from NSPersistentStoreCoordinator. Don't just copy your SQLite file over, because that won't include all the data.

Upvotes: 1

Related Questions