I'm using UIManagedDocument to reading and writing to CoreData. I have Document class. This is a document from some tutorial:
.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
typedef void (^OnDocumentReady) (UIManagedDocument *document);
#interface Document : NSObject
#property (strong, nonatomic) UIManagedDocument *document;
+ (Document *)sharedDocument;
- (void)performWithDocument:(OnDocumentReady)onDocumentReady;
#end
.m
#interface Document ()
- (void)objectsDidChange:(NSNotification *)notification;
- (void)contextDidSave:(NSNotification *)notification;
#end;
#implementation Document
#synthesize document = _document;
static Document*_sharedInstance;
+ (Document *)sharedDocument
{
static dispatch_once_t once;
dispatch_once(&once, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Document"];
self.document = [[UIManagedDocument alloc] initWithFileURL:url];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
self.document.persistentStoreOptions = options;
// Register for notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(objectsDidChange:)
name:NSManagedObjectContextObjectsDidChangeNotification
object:self.document.managedObjectContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:self.document.managedObjectContext];
}
return self;
}
- (void)performWithDocument:(OnDocumentReady)onDocumentReady
{
void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) {
onDocumentReady(self.document);
};
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) {
[self.document saveToURL:self.document.fileURL
forSaveOperation:UIDocumentSaveForCreating
completionHandler:OnDocumentDidLoad];
} else if (self.document.documentState == UIDocumentStateClosed) {
[self.document openWithCompletionHandler:OnDocumentDidLoad];
} else if (self.document.documentState == UIDocumentStateNormal) {
OnDocumentDidLoad(YES);
}
}
- (void)objectsDidChange:(NSNotification *)notification
{
#ifdef DEBUG
NSLog(#"NSManagedObjects did change.");
#endif
}
- (void)contextDidSave:(NSNotification *)notification
{
#ifdef DEBUG
NSLog(#"NSManagedContext did save.");
#endif
}
#end
Then in ViewController i have NSURLConnection:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
DataParser *parser = [[DataParser alloc] init];
[parser startParsingData:self.myMutableData withContext:self.moc];
}
and i open the document here:
-(void)initDocument {
if (!self.moc) {
[[Document sharedDocument] performWithDocument:^(UIManagedDocument *document) {
self.moc = document.managedObjectContext;
[[NSNotificationCenter defaultCenter] postNotificationName:UIDocumentStateChangedNotification object:self];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"someURL"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
[NSURLConnection connectionWithRequest:request delegate:self];
}];
}
}
Then i try to parse data:
-(void)startParsingData:(NSData*)data withContext:(NSManagedObjectContext*)context {
self.moc = context;
self.startDate = [NSDate date];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
ant try to load it in core data after XML parsing:
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Item"]) {
[self.moc performBlockAndWait:^{
TestEntity *te = [NSEntityDescription insertNewObjectForEntityForName:#"TestEntity" inManagedObjectContext:self.moc];
te.surname = #"5433fds";
te.name = #"5342fdsfsd";
}];
}
}
And i see the logs:
2014-01-24 16:21:21.692 Ce[85149:70b] NSManagedObjects did change.
2014-01-24 16:21:36.696 Ce[85149:70b] NSManagedContext did save.
So i assume that this should be in sqlite file, but when I try to read it is completely empty. awl file have some bigger size, but persistentStore have 0 rows. Why?
What happens when you launch the app a second time, can you see the data in your app?
In iOS 7, SQLite now has journaling turned on. I wonder if the journalling is causing a confusion (data is there but not in the sqlite file yet).
I would also question why you are using a UIManagedDocument. It appears you are using Core Data in a Singleton pattern (which is bad) and then using UIManagedDocument in that singleton pattern (worse).
UIManagedDocument is intended for document based apps. You are probably running foul of some of the "automated" features inside of that class that would be easily cleared up if you built a standard Core Data stack instead.
You might consider turning Journaling off, adding a vacuum option to your NSPersistentStoreCoordinator or switch to a standard Core Data stack.
This is likely because of a new SQLite journal mode used in iOS 7. If you set it to the old mode as shown in the answer to the following SO question you should see your data again.
How to disable WAL journal mode
Related
I'm trying to implement background fetch as well as refresh in iOS 10.
I'm using XML parsing to parse the data and then storing it in a file in the document's directory. For parsing XML I'm using a custom class (XMLParser) that confirms the NSXMLParserDelegate protocol.
The background fetch works fine. But I'm having problems in displaying the refreshed data, both when I click on the refresh button as well as in viewDidLoad.
I'm calling the refreshData method in viewDidLoad.
Here's how far I've gotten.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//--Set background fetch--//
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}
...
#pragma mark Background data fetch methods
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDate *fetchStart = [NSDate date];
ArtsViewController *artsViewController = (ArtsViewController *)self.window.rootViewController;
[artsViewController fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
NSLog(#"Background Fetch Duration: %f seconds", timeElapsed);
}];
}
ArtsViewController.h
#interface ArtsViewController : UIViewController <UIPageViewControllerDataSource>
#property BOOL newsAvailable;
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // No problems here
#end
ArtsViewcontroller.m
#interface ArtsViewController ()
#property (nonatomic, strong) NSArray *arrNewsData;
-(void)refreshData;
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray;
#end
...
#implementation ArtsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshData];
//--Load the file that saves news--//
[self loadNews];
if (_newsAvailable == YES)
{
[self setupPageViewController];
}
else
{
[self showNoNewsMessage];
}
}
...
#pragma mark Data Fetch methods
-(void)refreshData{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
[self performNewFetchedDataActionsWithDataArray:dataArray];
}
else{
NSLog(#"%#", [error localizedDescription]);
}
}];
}
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray{
// 1. Initialize the arrNewsData array with the parsed data array.
if (self.arrNewsData != nil) {
self.arrNewsData = nil;
}
self.arrNewsData = [[NSArray alloc] initWithArray:dataArray];
// 2. Write the file and reload the view.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docDirectory = [paths objectAtIndex:0];
NSString * newsFilePath = [NSString stringWithFormat:#"%#",[docDirectory stringByAppendingPathComponent:#"arts2"]]; // NewsFile
if (![self.arrNewsData writeToFile:newsFilePath atomically:YES]) {
_newsAvailable = NO;
NSLog(#"Couldn't save data.");
}
else
{
_newsAvailable = YES;
NSLog(#"Saved data.");
[self viewWillAppear:YES];
}
}
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
NSDictionary *latestDataDict = [dataArray objectAtIndex:0];
NSString *latestTitle = [latestDataDict objectForKey:#"title"];
NSDictionary *existingDataDict = [self.arrNewsData objectAtIndex:0];
NSString *existingTitle = [existingDataDict objectForKey:#"title"];
if ([latestTitle isEqualToString:existingTitle]) {
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"No new data found.");
}
else{
[self performNewFetchedDataActionsWithDataArray:dataArray];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"New data was fetched.");
}
}
else{
completionHandler(UIBackgroundFetchResultFailed);
NSLog(#"Failed to fetch new data.");
}
}];
}
...
#pragma mark IBActions
- (IBAction)reloadNews:(UIBarButtonItem *)sender
{
[self viewDidLoad];
}
I've debugged the application and found that after viewDidLoad
completes execution, the data file is written but the view isn't
updated. I've also tried calling the refreshData method in the main
thread, but there's no change.
after viewDidLoad is complete the showNoNewNews method is called.
I'm suspecting that my logic isn't wrong but implementation is. Threads at play here..
Any help would be appreciated.
Update:
Hope this helps those with similar problems...
I moved the logic of viewDidLoad to a different method, called the method for the first time in viewDidLoad and again in refreshData, after
[self performNewFetchedDataActionsWithDataArray:dataArray];
I need help to integrate iCloud to my CoreData.
I have tried many changes on AppDelegate, but only Error:
"libc++abi.dylib: terminating with uncaught exception of type NSException"
I have a TeamID and a Developer Account.
AppDelegate:
//
// AppDelegate.m
// ToolDB
//
// Created by Patrick Gottberg on 01.07.14.
//
//
#import "AppDelegate.h"
#import "PersonsTVC.h"
#import "RolePersonsTVC.h"
#import "MaterialPersonsTVC.h"
#import "RolesTVC.h"
#import "MaterialsTVC.h"
#import "PersonsTVC.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
#synthesize fetchedResultsController = __fetchedResultsController;
- (void)insertRoleWithRoleName:(NSString *)roleName
{
Role *role = [NSEntityDescription insertNewObjectForEntityForName:#"Role"
inManagedObjectContext:self.managedObjectContext];
role.name = roleName;
[self.managedObjectContext save:nil];
}
- (void)insertMaterialWithMaterialName:(NSString *)materialName
{
Material *material = [NSEntityDescription insertNewObjectForEntityForName:#"Material"
inManagedObjectContext:self.managedObjectContext];
material.name = materialName;
[self.managedObjectContext save:nil];
}
- (void)insertPersonWithPersonfirstname:(NSString *)personfirstname
{
Person *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:self.managedObjectContext];
person.firstname = personfirstname ;
person.surname= #"Example";
[self.managedObjectContext save:nil];
}
- (void)importCoreDataDefaultRoles {
NSLog(#"Importing Core Data Default Values for Roles...");
[self insertRoleWithRoleName:#"DMG Mori DMU 70 "];
[self insertRoleWithRoleName:#"Kunzmann BA 800"];
NSLog(#"Importing Core Data Default Values for Roles Completed!");
}
- (void)importCoreDataDefaultMaterials {
NSLog(#"Importing Core Data Default Values for Materials...");
[self insertMaterialWithMaterialName:#"S235"];
[self insertMaterialWithMaterialName:#"26Mo2"];
[self insertMaterialWithMaterialName:#"1.2316"];
[self insertMaterialWithMaterialName:#"C16"];
NSLog(#"Importing Core Data Default Values for Materials Completed!");
}
- (void)importCoreDataDefaultPersons {
NSLog(#"Importing Core Data Default Values for Persons...");
[self insertPersonWithPersonfirstname:#"Square"];
[self insertPersonWithPersonfirstname:#"T-Slot"];
[self insertPersonWithPersonfirstname:#"Ball"];
[self insertPersonWithPersonfirstname:#"Shell"];
NSLog(#"Importing Core Data Default Values for Persons Completed!");
}
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = #"Role"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:#"Person.name = Blah"];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self.fetchedResultsController performFetch:nil];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Set the application defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:#"YES"
forKey:#"myKeyName"];
[defaults registerDefaults:appDefaults];
[defaults synchronize];
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
[self importCoreDataDefaultMaterials];
[self importCoreDataDefaultPersons];
}
else {
NSLog(#"There's stuff in the database so skipping the import of default data");
}
// TAB BAR
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
NSLog(#"I'm an iPad");
// *** Set up the Persons Split Views (2-Way Delegation & Pass Managed Object Context) *** //
// Set up SPLIT VIEW for Persons
UISplitViewController *splitViewController = [[tabBarController viewControllers] objectAtIndex:0];
// Set up Split View MASTER view for Persons
UINavigationController *personsMasterTVCnav = [splitViewController.viewControllers objectAtIndex:0];
splitViewController.delegate = (id)personsMasterTVCnav.topViewController;
PersonsTVC *personsTVC = [[personsMasterTVCnav viewControllers] objectAtIndex:0];
personsTVC.managedObjectContext = self.managedObjectContext;
// Set up Split View DETAIL view for Persons
UINavigationController *personsDetailTVCnav = [splitViewController.viewControllers objectAtIndex:1];
PersonDetailTVC *personDetailTVC = [personsDetailTVCnav.viewControllers objectAtIndex:0];
// Set up MASTER and DETAIL delegation so we can send messages between views
personsTVC.delegate = personDetailTVC;
personDetailTVC.delegate = personsTVC;
// *** Set up the Roles Views *** (Pass Managed Object Context)//
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
RolesTVC *rolesTVC = [[rolesTVCnav viewControllers] objectAtIndex:0];
rolesTVC.managedObjectContext = self.managedObjectContext;
// *** Set up the Materials Views *** (Pass Managed Object Context)//
UINavigationController *materialsTVCnav = [[tabBarController viewControllers] objectAtIndex:2];
MaterialsTVC *materialsTVC = [[materialsTVCnav viewControllers] objectAtIndex:0];
materialsTVC.managedObjectContext = self.managedObjectContext;
// Set delegate for splitViewController
splitViewController.delegate = personDetailTVC;
}
else
{
NSLog(#"I'm an iPhone or iPod Touch");
// The Two Navigation Controllers attached to the Tab Bar (At Tab Bar Indexes 0 and 1)
UINavigationController *personsTVCnav = [[tabBarController viewControllers] objectAtIndex:0];
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
UINavigationController *materialsTVCnav = [[tabBarController viewControllers] objectAtIndex:2];
// The Persons Table View Controller (First Nav Controller Index 0)
PersonsTVC *personsTVC = [[personsTVCnav viewControllers] objectAtIndex:0];
personsTVC.managedObjectContext = self.managedObjectContext;
// The Roles Table View Controller (Second Nav Controller Index 0)
RolesTVC *rolesTVC = [[rolesTVCnav viewControllers] objectAtIndex:0];
rolesTVC.managedObjectContext = self.managedObjectContext;
// The Materials Table View Controller (Third Nav Controller Index 0)
MaterialsTVC *materialsTVC = [[materialsTVCnav viewControllers] objectAtIndex:0];
materialsTVC.managedObjectContext = self.managedObjectContext;
}
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
#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:#"Model" 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:#"StaffManager.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
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];
}
#end
i have a example that use the icloud but it is write for ios5, and i don't no what i have to change for ios7:
thats the Code for icloud Support in AppDelegate:
if((__persistentStoreCoordinator != nil)) {
return __persistentStoreCoordinator;
}
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;
// Set up iCloud in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ** Note: if you adapt this code for your own use, you MUST change this variable:
NSString *iCloudEnabledAppID = #"5KFJ75859U.Tim-Roadley.Staff-Manager";
// ** Note: if you adapt this code for your own use, you should change this variable:
NSString *dataFileName = #"StaffManager.sqlite";
// ** Note: For basic usage you shouldn't need to change anything else
NSString *iCloudDataDirectoryName = #"Data.nosync";
NSString *iCloudLogsDirectoryName = #"Logs";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];
if (iCloud) {
NSLog(#"iCloud is working");
NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];
NSLog(#"iCloudEnabledAppID = %#",iCloudEnabledAppID);
NSLog(#"dataFileName = %#", dataFileName);
NSLog(#"iCloudDataDirectoryName = %#", iCloudDataDirectoryName);
NSLog(#"iCloudLogsDirectoryName = %#", iCloudLogsDirectoryName);
NSLog(#"iCloud = %#", iCloud);
NSLog(#"iCloudLogsPath = %#", iCloudLogsPath);
if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if(fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *iCloudData = [[[iCloud path]
stringByAppendingPathComponent:iCloudDataDirectoryName]
stringByAppendingPathComponent:dataFileName];
NSLog(#"iCloudData = %#", iCloudData);
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];
[psc lock];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[NSURL fileURLWithPath:iCloudData]
options:options
error:nil];
[psc unlock];
}
else {
NSLog(#"iCloud is NOT working - using a local store");
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[psc lock];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:localStore
options:options
error:nil];
[psc unlock];
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomethingChanged" object:self userInfo:nil];
});
});
return __persistentStoreCoordinator;
i have also this code for ManagedObjectContext :
- (NSManagedObjectContext *)managedObjectContext {
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc performBlockAndWait:^{
[moc setPersistentStoreCoordinator: coordinator];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(mergeChangesFrom_iCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
}];
__managedObjectContext = moc;
}
return __managedObjectContext;
}
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
NSLog(#"Merging in changes from iCloud...");
NSManagedObjectContext* moc = [self managedObjectContext];
[moc performBlock:^{
[moc mergeChangesFromContextDidSaveNotification:notification];
NSNotification* refreshNotification = [NSNotification notificationWithName:#"SomethingChanged"
object:self
userInfo:[notification userInfo]];
[[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
}];
}
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
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
Wait on NSPersistentStoreDidImportUbiquitousContentChangesNotification
When it arrives, look at the notification's userInfo to see if any of your CertificateType objects are included.
If so, do a fetch for that entity to find and match up duplicates
Clean up those duplicates in whatever way makes sense for your app.
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.
while following the ebook of http://timroadley.com/ i am inserting. but when i check in sqlite their is no data present in it.also i have used -com.apple.CoreData.SQLDebug to debug but no query is being shown.Source Code
Solution:- Data will only show in sqlite when i will terminate the app or the app will go in background after pressing home button.
AppDelegate.h
#import <UIKit/UIKit.h>
#import "CoreDataHelper.h"
#import <CoreData/CoreData.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong, readonly) CoreDataHelper *coreDataHelper;
#end
AppDelegate.m
- (void)demo {
NSArray *newItemNames = [NSArray arrayWithObjects:
#"Apples", #"Milk", #"Bread", #"Cheese", #"Sausages", #"Butter", #"Orange Juice", #"Cereal", #"Coffee", #"Eggs", #"Tomatoes", #"Fish", nil];
for (NSString *newItemName in newItemNames) {
Item *newItem =
[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:_coreDataHelper.context];
newItem.name = newItemName;
NSLog(#"Inserted New Managed Object for '%#'", newItem.name);
}
}
- (CoreDataHelper*)cdh {
if (!_coreDataHelper) {
_coreDataHelper = [CoreDataHelper new];
[_coreDataHelper setupCoreData];
}
return _coreDataHelper;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[self cdh] saveContext];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self cdh];
[self demo];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[[self cdh] saveContext];
}
CoreDataHelper.h
#property(nonatomic,readonly) NSManagedObjectContext *context;
#property(nonatomic,readonly) NSManagedObjectModel *model;
#property(nonatomic,readonly) NSPersistentStore *store;
#property(nonatomic,readonly) NSPersistentStoreCoordinator *coordinator;
-(void)setupCoreData;
-(void)saveContext;
CoreDataHelper.m
#pragma mark - FILES
NSString *storeFilename = #"Grocery-Dude.sqlite";
#pragma mark - PATHS
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES) lastObject];
}
- (NSURL *)applicationStoresDirectory {
NSURL *storesDirectory =
[[NSURL fileURLWithPath:[self applicationDocumentsDirectory]]
URLByAppendingPathComponent:#"Stores"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:[storesDirectory path]]) {
NSError *error = nil;
if ([fileManager createDirectoryAtURL:storesDirectory
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
}
else {
NSLog(#"FAILED to create Stores directory: %#", error);}
}
return storesDirectory;
}
- (NSURL *)storeURL {
return [[self applicationStoresDirectory]
URLByAppendingPathComponent:storeFilename];
}
#pragma mark - SETUP
- (id)init {
self = [super init];
if (!self) {return nil;}
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
_coordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:_model];
_context = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setPersistentStoreCoordinator:_coordinator];
return self;
}
- (void)loadStore {
if (_store) {return;} // Don’t load store if it's already loaded
NSDictionary *options =
#{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}};
NSError *error = nil;
_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self storeURL]
options:options error:&error];
if (!_store) {NSLog(#"Failed to add store. Error: %#", error);abort();}
else {NSLog(#"Successfully added store: %#", _store);}
}
- (void)setupCoreData {
[self loadStore];
}
#pragma mark - SAVING
- (void)saveContext {
if ([_context hasChanges]) {
NSError *error = nil;
if ([_context save:&error]) {
NSLog(#"_context SAVED changes to persistent store");
} else {
NSLog(#"Failed to save _context: %#", error);
}
} else {
NSLog(#"SKIPPED _context save, there are no changes!");
}
}
Try saving item in the loop itself like
NSArray *newItemNames = [NSArray arrayWithObjects:
#"Apples", #"Milk", #"Bread", #"Cheese", #"Sausages", #"Butter", #"Orange Juice", #"Cereal", #"Coffee", #"Eggs", #"Tomatoes", #"Fish", nil];
for (NSString *newItemName in newItemNames) {
Item *newItem =
[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:_coreDataHelper.context];
newItem.name = newItemName;
NSLog(#"Inserted New Managed Object for '%#'", newItem.name);
[[self cdh] saveContext];
}
I have an issue where I can't seem to dealloc UIDocument (used in iCloud)
After running an NSMetaDataQuery to look for the document as follows..
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate predicateWithFormat:
#"%K == %#", NSMetadataItemFSNameKey, kFILENAME];
[query setPredicate:pred];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(queryDidFinishGathering:)
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
[query startQuery];
I process my query
- (void)queryDidFinishGathering:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
_query = nil;
[self loadData:query];
}
Then load or create a new document.
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
MyDocument *doc = [[[MyDocument alloc] initWithFileURL:url] autorelease];
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(#"iCloud document opened %#", doc);
[doc updateChangeCount:UIDocumentChangeDone];
} else {
NSLog(#"failed opening document from iCloud");
}
}];
} else {
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:
#"Documents"] URLByAppendingPathComponent:kFILENAME];
MyDocument *doc = [[[MyDocument alloc] initWithFileURL:ubiquitousPackage] autorelease];
[doc saveToURL:[doc fileURL]
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
[doc openWithCompletionHandler:^(BOOL success) {
NSLog(#"new document opened from iCloud");
[doc updateChangeCount:UIDocumentChangeDone];
}];
}
}];
}
}
The NSLog(#"iCloud document opened %#", doc); shows a different memory address for each UIDocument.
I have an NSLog in my UIDocument subclass, it never gets called. I cannot see where it is being retained that I am not releasing it. This query is ran whenever I want to sync my cloud data, this happens fairly regularly. The data syncs correctly.
I am experiencing strange crashes where my app will simply close to the dashboard, with nothing in the debug (from previous experiences I know this often to be the app expending too much memory and being terminated.)
I think that my UIDocument is leaking, would I be correct in this assumption, this is the first time i've wrestled with iCloud so I'm still in the dark over a few things.
My subclass has the following properties:
#property (copy, nonatomic) NSData *infoData;
#property (copy, nonatomic) NSMutableArray *firstArray;
#property (copy, nonatomic) NSMutableArray *secondArray;
#property (copy, nonatomic) NSMutableArray *thirdArray;
I am not using ARC.
I did not realise that I had to do this:
[doc updateChangeCount:UIDocumentChangeDone];
[doc closeWithCompletionHandler:nil];
Obviously if a file is open for writing, then it would not be wise to allow it to be deallocated!
Doh! Hopefully this saves someone some time in the future.