Could not create UIManagedDocument - ios

I'm trying to make an iPhone app using Core Data. I have to use NSManagedObjectContext to access data and in order to do this i use UIManagedDocument. But if I try to create a document with UIManagedDocument, document's openWithCompletionHandler isn't success. This why my NSManagedObjectContext is always nil and Xcode says could not create document at etc. Here are the classes:
AddUserViewController.h
#import <UIKit/UIKit.h>
#interface AddUserViewController : UIViewController
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic,strong) UIManagedDocument *document;
#end
AddUserViewController.m
#import "AddUserViewController.h"
#import "User.h"
#import "User+Create.h"
#interface AddUserViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#property (weak, nonatomic) IBOutlet UITextField *ageField;
#property (weak, nonatomic) IBOutlet UITextField *sexField;
#property (weak, nonatomic) IBOutlet UITextField *weightField;
#property (weak, nonatomic) IBOutlet UITextField *activityField;
#end
#implementation AddUserViewController
-(void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext{
_managedObjectContext = managedObjectContext;
}
-(void)createOrWriteDocument{
NSURL *url = [[[NSFileManager defaultManager]URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask]firstObject];
url = [url URLByAppendingPathComponent:#"Activities"]; // edited mistakenly deleted
self.document = [[UIManagedDocument alloc] initWithFileURL:url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[self.document openWithCompletionHandler:^(BOOL success) {
if (success) [self documentIsReady];
if (!success){
NSLog(#"could not open document at %#",url);
}
}];
} else {
[self.document saveToURL:url
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
[self documentIsReady];
}
if (!success){
NSLog(#"could not create document at %#",url);
}
}];
}
}
- (void)documentIsReady
{
if (self.document.documentState == UIDocumentStateNormal) {
self.managedObjectContext = self.document.managedObjectContext;
} }
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if (!self.managedObjectContext) {
[self createOrWriteDocument];
}
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
}
-(void)dismissKeyboard{
[self.nameField resignFirstResponder];
[self.ageField resignFirstResponder];
[self.sexField resignFirstResponder];
[self.weightField resignFirstResponder];
[self.activityField resignFirstResponder];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"setUser:"]) {
//User *user = [[User alloc]init];
User *user = [User createUserWithname:self.nameField.text
withAge:[NSNumber numberWithDouble:[self.ageField.text doubleValue]]
withSex:self.sexField.text
withWeight:[NSNumber numberWithDouble:[self.weightField.text doubleValue]]
withActivity:self.activityField.text
inManagedObjectContext:self.managedObjectContext];
if ([segue.destinationViewController respondsToSelector:#selector(setUser:)]) {
[segue.destinationViewController performSelector:#selector(setUser:) withObject:user];
}
}
}
#end
EDIT
I solved the problem.Problem is at the NSURL *url = [[[NSFileManager defaultManager]URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask]firstObject];
line. Instead of NSDocumentationDirectory, I used NSDocumentDirectory and it solved the problem.

You can try this code to create you managedobjectcontext.
- (NSManagedObjectContext *) managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return _managedObjectContext;
}

Take a look at the example below. This works for me, sorry don't have time to take a closer look to see the differences, I am using iCloud options so remove them but try setting the other persistentStoreOptions. And also post details of the error are you getting?
// This gets called when the user has done one of the following:
// 1. Created a new file and entered a new file name. We have then created the fileURL
// using the /Documents directory, the filename and appending '_UUID_'+uuid to ensure that
// avoid duplicate file names in case the user used the same file name on another device.
// 2. Selected an existing file from the file browser
//
- (void)createNewFile:(NSURL*)fileURL {
//FLOG(#"createNewFile called with url %#", fileURL);
_creatingNewFile = YES; // Ignore any file metadata scan events coming in while we do this because some iCloud
// files get created by Core Data before the local files are created and our scanning
// picks up new iCloud files and attempts to create local copies and we don't want this
// if this devices is busy creating the new iCloud file
_document = [[OSManagedDocument alloc] initWithFileURL:fileURL];
// Set oberving on this file to monitor the state (we don't use it for anything other than debugging)
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:#selector(documentStateChanged:)
name:UIDocumentStateChangedNotification
object:_document];
_openedVersion = [NSFileVersion currentVersionOfItemAtURL:fileURL];
_openedVersionDate = _openedVersion.modificationDate;
_openedVersionDevice = _openedVersion.localizedNameOfSavingComputer;
//FLOG(#" file version date: %#", _openedVersionDate);
//FLOG(#" file version device: %#", _openedVersionDevice);
NSString *fileName = [[fileURL URLByDeletingPathExtension] lastPathComponent];
[_document setPersistentStoreOptions:#{NSPersistentStoreUbiquitousContentNameKey:fileName,
NSMigratePersistentStoresAutomaticallyOption:#YES,
NSInferMappingModelAutomaticallyOption:#YES,
NSSQLitePragmasOption:#{ #"journal_mode" : #"DELETE" }}];
_managedObjectContext = _document.managedObjectContext;
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
//FLOG(#" file exists so open it: %#",fileURL);
[_document openWithCompletionHandler:^(BOOL success){
if (!success) {
// Handle the error.
LOG(#" error creating file");
} else {
//LOG(#" file opened");
[self fileOpened]; // Now initialise the UI and let the user continue...
}
}];
}
else {
// File does not exist so that means the user has created a new one and we need to
// load some initialisation data into the Core Data store (codes tables, etc.)
//
// At this stage we have a database in memory so we can just use the _document.managedObjectContext
// to add objects prior to attempting to write to disk.
//LOG(#" file DOES NOT exist so add initial data");
[self addInitialData];
// Just checking if anything has been written to disk, nothing should not exist on disk yet.
// Debugging use only
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
LOG(#" file exists but keep going anyway :-(");
else
LOG(#" file still does not exist :-)");
// OK now save a copy to disk using UIManagedDocument
// NOTE: the iCloud files are written before the UIManagedDocument.fileURL, presumably because Core Data does this setup
// in response to the [moc save:]. Make sure we don't pick this up in our iCloud metaData scan and attempt to create
// it as if it were a new iCloud file created by some other device.
//
[_document saveToURL:_document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success){
if (!success) {
// Handle the error.
LOG(#" error saving file :-(");
}
// We close the file and wait for it to appear in the file browser and then
// let the user select it from the browser to open it and start using it.
// Skip this if you want to open it directly and [self fileopened] but
// bear in mind the fileListController will not be correctly set up when we return to it.
[_document closeWithCompletionHandler:^(BOOL success){
if (!success) {
// Handle the error.
LOG(#" error closing file after creation :-(");
FLOG(#" file URL is %#", [fileURL path]);
} else {
FLOG(#" file closed %#", fileName);
_creatingNewFile = NO; // OK we are done, so let metaData scanning go ahead as normal
// Tell our UITableView file list that we are done and trigger scanning of local and iCloud files
// The fileListController will the add the new file itself and the user will then pick the
// file from this list in order to open it.
// To open the file automatically use the callback in the fileListController
// to select and then open the file so it looks seamless to the user.
[self.fileListController fileHasBeenCreated:fileURL];
// Stop observing now
[center removeObserver:self
name:UIDocumentStateChangedNotification
object:_document];
}
}];
}];
}
}
Oh and also create a subclass of UIManagedDocument so you can get the errors, if you don't already have one. All you need is the following in the subclass.
#implementation OSManagedDocument
- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
LOG(#"Auto-Saving Document");
return [super contentsForType:typeName error:outError];
}
- (void)handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted
{
FLOG(#" error: %#", error.localizedDescription);
NSArray* errors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(errors != nil && errors.count > 0) {
for (NSError *error in errors) {
FLOG(#" Error: %#", error.userInfo);
}
} else {
FLOG(#" error.userInfo = %#", error.userInfo);
}
}
#end
This is the method to get the /Documents directory on the device. The file name is appended to this path.
- (NSURL*)documentsDirectoryURL
{
_dataDirectoryURL = [NSURL fileURLWithPath:NSHomeDirectory() isDirectory:YES];
return [_dataDirectoryURL URLByAppendingPathComponent:#"Documents"];
}
Here is a link to more information http://ossh.com.au/design-and-technology/software-development/uimanageddocument-icloud-integration/

Related

Error and Crash when using the Core Data to create a UITableView

I try to use Core Data to make a UITableView, but I come across a crash when I run it:
014-07-29 10:13:12.443 TableAndCoreData[797:60b] -[AppDelegate managedObjectContext]: unrecognized selector sent to instance 0x8f319a0
2014-07-29 10:13:12.446 TableAndCoreData[797:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AppDelegate managedObjectContext]: unrecognized selector sent to instance 0x8f319a0'
I generally check it and I guess it might be the problem of the creating the managedObjectContext. Does any one have some idea to help me to fix this problem?
#interface DetailViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *ageTextField;
#end
#implementation DetailViewController
// Set NSManagedObjectContext
- (NSManagedObjectContext *) managedOjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication]delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedOjectContext];
}
return context;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)cancelButton:(UIButton *)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)saveButton:(UIButton *)sender
{
[self save];
}
- (void)save
{
// Get ManagedObjectContext
NSManagedObjectContext *context = [self managedOjectContext];
// Create a ManagedObject
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attributes of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check the error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Besides, I am trying another way to create UITableView using Core Data:
- (void) save
{
// Create UIManagedDocument
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask]firstObject];
NSString *documentName = #"Model";
NSURL *url = [documentDirectory URLByAppendingPathComponent:documentName];
UIManagedDocument *document = [[UIManagedDocument alloc]initWithFileURL:url];
if ([fileManager fileExistsAtPath:[url path]]) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
else {
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
}
However, it just couldn't find the UIManagedDocument. I really wonder the reason why I should create a UIManagedDocument and the document name I should name it.
Your crash does not have anything to do with Core Data.
Change this:
[delegate performSelector:#selector(managedObjectContext)]
to:
[delegate respondsToSelector:#selector(managedObjectContext)]
The crash was happening because you were sending the message managedObjectContext to the application delegate object, which does not respond to that message. It was being sent because where you meant to check to see if it responds to that message with respondsToSelector:, you had performSelector instead. Your application delegate object still needs to implement managedObjectContext for your code to be functional, but the portions you have posted should no longer crash as you describe.
In general you want to avoid calling a method on the application delegate this way. It's preferred to pass a value like this from the application delegate into the root view controller at startup, and it's passed along to the next view controller and the next.
There is a slightly outdated, but still relevant section of the Core Data documentation that discusses this:
A view controller typically shouldn’t retrieve the context from a global object such as the application delegate—this makes the application architecture rigid. Neither should a view controller create a context for its own use (unless it’s a nested context).
...as well as the iOS 5 release notes:
Nested contexts make it more important than ever that you adopt the “pass the baton” approach of accessing a context (by passing a context from one view controller to the next) rather than retrieving it directly from the application delegate.

Syncing core data with iCloud - excluding entities

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.

core data: background context ios

I already posted this problem and no one tell me what is the problem, please help.
I have problem when I use Core Data to save data in my iOS app.
My application can receive many and many text message in a second from a server.
So i need to save those message in my data base, now i use NSOperations inside queue to save messages (so you can imagine the number of the NSOperation in the NSOperationQueue).
The problem is if the i have a normal flow of messages then it works fine and if have an important flow of messages , the app freeze indefinitely or the NSOperations increase without limit in the queue.
PS: if remove the observer of merge the messages are saved without any problem
This my code of the NSOperation:
#interface SaveRecievedMessageOperation()
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong) AppDelegate *appdelegate;
#property (nonatomic, assign) BOOL executing;
#property (nonatomic, assign) BOOL finished;
#end
#implementation SaveRecievedMessageOperation
#synthesize message;
#synthesize managedObjectContext;
#synthesize appdelegate;
#synthesize executing;
#synthesize finished;
- (id)initWithMessage:(SipMessage *)messageToSave
{
if (self = [super init]) {
self.message = messageToSave;
}
return self;
}
- (void)main
{
#autoreleasepool
{
self.appdelegate = [[UIApplication sharedApplication] delegate];
[self managedObjectContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:self.managedObjectContext];
[self saveMessage];
}
}
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil)
{
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self.appdelegate persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
[managedObjectContext setMergePolicy:NSOverwriteMergePolicy];
}
return managedObjectContext;
}
- (void)saveMessage
{
NSDictionary *header = self.message.headers;
NSArray *bodies = self.message.bodies;
SipBody *sipBody;
NSDictionary* body;
NSData *ContentData;
if ([[header valueForKey:#"Content-Type"] rangeOfString:#"application/json"].location != NSNotFound)
{
sipBody = [bodies objectAtIndex:0];
body = [NSJSONSerialization
JSONObjectWithData:sipBody.content
options:NSJSONReadingAllowFragments
error:nil];
}
else if ([[header valueForKey:#"Content-Type"] rangeOfString:#"multipart/mixed"].location != NSNotFound)
{
for (SipBody *sipB in bodies) {
if ([[sipB.headers valueForKey:#"Content-Type"] rangeOfString:#"application/json"].location != NSNotFound)
{
body = [NSJSONSerialization
JSONObjectWithData:sipB.content
options:NSJSONReadingAllowFragments
error:nil];
}
else
{
ContentData = [NSData dataWithData:sipB.content];
}
}
}
else
{
return;
}
MGMPhone *sender;
NSArray *senders = [self updatePhonesFromMSISDNsList:[[header valueForKey:#"swfrom"] componentsSeparatedByString:MGMseparator]];
sender = senders[0];
NSError *error;
MGMMessage *aMesage = [MGMMessage createInContext:self.managedObjectContext];
[aMesage setBoxType:[NSNumber numberWithInteger:BoxTypeIncomingMessage]];
[aMesage setContent:[body valueForKey:#"Content"]];
[aMesage setContentType:[header valueForKey:#"Content-Type"]];
[aMesage setGroupMessage:( [[header valueForKey:#"groupmessage"] isEqualToString:#"true"]
?
[self saveContext];
}
#pragma mark core data
- (void)mergeChanges:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext *mainContext = [appdelegate managedObjectContext];
[mainContext performSelector:#selector(mergeChangesFromContextDidSaveNotification:) withObject:notification];
[self setManagedObjectContext:nil];
}
- (void)saveContext
{
NSError *error = nil;
if (self.managedObjectContext != nil)
{
if ([self.managedObjectContext hasChanges]) {
BOOL isSaved = [self.managedObjectContext save:&error];
if(!isSaved){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
else if (![self.managedObjectContext hasChanges]){
[self setManagedObjectContext:nil];
}
}
}
#end
I think everything is fine, except the merge operation. I'm using very similar CoreData operations in background myself.
Typically you want to merge updates from background operations with the 'main' managed object context. So basically in the background operation you should only perform the save: method. (So don't register for save notifications nor initiate merge actions).
Unless you really need to merge managed object contexts in background threads, the save notification should be registered only once in the main thread (typically in a singleton e.g. appDelegate or your own singleton). The save notification will be sent by CoreData after any save operation in other contexts, allowing you to merge these changes with the main context.

iOS. I can´t play an mp3 calling a NSObject class, However it works directly from the UIViewController

I can play an mp3 file with AVAudioPlayer framework directly in the ViewController as an action from a button, but if I want to play the same mp3 calling a selector in a NSObject Class, failed. Thanks for your advice.
Playing an mp3 from the ViewController (audioPlayerDemo.xcodeproj) and it works:
AVFoundation.framework included in the frameworks folder
APDViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface APDViewController : UIViewController <AVAudioPlayerDelegate>
{
}
#property (strong,nonatomic) AVAudioPlayer *audioPlay;
- (IBAction)play:(id)sender;
APDViewController.m
#implementation APDViewController
#synthesize audioPlay;
- (IBAction)play:(id)sender
{
NSError *error = nil;
NSData *dataAudio = [[NSData alloc] initWithContentsOfFile:
#"/Users/User/Documents/AudioPlayerDemo/AudioPlayerDemo/hello world.mp3"options:0 error:&error];
if(error){
NSLog(#" error loading data: %# ", [error localizedDescription]);
}
else{
audioPlay = [[AVAudioPlayer alloc] initWithData:dataAudio error:&error];
if(error){
NSLog(#" error loading audio: %# ", [error localizedDescription]);
}
else{
audioPlay.delegate = self;
[audioPlay prepareToPlay];
[audioPlay play];
}
}
}
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (player == audioPlay){
audioPlay = nil;
NSLog(#"audioplay = nil");
}
}
But, if I want to play the same .mp3 calling a NSObject class (AudioPlayClass.xcodeproj), there is no error, neither any sound. Here the code that doesn`t work:
AVFoundation.framework included in the frameworks folder
APCplayObject.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#interface APCplayObject : NSObject <AVAudioPlayerDelegate>
{
}
#property (strong,nonatomic) AVAudioPlayer *audioPlay;
- (void)playAudio;
#end
APCplayObject.m
#import "APCplayObject.h"
#implementation APCplayObject
#synthesize audioPlay;
-(void)playAudio{
NSError *error = nil;
NSData *dataAudio = [[NSData alloc] initWithContentsOfFile:
#"/Users/User/Documents/AudioPlayerClass/AudioPlayerClass/hello world.mp3"options:0 error:&error];
if(error){
NSLog(#" error loading data: %# ", [error localizedDescription]);
}
else{
audioPlay = [[AVAudioPlayer alloc] initWithData:dataAudio error:&error];
if(error){
NSLog(#" error loading audio: %# ", [error localizedDescription]);
}
else{
audioPlay.delegate = self;
[audioPlay prepareToPlay];
[audioPlay play];
}
}
}
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (player == audioPlay){
audioPlay = nil;
NSLog(#"audioplay = nil");
}
}
APCviewController.m, the caller as an action from a button:
- (IBAction)callPlay:(id)sender {
APCplayObject *a = [[APCplayObject alloc] init];
[a playAudio];
}
Thanks in advance.
The problem is that after you start the audio playing, the variable a goes out of scope and is therefore destroyed.
You need to create a declared property to hold your APCplayObject when the method exits.
So, in APCviewController.h you can do something like this:
#property (strong,nonatomic) APCplayObject *playObject;
And then change callPlay to:
- (IBAction)callPlay:(id)sender {
if (!self.playObject) // Use this line if you only want to create one playObject, take it out to create a new one every time.
self.playObject = [[APCplayObject alloc] init];
[self.playObject playAudio];
}

Adding Core Data to existing iPhone project

I'd like to add core data to an existing iPhone project, but I still get a lot of compile errors:
- NSManagedObjectContext undeclared
- Expected specifier-qualifier-list before 'NSManagedObjectModel'
- ...
I already added the Core Data Framework to the target (right click on my project under "Targets", "Add" - "Existing Frameworks", "CoreData.framework").
My header-file:
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
[...]
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
What am I missing? Starting a new project is not an option...
Thanks a lot!
edit
sorry, I do have those implementations... but it seems like the Library is missing... the implementation methods are full with compile error like "managedObjectContext undeclared", "NSPersistentStoreCoordinator undeclared", but also with "Expected ')' before NSManagedObjectContext" (although it seems like the parenthesis are correct)...
#pragma mark -
#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 by merging all of the models found in
application bundle.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
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 = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"Core_Data.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil 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. If it is not possible to recover from the error, display an alert panel that
instructs the user to quit the application by pressing the Home button.
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.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
All the CoreData header files are imported in App_Prefix.pch, so the CoreData classes will be available throughout your Project, so you don't have to manually import the header in the files you need them.
So open up Xcode and look for some file like App_Prefix.pch, by default it's in the Other Sources group. After the UIKit import statement, add the following line:
#import <CoreData/CoreData.h>
And you should be ready to go.
Xcode 4
For projects created in Xcode 4, the prefix file can be found in the Supporting Files group in the Project navigator. It's called 'projectname-Prefix.pch' by default.
Xcode 6+
Starting with Xcode 6, the precompiled header file is no longer included by default. This is because of the introduction of Modules, which take away the need to use precompiled headers. While it is still possible to manually add a PCH file to globally include the CoreData headers, consider specifying the CoreData dependency using #import CoreData;* in every file that uses CoreData. This makes dependencies explicit and more importantly will avoid this question's problem in the future.
* Modules need to be enabled for this to work.
Just to expound on all the steps you actually need to perform to add Core Data to a project that previously did not have it:
Step 1: Add the Framework
Click on your app target (on the left pane its the top icon with the name of your app) then go to the 'Build Phases' tab then on 'Link Binary With Libraries', click the little '+' at the bottom then find 'CoreData.framework' and add it to your project
Then either import coredata on all the objects you need it (the non-sexy way) using:
Swift
import CoreData
Objective C
#import <CoreData/CoreData.h>
or add the import below the common imports in your .pch file (much more sexy) like this:
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
Step 2: Add the Data Model
To add the .xcdatamodel file right click/control-click on your files in the right pane (like in a Resources folder for safe keeping) and select to Add a New File, Click the Core Data tab when selecting your file type then Click 'Data Model', give it a name and click Next and Finish and it will add it to your project. When you click on this Model object you will see the interface to add the Entities to your project with any relationships you want.
Step 3: Update App Delegate
In Swift on AppDelegate.swift
//replace the previous version of applicationWillTerminate with this
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
func saveContext () {
var error: NSError? = nil
let 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.
//println("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.
var managedObjectContext: NSManagedObjectContext {
if !_managedObjectContext {
let coordinator = self.persistentStoreCoordinator
if coordinator != nil {
_managedObjectContext = NSManagedObjectContext()
_managedObjectContext!.persistentStoreCoordinator = coordinator
}
}
return _managedObjectContext!
}
var _managedObjectContext: NSManagedObjectContext? = nil
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
var managedObjectModel: NSManagedObjectModel {
if !_managedObjectModel {
let modelURL = NSBundle.mainBundle().URLForResource("iOSSwiftOpenGLCamera", withExtension: "momd")
_managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)
}
return _managedObjectModel!
}
var _managedObjectModel: NSManagedObjectModel? = nil
// 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.
var persistentStoreCoordinator: NSPersistentStoreCoordinator {
if !_persistentStoreCoordinator {
let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("iOSSwiftOpenGLCamera.sqlite")
var error: NSError? = nil
_persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil {
/*
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:
[NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true}
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
return _persistentStoreCoordinator!
}
var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil
// #pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
var applicationDocumentsDirectory: NSURL {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.endIndex-1] as NSURL
}
In Objective C make sure to add these objects to AppDelegate.h
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory; // nice to have to reference files for core data
Synthesize the previous objects in AppDelegate.m like this:
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
Then add these methods to AppDelegate.m (make sure to put the name of the model that you added in the spots shown):
- (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();
}
}
}
- (NSManagedObjectContext *)managedObjectContext{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"NAMEOFYOURMODELHERE" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"NAMEOFYOURMODELHERE.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];
}
Step 4: Get the Data Objects to the ViewControllers Where You Need the Data
Option 1. Use the App Delegate's ManagedObjectContext from VC (Preferred and Easier)
As suggeted by #brass-kazoo - Retrieve a reference to AppDelegate and its managedObjectContext via:
Swift
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.managedObjectContext
Objective C
[[[UIApplication sharedApplication] delegate] managedObjectContext];
in your ViewController
Option 2. Create ManagedObjectContext in your VC and have it match AppDelegate's from the AppDelegate (Original)
Only showing old version for Objective C since much easier to use the preferred method
in the ViewController.h
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
In the ViewController.m
#synthesize managedObjectContext = _managedObjectContext;
In the AppDelegate, or class where the ViewController is created set the managedObjectContext to be the same as the AppDelegate one
ViewController.managedObjectContext = self.managedObjectContext;
If you want the viewcontroller using Core Data to be a FetchedResultsController then you'll need to make sure this stuff is in your ViewController.h
#interface ViewController : UIViewController <NSFetchedResultsControllerDelegate> {
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
And this is in ViewController.m
#synthesize fetchedResultsController, managedObjectContext;
After all of that you can now use this managedObjectContext to run all the usual fetchRequests needed for CoreData goodness! Enjoy
For Swift 3: INCLUDES SAVING AND RETRIEVING DATA
Step 1: Add Framework
Step 2: Add Data model
File > New > File > Core Data > Data Model
Name the file as SampleData the resultant file would be SampleData.xcdatamocelId
Step 3: Add the below functions to your App Delegate and add "import CoreData" to the top
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
// SEE BELOW LINE OF CODE WHERE THE 'name' IS SET AS THE FILE NAME (SampleData) FOR THE CONTAINER
let container = NSPersistentContainer(name: "SampleData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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 parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
STEP 4: Adding Entity and Attribute to the Model
a) Add Entity
b) Add Attribute
STEP 5: Saving Data
func saveItem(itemToSave: String){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//**Note:** Here we are providing the entityName **`Entity`** that we have added in the model
let entity = NSEntityDescription.entity(forEntityName: "Entity", in: context)
let myItem = NSManagedObject(entity: entity!, insertInto: context)
myItem.setValue(itemToSave, forKey: "item")
do {
try context.save()
}
catch{
print("There was an error in saving data")
}
}
STEP 5: Retrieving Data
override func viewWillAppear(_ animated: Bool) {
// Obtaining data from model
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
do {
let results = try context.fetch(fetchRequest)
let obtainedResults = results as! [NSManagedObject]
let firstResult = obtainedResults[0]
let myValue = firstResult.value(forKey: "item")
print("myValue: \(myValue)")
} catch {
print("Error")
}
}
Try creating Core Data backed Cocoa application and look at AppDelegate. You'll see core data stack implementation methods there as well as managed object model file for defining your entities and other core-data releated stuff.
You've shown us only header (i.e. declaration), but not implementation (i.e. definition) of the Core Data stack.
If you run into this same issue in xcode 4, as I did.
It is different: I had to select the project, then in targets expand "Link Binary With Libraries" which shows the current libraries.
From there click the + (plus sign) to select any additional libraries you need.
I placed it in the top of the project and had to move it (drag and drop) to the Frameworks Group, but that was it.
As Eimantas stated your missing the implementiation of the Core Stack, like
- (NSManagedObjectContext *) managedObjectContext;
- (NSManagedObjectModel *)managedObjectMode;
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
On solution would be to create a new core data driver project and copy / paste the implementation to your project.
For Swift 3:
File->new file->CoreData->Model to create a model.
Refer to this link for more information on how to implement it.
//in Swift 2.2 , you may do the following without changing the AppDelegate file.
Project->targets-->linked frameworks and libraries
Now add a new framework (click on +) 'CoreData'
File->new file->CoreData->DataModel
name it as say A.xcdatamodelid
In A.xcdatamodelid create new enitity (click on entity+)
name it as say Bc and set its class as 'Bc' in inspector window on right.
Now Add attributes to the entity (click on attributes +) , add one attribute for eg : name and its type as String.
Now editor->create NSManagedObject Subclass -->click next on the pop up window-->again next-->then click create.
Two new files will be created 1. a new class named Bc.swift and an extension named Bc+coredataproperties.swift.
File->new file->ios->cocoa Touch class-->set its subclass as NSObject->name it as DataController.swift
Inside the file include
///
import UIKit
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
override init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("A", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
self.managedObjectContext.persistentStoreCoordinator = psc
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "A.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("A.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
//////
Now inside the viewcontroller file you can access your db using two methods.
Important : include the statement in your viewController
"import CoreData"
a. call seed() -->to insert value into db/entity
b. call fetch()--> to fetch value from db/entity
///////seed()-->def
func seedPerson() {
// create an instance of our managedObjectContext
let moc = DataController().managedObjectContext
// we set up our entity by selecting the entity and context that we're targeting
let entity = NSEntityDescription.insertNewObjectForEntityForName("Bc", inManagedObjectContext: moc) as! Bc
// add our data
entity.setValue("Meera", forKey: "name")
// we save our entity
do {
try moc.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}
//fetch() def
func fetch() {
let moc = DataController().managedObjectContext
let personFetch = NSFetchRequest(entityName: "Bc")
do {
let fetchedPerson = try moc.executeFetchRequest(personFetch) as! [Bc]
print(fetchedPerson.first!.name!)
} catch {
fatalError("Failed to fetch person: \(error)")
}
}
view.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface ViewController :
UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *coreDataList;
- (IBAction)addBtnClick:(id)sender;
#property (strong, nonatomic) NSMutableArray *dataList;
#end
detail.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface DetailViewController : UIViewController<UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTxt;
#property (weak, nonatomic) IBOutlet UITextField *mobileTxt;
#property (weak, nonatomic) IBOutlet UITextField *emailIdTxt;
- (IBAction)saveBtnClick:(id)sender;
#property (strong,nonatomic) NSManagedObject *userData;
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.userData) {
[self.nameTxt setText:[self.userData valueForKey:#"name"]];
[self.mobileTxt setText:[self.userData
valueForKey:#"mobileNumber"]];
[self.emailIdTxt setText:[self.userData valueForKey:#"email"]];
[self.imgView setImage:[UIImage imageWithData:[self.userData
valueForKey:#"imageView"]]]; }
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
/*
#pragma mark - Navigation
- (IBAction)browseBtn:(id)sender
{
UIImagePickerController *imgpic =[[UIImagePickerController
alloc]init];
imgpic .delegate =self;
imgpic .sourceType =UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:imgpic animated:YES completion:nil];
}
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
UIImage *choose = info[UIImagePickerControllerOriginalImage];
self.imgView.image=choose;
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)saveBtnClick:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
if (self.userData) {
// Update existing data
[self.userData setValue:self.nameTxt.text forKey:#"name"];
[self.userData setValue:self.mobileTxt.text
forKey:#"mobileNumber"];
[self.userData setValue:self.emailIdTxt.text forKey:#"email"];
UIImage *sampleimage = _imgView.image;
NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 1.0);
[self.userData setValue:dataImage forKey:#"imageView"];
} else {
// Create a new data
NSManagedObject *newDevice = [NSEntityDescription
insertNewObjectForEntityForName:#"Details"
inManagedObjectContext:context];
[newDevice setValue:self.nameTxt.text forKey:#"name"];
[newDevice setValue:self.mobileTxt.text forKey:#"mobileNumber"];
[newDevice setValue:self.emailIdTxt.text forKey:#"email"];
UIImage *sampleimage = _imgView.image;
NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 1.0);
[newDevice setValue:dataImage forKey:#"imageView"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface DetailViewController :
UIViewController<UITextFieldDelegate,UINavigationControllerDelegate,
UIIma
gePickerControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTxt;
#property (weak, nonatomic) IBOutlet UITextField *mobileTxt;
#property (weak, nonatomic) IBOutlet UITextField *emailIdTxt;
#property (weak, nonatomic) IBOutlet UIImageView *imgView;
- (IBAction)browseBtn:(id)sender;
- (IBAction)saveBtnClick:(id)sender;
#property (strong,nonatomic) NSManagedObject *userData;
#end
let alert = UIAlertController(title:"Error", message: "No Internet Connection", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in}))
alert.addAction(UIAlertAction(title: "Try Again", style: .default, handler: { (action) in
self.networkCall(text: self.daySelected)
}))
self.present(alert, animated: false, completion: nil)
+(void) insetPlusUpdate:(NSDictionary *)dataa {
NSManagedObjectContext * context;
if (![[NSThread currentThread] isMainThread]) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[APP_DELEGATE persistentStoreCoordinator]];
} else {
context = [APP_DELEGATE managedObjectContext];
}
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate * check = [NSPredicate predicateWithFormat:#"attribute == %#", Dict[#"key"]];
[request setPredicate:check];
NSError * error = nil;
if ([context countForFetchRequest:request error:&error] == 0) {
Entity.attribute = #"";
} else {
NSArray * array = [context executeFetchRequest:request error:&error];
EntityName * entity = [array firstObject];
Entity.attribute = #"";
}
}
+(NSString *)fetch:(NSString *)feed_id{
NSManagedObjectContext * context;
if(![[NSThread currentThread] isMainThread]){
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[APP_DELEGATE persistentStoreCoordinator]];
} else {
context = [APP_DELEGATE managedObjectContext];
}
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ENTITYNAME" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate * check = [NSPredicate predicateWithFormat:#"attribute == %#", Dict[#"key"]];
[request setPredicate:check];
NSError * error = nil;
if ([context countForFetchRequest:request error:&error] > 0) {
NSArray * array = [context executeFetchRequest:request error:&error];
ENTITYNAME * fetchData = [array firstObject];
NSString * string = fetchData.attribte[#"key"];
return string;
}
return nil;
}
+(BOOL)delete{
NSManagedObjectContext * context;
if (![[NSThread currentThread] isMainThread]) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[APP_DELEGATE persistentStoreCoordinator]];
} else {
context = [APP_DELEGATE managedObjectContext];
}
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ENTITYNAME" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error = nil;
NSBatchDeleteRequest *deleteRequest = [[NSBatchDeleteRequest alloc] initWithFetchRequest: request];
#try{
[context executeRequest:deleteRequest error:&error];
if([context save:&error]){
NSLog(#"Deleted");
return [context save:&error];
}
else{
return [context save:&error];
}
}
#catch(NSException *exception){
NSLog(#"failed %#",exception);
return [context save:&error];
}
}
sample coding view1
#import "ViewController.h"
#import "DetailViewController.h"
#interface ViewController ()
{
NSInteger indexPathvalue;
}
#end
#implementation ViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(#"call this one2");
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSManagedObjectContext *managedObjectContext = [self
managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]
initWithEntityName:#"Details"];
self.dataList = [[managedObjectContext executeFetchRequest:fetchRequest
error:nil] mutableCopy];
[_coreDataList reloadData];
NSLog(#"call this one");
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
return self.dataList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell
alloc]initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
NSManagedObject *user = [self.dataList objectAtIndex:indexPath.row];
cell.textLabel.text = [user valueForKey:#"name"];
cell.detailTextLabel.text = [user valueForKey:#"mobileNumber"];
cell.imageView.image = [UIImage imageWithData:[user
valueForKey:#"imageView"]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
indexPathvalue = indexPath.row;
[self performSegueWithIdentifier:#"detailView" sender:self];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:
(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:
(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[context deleteObject:[self.dataList objectAtIndex:indexPath.row]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Delete! %# %#", error, [error localizedDescription]);
return;
}
[self.dataList removeObjectAtIndex:indexPath.row];
[_coreDataList reloadData];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)addBtnClick:(id)sender {
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if ([segue.identifier isEqualToString:#"detailView"])
{
NSManagedObject *obj = [self.dataList objectAtIndex:indexPathvalue];
DetailViewController *detail = segue.destinationViewController;
detail.userData = obj;
}
}
#end
sample detail view
#import "DetailViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.userData) {
[self.nameTxt setText:[self.userData valueForKey:#"name"]];
[self.mobileTxt setText:[self.userData valueForKey:#"mobileNumber"]];
[self.emailIdTxt setText:[self.userData valueForKey:#"email"]];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
/*
savebutton
- (IBAction)saveBtnClick:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
if (self.userData) {
// Update existing data
[self.userData setValue:self.nameTxt.text forKey:#"name"];
[self.userData setValue:self.mobileTxt.text forKey:#"mobileNumber"];
[self.userData setValue:self.emailIdTxt.text forKey:#"email"];
UIImage *sampleimage = [UIImage imageNamed:#"icon.png"];
NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 1.0);
[self.userData setValue:dataImage forKey:#"imageView"];
} else {
// Create a new data
NSManagedObject *newDevice = [NSEntityDescription
insertNewObjectForEntityForName:#"Details"
inManagedObjectContext:context];
[newDevice setValue:self.nameTxt.text forKey:#"name"];
[newDevice setValue:self.mobileTxt.text forKey:#"mobileNumber"];
[newDevice setValue:self.emailIdTxt.text forKey:#"email"];
UIImage *sampleimage = [UIImage imageNamed:#"icon.png"];
NSData *dataImage = UIImageJPEGRepresentation(sampleimage, 1.0);
[newDevice setValue:dataImage forKey:#"imageView"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
#end

Resources