Reputation: 5936
Im adding to iCloud to an existing app. Syncing works fine however I need to exclude some entities, or come up with a work around as some of my core data is being duplicated.
eg:
CertificateColour
is sent to a table view and each row is shown twice now.
CertificateType
presents four options in a action sheet, now 8 rows are present, with each row having been duplicated once.
Im using https://github.com/mluisbrown/iCloudCoreDataStack for my core data sync.
I checked out Core data + iCloud: exclude certain attributes from sync? That suggested a couple things:
1. Creating a separate local and cloud store, sounds...promising but not sure how as this is my first attempt with iCloud and Core data.
2. The second suggestion was sync to an entity that includes a unique device identifier but this is deprecated and again unsure of the process with core data
AppDelegate.h
#import <UIKit/UIKit.h>
#import <sqlite3.h>
#import "PersistentStack.h"
@interface ICAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
@end
Appdelegate.m
@interface ICAppDelegate () <DBSessionDelegate, DBNetworkRequestDelegate>
//iCloud
@property (nonatomic, strong) PersistentStack* persistentStack;
@property (nonatomic, strong) NSManagedObjectContext* managedObjectContext;
@end
@implementation ICAppDelegate
@synthesize window = _window;
@synthesize navigationController = _navigationController;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//iCloud
self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:self.modelURL];
self.managedObjectContext = self.persistentStack.managedObjectContext;
BOOL populateData = NO;
BOOL copyDb = YES;
// Copy DB to documents directory
if (copyDb == YES) {
NSString *srcPath = [[NSBundle mainBundle] pathForResource:@"myApp" ofType:@"sqlite"];
NSString *destPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"myApp.sqlite"];
if (![[NSFileManager defaultManager] fileExistsAtPath:srcPath]) {
DebugLog(@"Source file doesn't exist");
}
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
DebugLog(@"Copying DB to documents directory");
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:destPath error:&error];
if (error != nil) {
DebugLog(@"Copy failed %@", [error localizedDescription]);
}
}
}
/////*****ALL OF THIS IS DUPLICATED AND NEEDS EXCLUDING FROM ICLOUD BACK UP****////////////
if (populateData) {
DebugLog(@"Populating database");
NSManagedObjectContext *context = [self managedObjectContext];
Subscription *subscription = (Subscription *)[NSEntityDescription insertNewObjectForEntityForName:@"Subscription" inManagedObjectContext:context];
subscription.subscribed = @NO;
CertificateColour *red = (CertificateColour *)[NSEntityDescription insertNewObjectForEntityForName:@"CertificateColour" inManagedObjectContext:context];
red.name = @"Red";
red.redComponent = @1.0f;
red.greenComponent = @0.0f;
red.blueComponent = @0.0f;
CertificateColour *purple = (CertificateColour *)[NSEntityDescription insertNewObjectForEntityForName:@"CertificateColour" inManagedObjectContext:context];
purple.name = @"Purple";
purple.redComponent = @0.4f;
purple.greenComponent = @0.0f;
purple.blueComponent = @0.6f;
CertificateColour *green = (CertificateColour *)[NSEntityDescription insertNewObjectForEntityForName:@"CertificateColour" inManagedObjectContext:context];
green.name = @"Green";
green.redComponent = @0.0f;
green.greenComponent = @0.6f;
green.blueComponent = @0.2f;
CertificateColour *blue = (CertificateColour *)[NSEntityDescription insertNewObjectForEntityForName:@"CertificateColour" inManagedObjectContext:context];
blue.name = @"Blue";
blue.redComponent = @0.0f;
blue.greenComponent = @0.2f;
blue.blueComponent = @1.0f;
ICCertificateTypeManager *ctm = [ICCertificateTypeManager manager];
CertificateType *type = [ctm newCertificateType];
type.title = @"Works";
type.identifier = @(Works);
type = [ctm newCertificateType];
type.title = @"Type1";
type.identifier = @(Type1);
type = [ctm newCertificateType];
type.title = @"Type2";
type.identifier = @(Type2);
type = [ctm newCertificateType];
type.title = @"Type4";
type.identifier = @(Type3);
[self saveContext];
}
if ([[ICWebServiceClient sharedInstance] isLoggedIn])
{
DebugLog(@"User is logged in ");
}
////////////////////////////////////////////////////////////
return YES;
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
LogCmd();
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
DebugLog(@"handling url");
}
return YES;
}
return NO;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.managedObjectContext save:NULL];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[self saveContext];
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
#pragma mark - DBSessionDelegate
- (void)sessionDidReceiveAuthorizationFailure:(DBSession*)session userId:(NSString *)userId
{
LogCmd();
}
#pragma mark - DBNetworkRequestDelegate
static int outstandingRequests;
- (void)networkRequestStarted {
outstandingRequests++;
if (outstandingRequests == 1) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
}
- (void)networkRequestStopped {
outstandingRequests--;
if (outstandingRequests == 0) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
#pragma mark - Core Data stack
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel != nil) {
return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"myApp" withExtension:@"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"myApp.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
#pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#pragma mark - iCloud store
- (NSURL*)storeURL
{
NSURL* documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
return [documentsDirectory URLByAppendingPathComponent:@"myApp.sqlite"];
}
- (NSURL*)modelURL
{
return [[NSBundle mainBundle] URLForResource:@"myApp" withExtension:@"momd"];
}
@end
Upvotes: 3
Views: 1011
Reputation: 70976
Using multiple persistent stores is really the only option for excluding specific entity types from iCloud. You do that by calling addPersistentStoreWithType
more than once on the same persistent store coordinator but with different persistent store files (it's called a coordinator because it can coordinate between multiple persistent stores). Use iCloud options for one of the stores but not for the other.
You can either use two separate managed object models with different entities, or you can use a single model with different configuration options. Either is effective.
The problem you'll probably have with this is that you can't create relationships between instances in different persistent store files. (Technically you can create them, you just can't save them, which in most cases makes them useless). Your CertificateType
entity sounds like it would have relationships to other instances, but that's not going to work with multiple stores.
What you can do instead is sync every object but add code to detect the duplicates and deal with them. There was a good example of this in Apple's "SharedCoreData" sample app from the "Using iCloud with Core Data" session at WWDC 2012, but I can't find a copy of that online right now. You'd do something like
NSPersistentStoreDidImportUbiquitousContentChangesNotification
userInfo
to see if any of your CertificateType
objects are included.Update: I forgot that I had done a blog post which covers this in more detail. I also found a link to the WWDC 2012 sample code bundle (link requires current developer account) which includes SharedCoreData. A lot of what's in that demo is obsolete, but the duplicate removal code is as valid as ever.
Upvotes: 4