Sharing Core Data with watch app in SwiftUI

Hi everyone!
Here's a little tricky question, at least for me.
I'm trying to add an apple watch extension to an existing app of mine (wrote in Swift).
This App uses a CoreData database and i need to retrieve that data to build the Apple Watch UI.
I decided to go for SwiftUI to code the Watch App, because of the new possibilities in the UI.

So Main App in Swift and Watch App in SwiftUI


First i created an app group of course, and a class to define a storeURL for the container:

class AppGroupPersistentContainer: NSPersistentContainer {

    override open class func defaultDirectoryURL() -> URL {
        var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.ManhattanGroup")
        storeURL = storeURL?.appendingPathComponent("Manhattan.sqlite")
        return storeURL!
    }
    
}

in my core data manager (a class i created to crud) i placed the persistant container

    // MARK: - PersistentContainer
    lazy var persistentContainer: NSPersistentContainer = {
        let container = AppGroupPersistentContainer(name: "Manhattan")

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()


then for each operation i just get the context from the persistent container.

    func getContext() -> NSManagedObjectContext {
        return persistentContainer.viewContext
    }


So far so good i guess.
In my watch app, i call the context, and use that context for the environment.

struct ContentView: View {
    var body: some View {
        let managedObjectContext = CoreDataManager.shared.getContext()
        
        return ZStack(alignment: .center) {
            CategoryView().environment(\.managedObjectContext, managedObjectContext)
        }
    }
}


finally in the CategoryView i fetch like this:

@FetchRequest(entity: CoreList.entity(), sortDescriptors: []) var coreList: FetchedResults

 var body: some View {

        return VStack(alignment: .leading) {
            List {
                ForEach(coreList, id: \.self) { object in
                    Text("\(object)")
                }
            }.environment(\.managedObjectContext, managedObjectContext)//not sure i should use this again


and save like this:

@Environment(\.managedObjectContext) var managedObjectContext

 var body: some View {

        return VStack(alignment: .leading) {
          ...     
          Button("Add") {
                let coreList = CoreList(context: self.managedObjectContext)
                
//coreList init

                try? self.managedObjectContext.save()
            }

I can save and fetch locally successfully but this operations don't take effect on the iPhone App and as you can guess, if i save from the iPhone App i can't find these values on my Watch App.
Does anyone have an idea? i think something is missing in the configuration of the app group but i just cant figure out.
Thank you in advance.

Post not yet marked as solved Up vote post of matryx87 Down vote post of matryx87
5.7k views

Replies

Did you add the app group entitlements correctly in all targets?

Hi @andre07, thank you for answering.

Actually, I did:
I added the capability in both targets and so the group.
Then, in my dev account, added the group.

AFAIK Watch apps do not naturally sync data back to the companion. This has to be done via one or more of these solutions, Keeping Your watchOS Content Up to Date.

The "easiest" way to do that would be NSPersistentCloudKitContainer, however WatchConnectivity may be a better fit depending on your needs.
If you are trying to use an App Group container to share a database between your iOS app and an WatchKit extension, that won't work because the iOS app runs on iPhone and the WatchKit extension runs on the paired watch, and an app ground container can't be cross devices. You will have to synchronize the data across the devices with your own code. As Nick points out, you can try with NSPersistentCloudKitContainer, you can also use CloudKit directly to synchronize the data you need. WatchConnectivity is not recommended if you want your watch app to be standalone.

I know this is 2 years old, but why on earth isn't NSUbiquitousKeyValueStore supported by watchOS? That would make sharing small bits of data so much easier. The fact that I have to bring up an entire CloudKit infrastructure just to share an auth key is criminal.

  • It actually is since watchOS 9 :)

Add a Comment