I'm not getting the NSPersistentStoreDidImportUbiquitousContentChangesNotification (with code sample) - cocoa

I'm trying to learn how to use iCloud with coredata. My target is to sync a single database file on mac/ios. Both my mac and iOS apps seem to be updating iCloud as I can see it under System Preferences->iCloud->Manage ... There's an app named Unknown which everytime I change something on the ios/mac app is getting bigger and bigger. So that makes me think The apps are storing changes to the cloud. The problem is, I'm not getting the NSPersistentStoreDidImportUbiquitousContentChangesNotification when i change something on the other platform.
There's my code (mac app):
- (void)persistentStoreDidImportUbiquitousContentChangesNotification:(NSNotification *)notif
{
NSLog(#"%#", notif);
}
- (NSURL *)applicationFilesDirectory
{
NSString *identifier = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *libraryURL = [[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:identifier];
if(![fileManager fileExistsAtPath:[libraryURL path]])
{
[fileManager createDirectoryAtPath:[libraryURL path] withIntermediateDirectories:YES attributes:nil error:nil];
}
return libraryURL;
}
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel)
{
return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"MyApp" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator)
{
return __persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom)
{
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];
if (!properties)
{
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError)
{
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok)
{
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
else
{
if ([[properties objectForKey:NSURLIsDirectoryKey] boolValue] != YES)
{
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSString *appBundleID = [[NSBundle mainBundle] bundleIdentifier];
NSURL *storeURL = [applicationFilesDirectory URLByAppendingPathComponent:#"MyApp.sqlite"];
NSURL *cloudURL = [[fileManager URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:#"Database"];
NSPersistentStoreCoordinator *coordinator = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom] autorelease];
NSDictionary *optionsDict = [NSDictionary dictionaryWithObjectsAndKeys:appBundleID, NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:optionsDict error:&error])
{
[[NSApplication sharedApplication] presentError:error];
return nil;
}
__persistentStoreCoordinator = [coordinator retain];
return __persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator)
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:#"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:#"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(persistentStoreDidImportUbiquitousContentChangesNotification:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
return __managedObjectContext;
}

I faced the same problem with iOS SDK.
To get "NSPersistentStoreDidImportUbiquitousContentChangesNotification" notification, you must call "- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject" from NSNotification center.
I added this line
[[NSNotificationCenter defaultCenter] addObserver:masterViewController selector:#selector(notifyiCloud:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:[self persistentStoreCoordinator]];
in the "didFinishLaunchingWithOptions" method of AppDelegate.
In my case, (iOS5) it worked perfectly, but I don't know if it will work in mac os, too.

I was having the same problem with CoreData stack running on both iOS and MacOS X. Between my iPhone and iPad, the NSPersistentStoreDidImportUbiquitousContentChangesNotification:s worked fine.
On the Mac I ended up periodically executing an NSFetchRequest (say every 5 seconds) and discarded the result. Only then did the notification arrive.

Related

Crash Occurring on First Launch When populating core data database

I keep getting an error in the debugger for my application saying,
2013-06-23 16:07:15.826 collection view recipies[5681:c07] -[NSManagedObject length]: unrecognized selector sent to instance 0x9495280
2013-06-23 16:07:15.827 collection view recipies[5681:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject length]: unrecognized selector sent to instance 0x9495280'
* First throw call stack:
(0x26ac012 0x1517e7e 0x27374bd 0x269bbbc 0x269b94e 0x2b11c4 0x16d80a 0x4464 0x64f2da 0x6508f4 0x652b91 0x19c2dd 0x152b6b0 0x18eefc0 0x18e333c 0x18eeeaf 0x23b2bd 0x183b56 0x18266f 0x182589 0x1817e4 0x18161e 0x1823d9 0x1852d2 0x22f99c 0x17c574 0x17c76f 0x17c905 0x185917 0x14996c 0x14a94b 0x15bcb5 0x15cbeb 0x14e698 0x2c06df9 0x2c06ad0 0x2621bf5 0x2621962 0x2652bb6 0x2651f44 0x2651e1b 0x14a17a 0x14bffc 0x1e9d 0x1dc5)
libc++abi.dylib: terminate called throwing an exception
In My application delegate, if check to see if the application is being launched for the first time, and if it is, I then add several image paths to the core data structure.
In AppDelegate.m under ApplicationDidFinishLaunchingWithOptions,
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// app already launched
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first launch ever
NSArray *mainDishImages = [NSArray arrayWithObjects:#"egg_benedict.jpg", #"full_breakfast.jpg", #"ham_and_cheese_panini.jpg", #"ham_and_egg_sandwich.jpg", #"hamburger.jpg", #"instant_noodle_with_egg.jpg", #"japanese_noodle_with_pork.jpg", #"mushroom_risotto.jpg", #"noodle_with_bbq_pork.jpg", #"thai_shrimp_cake.jpg", #"vegetable_curry.jpg", nil];
NSArray *drinkDessertImages = [NSArray arrayWithObjects:#"angry_birds_cake.jpg", #"creme_brelee.jpg", #"green_tea.jpg", #"starbucks_coffee.jpg", #"white_chocolate_donut.jpg", nil];
for (NSString *imagePath in mainDishImages) {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:#"Recipe" inManagedObjectContext:context];
[newRecipe setValue:imagePath forKey:#"imageFilePath"];
}
for (NSString *imagePath in drinkDessertImages) {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:#"Deserts" inManagedObjectContext:context];
[newRecipe setValue:imagePath forKey:#"imageFilePath"];
}
}
And I access that data in my collectionViewController, I access that data.
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Deserts"];
deserts = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSFetchRequest *fetchRequestTwo = [[NSFetchRequest alloc] initWithEntityName:#"Recipe"];
meals = [[managedObjectContext executeFetchRequest:fetchRequestTwo error:nil] mutableCopy];
recipeImages = [NSArray arrayWithObjects:meals, deserts, nil];
[self.collectionView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Deserts"];
deserts = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSFetchRequest *fetchRequestTwo = [[NSFetchRequest alloc] initWithEntityName:#"Recipe"];
meals = [[managedObjectContext executeFetchRequest:fetchRequestTwo error:nil] mutableCopy];
recipeImages = [NSArray arrayWithObjects:meals, deserts, nil];
UICollectionViewFlowLayout *collectionViewLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
collectionViewLayout.sectionInset = UIEdgeInsetsMake(5, 0, 5, 0);
self.navigationController.navigationBar.translucent = YES;
self.collectionView.contentInset = (UIEdgeInsetsMake(44, 0, 0, 0));
selectedRecipes = [NSMutableArray array];
}
According to crashalytics, the error is in the line where it says
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section] objectAtIndex:indexPath.row]];
In the method
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section] objectAtIndex:indexPath.row]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame-selected.png"]];
return cell;
}
I hope you can help. Thanks In Advance!
The UIImage method imageNamed takes an NSString as argument, but you pass a NSManagedObject to it.
You should get the image path from the managed object first. Try this:
id managedObject = [recipeImages[indexPath.section] objectAtIndex:indexPath.row];
NSString* imagePath = [managedObject valueForKey:#"imageFilePath"];
recipeImageView.image = [UIImage imageNamed:imagePath];

how could i integrate via me social site into iphone app

hi i want to integrate Via Me social site into my iphone app,i googled but didn't find any samples.
The basic process is as follows:
Create a custom URL scheme for your app. Via Me will use this after the user has been authenticated, to return to your app. In my example, I created one called "robviame://"
Register your app at http://via.me/developers. This will give you a client id and a client secret:
When you want to authenticate the user, you call:
NSString *redirectUri = [[self redirectURI] stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:#"https://api.via.me/oauth/authorize/?client_id=%#&redirect_uri=%#&response_type=code", kClientID, redirectUri];
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
What that will do is fire up your web browser and give the user a chance to log on and grant permissions to your app. When user finishes that process, because you've defined your custom URL scheme, it will call the following method in your app delegate:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// do whatever you want here to parse the code provided back to the app
}
for example, I'll call a handler for my Via Me response:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
ViaMeManager *viaMeManager = [ViaMeManager sharedManager];
if ([[url host] isEqualToString:viaMeManager.host])
{
[viaMeManager handleViaMeResponse:[self parseQueryString:[url query]]];
return YES;
}
return NO;
}
// convert the query string into a dictionary
- (NSDictionary *)parseQueryString:(NSString *)query
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSArray *queryParameters = [query componentsSeparatedByString:#"&"];
for (NSString *queryParameter in queryParameters) {
NSArray *elements = [queryParameter componentsSeparatedByString:#"="];
NSString *key = [elements[0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *value = [elements[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
value = [[value componentsSeparatedByString:#"+"] componentsJoinedByString:#" "];
[dictionary setObject:value forKey:key];
}
return dictionary;
}
That handler might, for example, save the code and then request the access token:
- (void)handleViaMeResponse:(NSDictionary *)parameters
{
self.code = parameters[#"code"];
if (self.code)
{
// save the code
[[NSUserDefaults standardUserDefaults] setValue:self.code forKey:kViaMeUserDefaultKeyCode];
[[NSUserDefaults standardUserDefaults] synchronize];
// now let's authenticate the user and get an access key
[self requestToken];
}
else
{
NSLog(#"%s: parameters = %#", __FUNCTION__, parameters);
NSString *errorCode = parameters[#"error"];
if ([errorCode isEqualToString:#"access_denied"])
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Via Me functions will not be enabled because you did not authorize this app"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
else
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Unknown Via Me authorization error"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
}
}
and the code to retrieve the token might look like:
- (void)requestToken
{
NSURL *url = [NSURL URLWithString:#"https://api.via.me/oauth/access_token"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
NSDictionary *paramsDictionary = #{#"client_id" : kClientID,
#"client_secret" : kClientSecret,
#"grant_type" : #"authorization_code",
#"redirect_uri" : [self redirectURI],
#"code" : self.code,
#"response_type" : #"token"
};
NSMutableArray *paramsArray = [NSMutableArray array];
[paramsDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[paramsArray addObject:[NSString stringWithFormat:#"%#=%#", key, [obj stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding]]];
}];
NSData *paramsData = [[paramsArray componentsJoinedByString:#"&"] dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:paramsData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error)
{
NSLog(#"%s: NSURLConnection error = %#", __FUNCTION__, error);
return;
}
NSError *parseError;
id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError)
{
NSLog(#"%s: NSJSONSerialization error = %#", __FUNCTION__, parseError);
return;
}
self.accessToken = results[#"access_token"];
if (self.accessToken)
{
[[NSUserDefaults standardUserDefaults] setValue:self.accessToken forKey:kViaMeUserDefaultKeyAccessToken];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}];
}
Hopefully this will be enough to get you going. This is described in greater detail at the http://via.me/developers page.

Core Data Mac OS X app and compatibilty with OS X 10.7

Hello i have created a Simple Application with Core Data and OS X 10.8, it's the simple template that xcode create, but if i change the Base SDK to 10.7 xcode give me this error on this method in App Controller:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:#[NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
} else {
if (![properties[NSURLIsDirectoryKey] boolValue]) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"SimpleApp.storedata"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_persistentStoreCoordinator = coordinator;
return _persistentStoreCoordinator;
}
i receive this error:
how i can do?
This is actually not a problem with Core Data, but this is having to do with object subscripting. In order for these literals to work correctly, your SDK needs to be at least OS X 10.8 or iOS 6.
If there is no other way, you can still use subscripting and keep your SDK to 10.7 by adding a stub header to a category on NSObject that implements the required methods. You can see one such example here. I'd advise keeping your SDK on 10.8, however.

Automatic Lightwieight Migration for core data Problem

I'm having problem use Automatic Lightweight Migration Code in my App delegate !
I read all the apple's documentations about "Automatic Lightweight Migration" but after all I can't find my way to use the codes that is prepared to Automatic Lightweight Migration.
Recently, I just added some new Attribute to an Entity in my data model and I want to keep my old data.
my app delegate code is like this :
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (__persistentStoreCoordinator) {
return __persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
else {
if ([[properties objectForKey:NSURLIsDirectoryKey] boolValue] != YES) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"FinancingPro.storedata"];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
[__persistentStoreCoordinator release], __persistentStoreCoordinator = nil;
return nil;
}
return __persistentStoreCoordinator;
Now I don't know how to change this code to have Automatic Lightweight Migration !
Please Note that my db is NOT SQLlite.
You need to set the options dictionary with the NSInferMappingModelAutomaticallyOption key here:
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
By passing a nil value for options you are telling the store to ignore any migration.

Broken Multithreading With Core Data

This is a better-focused version of an earlier question that touches upon an entirely different subject from before.
I am working on a Cocoa Core Data application with multiple threads. There is a Song and Artist; every Song has an Artist relation. There is a delegate code file not cited here; it more or less looks like the template XCode generates.
I am far better working with the former technology than the latter, and any multithreading capability came from a Core Data template. When I'm doing all my ManagedObjectContext work in one method, I am fine. When I put fetch-or-insert-then-return-object work into a separate method, the application halts (but does not crash) at the new method's return statement, seen below. The new method even gets its own MOC to be safe, and it has not helped any. The result is one addition to Song and a halt after generating an Artist.
I get no errors or exceptions, and I don't know why. I've debugged out the wazoo. My theory is that any errors occurring are in another thread, and the one I'm watching is waiting on something forever.
What did I do wrong with getArtistObject: , and how can I fix it? Thanks.
- (void)main
{
NSInteger songCount = 1;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
/* songDict generated here */
for (id key in songDict)
{
NSManagedObject *song = [NSEntityDescription insertNewObjectForEntityForName:#"Song" inManagedObjectContext:moc];
[song setValue:[songDictItem objectForKey:#"Name"] forKey:#"title"];
[song setValue:[self getArtistObject:(NSString *) [songDictItem objectForKey:#"Artist"]] forKey:#"artist"];
[songDictItem release];
songCount++;
}
NSError *error;
if (![moc save:&error])
[NSApp presentError:error];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:moc];
[moc release], moc = nil;
[[self delegate] importDone];
}
- (NSManagedObject*) getArtistObject:(NSString*)theArtist
{
NSError *error = nil;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Artist" inManagedObjectContext:moc];
[fetch setEntity:entityDescription];
// object to be returned
NSManagedObject *artistObject = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:moc];
// set predicate (artist name)
NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"name = \"%#\"", theArtist]];
[fetch setPredicate:pred];
NSArray *response = [moc executeFetchRequest:fetch error:&error];
if (error)
[NSApp presentError:error];
if ([response count] == 0) // empty resultset --> no artists with this name
{
[artistObject setValue:theArtist forKey:#"name"];
NSLog(#"%# not found. Adding.", theArtist);
return artistObject;
}
else
return [response objectAtIndex:0];
}
#end
Try locking the managed object context before you access it and unlocking it again after you are done with it:
- (void)main
{
NSInteger songCount = 1;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc lock];
[moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
[moc unlock];
/* songDict generated here */
for (id key in songDict)
{
[moc lock];
NSManagedObject *song = [NSEntityDescription insertNewObjectForEntityForName:#"Song" inManagedObjectContext:moc];
[moc unlock];
[song setValue:[songDictItem objectForKey:#"Name"] forKey:#"title"];
[song setValue:[self getArtistObject:(NSString *) [songDictItem objectForKey:#"Artist"]] forKey:#"artist"];
[songDictItem release];
songCount++;
}
NSError *error;
[moc lock];
if (![moc save:&error])
[NSApp presentError:error];
[moc unlock];
[moc lock];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:moc];
[moc unlock];
[moc release], moc = nil;
[[self delegate] importDone];
}
- (NSManagedObject*) getArtistObject:(NSString*)theArtist
{
NSError *error = nil;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc lock];
[moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
[moc unlock];
NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
[moc lock];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Artist" inManagedObjectContext:moc];
[moc unlock];
[fetch setEntity:entityDescription];
// object to be returned
[moc lock];
NSManagedObject *artistObject = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:moc];
[moc unlock];
// set predicate (artist name)
NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"name = \"%#\"", theArtist]];
[fetch setPredicate:pred];
[moc lock];
NSArray *response = [moc executeFetchRequest:fetch error:&error];
[moc unlock];
if (error)
[NSApp presentError:error];
if ([response count] == 0) // empty resultset --> no artists with this name
{
[artistObject setValue:theArtist forKey:#"name"];
NSLog(#"%# not found. Adding.", theArtist);
return artistObject;
}
else
return [response objectAtIndex:0];
}
#end
You can not lock NSManagedObjects!

Resources