Core Data and iOS Data Storage Guidelines - core-data

We've got an app with Core Data and the backing store is a SQLite database.
It contains critical data (i.e. it needs to be always available offline).
It's currently stored in the Documents directory and so is being rejected due to iOS Data Storage Guidelines.
The solution seems to be to mark it using the "do not back up" tag.
However, I haven't seen any guidelines on this. i.e. should I manually mark this file as "do not back up" or is there some Core Data option that I should be enabling?

You'll have to do that yourself manually, here is the guide
https://developer.apple.com/library/ios/ipad/#qa/qa1719/_index.html

In iOS 5.1 or later, it's better to use the new NSURLIsExcludedFromBackupKey or kCFURLIsExcludedFromBackupKey file property, instead of setxattr (iOS 5.0.1 compatible).
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success;
}
For more information, see the "App Backup Best Practices" section of the iOS App Programming Guide.

Related

Core Data and CloudKit integration issue when renaming relationship (code 134110)

I currently have an app using Core Data in the App Store: the app allows people to record their water and sailing activities (think of it like Strava for sailors). I have not updated the app for 3 years, the app seems to be still working fine on latest iOS versions but I recently planned to improve the app.
I am currently working on an update for this app, and need to change the data model and schema. I would like to have an automatic lightweight migration. I renamed some entities, properties and relationships, but I made sure to put the previous ids in the Renaming ID field in the editor.
I want to take advantage of the opportunity to sync the updated schema on CloudKit. I followed the instruction on Apple Developer documentation to setup the sync. I also made sure to initialize the schema using initializeCloudKitSchema(). When I visit the dashboard, I see the correct schema. The container is only in development mode, not pushed into production.
When I launch the app with a sqlite file generated by the available app, it seems the migration works well because the data is still here and correct. I can navigate in the app normally and when I visit the CloudKit dashboard, the data is correctly saved.
But sometimes, the app crashes at launch with the following error:
UserInfo={reason=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.
NSUnderlyingException=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.}}}
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration."
The concerned entities were renamed, as the relationships and the relationship is a many-to-many, optional on both sides. This is occurring even if I reset the CloudKit development container. I don’t really have a clear idea of when this is appearing (seems random, after I updated some data or after I update the Core Data model). Any idea why the app is crashing? I would like as much as possible to keep the new naming for my entities and relationships.
SKPRCrewMemberMO renamed to Sailor
SKPRTrackMO renamed to Activity
crewMembers <<--->> tracks renamed sailors <<--->> activities
Here are some screenshots of the previous and updated data model for the entity at the origin of the migration issue, as well as some code regarding my Core Data stack initialization and the console error il getting.
PS: the app is used by few hundreds of people. That’s not a lot, but still, some of them have dozens of recorded activities and I don’t want to break anything and lose or corrupt data. I could launch a new app but users would lose their progress as it’s only saved locally in a shared container (app group was used as I wanted to share the Core Data with an Apple Watch extension). And I would lose the user base and App Store related things.
private init() {
container = NSPersistentCloudKitContainer(name: "Skipper")
guard let description = container.persistentStoreDescriptions.first else {
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
let id = "iCloud.com.alepennec.sandbox20201013"
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
description.cloudKitContainerOptions = options
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
do {
try container.initializeCloudKitSchema()
} catch {
print("Unable to initialize CloudKit schema: \(error.localizedDescription)")
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}

Liferay custom services on iOS - ld: symbol(s) not found for architecture x86_64

I am trying to consume custom web service from a liferay portlet but I am getting the not found architecture x86_64
I am using xcode 7.1.1 7B1005 running on El Capitan. Also using cocoapods and was able to call out of the box web service without any issues. It all started when I dragged the codes generated by liferay as part of my codes. There are no other compilation errors.
LRSession *session = [[LRSession alloc] initWithServer:#"http://localhost:8080"
authentication:[[LRBasicAuthentication alloc]
initWithUsername:#"richard.g.reyes#accenture.com"
password:#"xxx"]];
[session onSuccess:^(id result) {
NSLog(#"%#", result);
} onFailure:^(NSError *e) {
NSLog(#"Error: %#", e);
}];
// NSError *error;
LRGuestbookService_v62 *guestService = [[LRGuestbookService_v62 alloc] initWithSession:session];
To fix this:
What I am initially doing is drag the whole folder that the liferay generated into my project. For example, for Service/v_62/EntryService/EntryService.h, I dragged the whole service directory. Instead of that, I dragged the individual files and the error is resolved.
Hope this helps someone else.

Using same iCloud enabled CoreData store across Watch Extension and iPhone

I'm witnessing some strange behaviour when opening iCloud Enabled CoreData store from Apple Watch Extension.
I'm using the same iCloud Container across all targets.
Here is a picture that shows what folder (ubiquity container) structure looks like inside the ubiquity container :
It looks like it creates different stores for iPhone & Watch
I'm sharing the same CoreData Stack between iPhone app & Watch Extension. Any ideas why this is happening ?
If I understand this correctly it treats iPhone app & Watch Extension as a separate users ?
I would really appreciate if someone could give an advice.
You should use app groups to share the same Core Data store between Watch and iPhone. Enable app groups for both targets, configure it in your provisioning profiles and then get your persistent store URL like this:
NSURL *storeURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:appGroupIdentifier];
The watch would be accessing the Core Data store via a WatchKit extension also enabled for app groups. See e.g. Figure 4.1 in Apple's App Extension Programming Guide.
Consider having your WatchKit Extension use openParentApplication to communicate with the parent app. Using openParentApplication is simple to implement and helps keep the code in the WatchKit extension simple and fast.
From the WatchKit Extension InterfaceController, call openParentApplication.
NSDictionary *requst = #{#"request":#"myRequest"};
[InterfaceController openParentApplication:requst reply:^(NSDictionary *replyInfo, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
// DO STUFF
}
}];
Then, reply from the app using
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply{
Consider also using JSON data (NSJSONSerialization) in the main app to respond to the watch extension.

iCloud and existing Core Data store for "shoe box" app

I have an existing app with a single Core Data store. It is similar to what Apple calls a shoe box app.
There are many records already stored. I was able to add the necessary code easily to get iCloud integration setup.
I build the new app on my iPod touch and on my iPad, both running iOS5.
The iPod has the existing data. However the data doesn't show up on the iPad.
The app has iTunes file sharing enabled, so through iTunes I drop the sqlite db on both devices.
Now I try making changes and see if iCloud will now keep them in synch. It does not.
How do you get your existing data on both devices and then have iCloud keep them in synch from that point forward.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:#"ABCD1234.com.yourcompany.appID"];
NSDictionary *options = nil;
if (nil != cloudURL) {
NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:#"data"];
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
#"myApp.store", NSPersistentStoreUbiquitousContentNameKey,
cloudURL, NSPersistentStoreUbiquitousContentURLKey,
nil];
}
else {
options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
}
And I have this added to the managedObjectContext for notifications:
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
There are several thousand people using my app and have entered lots of data. They do not want to enter it all again. I want to deliver this new version of the app that is iCloud enabled but until I can get the data to show up on both and stay in synch there is no point.
A good nights sleep always helps.
Note, I have a backup of my SQLite DB already taken from my main device through iTunes file sharing.
I have iTunes files sharing enabled.I build the app on each device and run in XCode. Stop each device. Go to iTunes. Through file sharing I delete the sqlite database on each device. Sync the devices. Then I choose one device and restore my backed up database onto it using iTunes file sharing. Sync the device.
Launch my app on the device with the restored db. Then I launch my app on the second device that has no db (remember, deleted it above). Waited for a moment, and then behold, the data did appear. I think I heard the faint sound of an angelic choir singing.

how to sync core data icloud with ios and macOs app

I'm developping an iPad application that uses CoreData with iCloud. It works great!
I'm able to open ~/Library/Mobile Documents" with the folder that matches my Team ID and iCloud container.
I'm making also a macOS app that needs to access the iCloud. In macOS app, I added the iOS app container ID. So i have two container ID: ca.company.MacContainerID and ca.company.IPadContainerID which works for iPad app.
When i execute the following code in MacOS app, URLForUbiquityContainerIdentifier: returns nil.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *containerID = #"A1B2C3E4F5.ca.company.IPadContainerID";
NSURL *url = [fileManager URLForUbiquityContainerIdentifier:containerID];
url is nil
I don't know what to do to access to iCloud with core data inside from iPad app.
Do you have an idea ?
It must be a problem with your containerID. Did you double-check the ID string?
I found the solution! I was building the Mac application with an iOS provisionning file. So i put a Mac provisioning and it works; URLForUbiquityContainerIdentifier return me a non nul value.

Resources