How to Make Watchkit Extension App and My iPhone App Share the Same Icloud Databases

Using Core Data with watchOS 2.0

Yes, you'll have to maintain two separate stores. If either side is a "read-only" client and the CoreData datastore is small and changes infrequently you could potentially use the transferFile WatchConnectivity API to transfer the whole store each time it changes.

CloudKit error using watchOS 3

According to Apple:

CloudKit usage is blocked on watchOS Simulators. Running any test will
throw a “Not Authenticated” error even though you are signed in via
the paired iOS Simulator. Workaround: Use CloudKit on paired devices
with watchOS 3 and iOS 10.

This is from the watchOS 3 release notes, but does not seem to be fixed yet. Just test on a real watch and everything will work fine.

Notification of changes in Core Data SQLite store between iPhone and Apple Watch app

The solution that I've found quite satisfactory was to use MMWormhole library.

It works by using CFNotificationCenter Darwin Notifications and writing/reading data files in the shared app group, which results in instant communication between an iOS app and an app extension (Watch app, today's widget, etc).

Basic code goes like this:

  • Wormhole initialization

    wormhole = MMWormhole(applicationGroupIdentifier: appGroup, optionalDirectory: nil)
  • Passing data object to a wormhole

    let payload = ["Key": "Value"]
    wormhole.passMessageObject(payload, identifier: theSameMessageIdentifier)
  • Listening for incoming object from a wormhole

    wormhole.listenForMessageWithIdentifier(theSameMessageIdentifier) { message -> Void in

    if let payloadDictionary = message as? Dictionary<String, String> {
    // Do some work
    }
    }

It's as simple as that.

Realm database for iOS app with Watch Extension

While in watchOS 1, watch apps were shared extensions of the parent app with access to shared files, as of watchOS 2, this is no longer the case.

Apps on watchOS 2 are completely separate from their parent iOS apps (To the point where I believe they can function while the parent app is closed). As such, it's necessary for you to communicate changes made in the iOS app to the watch.

You can use WCSession in the WatchConnectivity framework to send data between the two platforms. If you have a pre-bundled Realm file containing an initial set of data, you should be able to send the entire file over when the app initially launches. After that point, you should only send the key-value data of what changed to the watch so it remains up to date.

What is the expected lifecycle of iOS 8 App Group directories?

I also posted this on the Apple Dev forums, and got an answer back from someone in Apple developer relations. Here is what they said:

Is there any potential for the system purging this location under low disc conditions?

That won't happen.

I'm trying to determine if it is "safe" to store my app's primary core data database in this App Group location, or if I should be making copies of data there instead.

Placing your core data store in an App Group directory is perfectly reasonable.

Also, what is the intent for App Group directories?

They are there so that suites of apps can shared data. I think the clearest expression of this comes from the Mac documentation. The "App Sandbox Design Guide" says:

[...] an application can use the com.apple.security.application-groups entitlement to request access to a shared container that is common to multiple applications produced by the same development team. This container is intended for content that is not user-facing, such as shared caches or databases.

Documentation

How can I guarantee unique entries in a Core Data store in a shared app container used by both the host app and an extension?

Is there a known approach to this rather novel problem introduced by iOS 8 extensions?

Yes, it's the same approach that applies when using iCloud with Core Data: let the duplicates happen, but then go and clean them up. Both situations run the risk of creating duplicate entries, and there's no completely reliable way to prevent them. Since you have a uniqueID key, you're in good shape as far as this is concerned.

It would be a lot easier, as Dave DeLong notes, to avoid the problem in the first place. If that's impossible, you can deal with it, with some extra work.

Finding duplicates would be something like:

NSError *error = nil;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:self.persistentStoreCoordinator];

NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:@"MyEntityName"];
[fr setIncludesPendingChanges:NO];

NSExpression *countExpr = [NSExpression expressionWithFormat:@"count:(uniqueID)"];
NSExpressionDescription *countExprDesc = [[NSExpressionDescription alloc] init];
[countExprDesc setName:@"count"];
[countExprDesc setExpression:countExpr];
[countExprDesc setExpressionResultType:NSInteger64AttributeType];

NSAttributeDescription *uniqueIDAttr = [[[[[_psc managedObjectModel] entitiesByName] objectForKey:@"MyEntityName"] propertiesByName] objectForKey:@"uniqueID"];
[fr setPropertiesToFetch:[NSArray arrayWithObjects:uniqueIDAttr, countExprDesc, nil]];
[fr setPropertiesToGroupBy:[NSArray arrayWithObject:uniqueIDAttr]];

[fr setResultType:NSDictionaryResultType];

NSArray *countDictionaries = [moc executeFetchRequest:fr error:&error];

This is pretty much the Core Data equivalent of something like this in SQL:

SELECT uniqueID, COUNT(uniqueID) FROM MyEntityName GROUP BY uniqueID;

You get an array of dictionaries, each of which contains a uniqueID and a count of the number of times that value is used. Run through the dictionary and deal with duplicates appropriately.

I described this in more detail in a blog post. There's also a sample project from Apple that demonstrates the process, called SharedCoreData, but I believe it's only available as part of the WWDC 2012 sample code bundle. It was also described in session 227 at that conference.



Related Topics



Leave a reply



Submit