If an entry is changed in core data on another device, NSLog messages show that it's noticed the changes, but NSPersistentStoreDidImportUbiquitousContentChangesNotification is not being called. It takes until a save on my first device for it to know to update my tableview.
This is my code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(iCloudUpdates:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:nil];
Anybody know why this might not be working?
First option:
If your problem is a delay in updating both devices, that is normal behavior, Apple doesn't guarantee update timing, but it is usually fast.
If on the other side the problem is that iCloudUpdates method is not called make sure that signature is correct, should be :
-(void)iCloudUpdates:(NSNotification*)notification {
// do your stuff here
}
Second option:
at time of writing, iOS 5 has big big problem with iCloud and CoreData, I recently shipped my application without iCloud support.
If you want to know what is going on turn logging on for both CoreData and iCloud by putting:
-com.apple.CoreData.SQLDebug 1
-com.apple.coredata.ubiquity.logLevel 3
in your scheme manager, under run->arguments tab.
If you see some strange errors in 'destination' device that's the case of iCloud not working for being bugged.
I think the problem with your code is that in the "addObserver" you've set the object to nil. The object should be your persistentStoreCoordinator as shown below.
__weak NSPersistentStoreCoordinator *psc = self.context.persistentStoreCoordinator;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(iCloudUpdates:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:psc];
Related
I want my OSX app to call a function when the user's screen becomes available, ex: their computer wakes from sleep or the user turns on their screen. Basically any time the user goes from having no screen active to having one active, I would like my function to be called.
I'm not sure if the best way to do this is to:
Check for changes in sleep/wake state or
Use CGDisplayReconfigurationCallback or
Listen for a NSWorkspaceScreensDidWakeNotification
Which of these seems like the best way to do this, or should I do something else entirely? Some kind of example Swift code would be really helpful since snippets of code implementing any of these seem to be few and far between. Thanks.
I was able to receive notifications for screen lock and screen unlock on OS X 10.10.5. Sorry I don't know Swift yet so here's the Objective-C:
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(screenIsLocked) name:#"com.apple.screenIsLocked" object:nil];
[center addObserver:self selector:#selector(screenIsUnlocked) name:#"com.apple.screenIsUnlocked" object:nil];
- (void)screenIsLocked {
NSLog(#"screen is locked");
}
- (void)screenIsUnlocked {
NSLog(#"screen is unlocked");
}
Well, first of all you have to test, whether you can get the solution for all situations with the different technologies. There are many situations that turns on the display (system restart, awake from sleep, connecting/disconneting screens, …) and I'm not sure, whether they are all triggered by all technologies.
However, as a general rule: You want to know, when the screen is activated. So use the notification that tells you that the screen is activated. This is NSWorkspaceScreensDidWakeNotification. As long it works I would always use the technique that is closest to what I want.
I'm writing an app that uses Core Data and is synced with iCloud. To do this, I have a UIManagedDocument that I set up as shown below:
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:[self iCloudStoreURL]];
document.persistentStoreOptions = #{NSPersistentStoreUbiquitousContentNameKey: [document.fileURL lastPathComponent], NSPersistentStoreUbiquitousContentURLKey: [self iCloudCoreDataLogFilesURL], NSMigratePersistentStoresAutomaticallyOption: #YES, NSInferMappingModelAutomaticallyOption : #YES};
self.mydoc = document;
[document release];
[document.managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentContentsChanged:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:document.managedObjectContext.persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentStateChanged:) name:UIDocumentStateChangedNotification object:document];
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.mydoc.fileURL path]]) {
// does not exist on disk, so create it
[self.mydoc saveToURL:self.mydoc.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self populateTable];//synchronous call. few items are added
[self iCloudIsReady];
}];
} else if (self.mydoc.documentState == UIDocumentStateClosed) {
// exists on disk, but we need to open it
[self.mydoc openWithCompletionHandler:^(BOOL success) {
[self iCloudIsReady];
}];
} else if (self.mydoc.documentState == UIDocumentStateNormal) {
// already open and ready to use
}
}
My issue with this approach is that I keep getting "Optimistic locking failure" when running the app in two devices. I read in Apple's Core Data documentation that a way to "avoid" this kind of issue was to set up the merge policy to NSMergeByPropertyObjectTrumpMergePolicy, something that I am already doing but for some reason is not working.
One thing I can't find is how to fix this. For example, if this is something that could happen, my app should be at least aware and prepared to handle this behavior. But I have no idea on how to handle this. For example, how do I get the conflicting objects and resolve them? Because every time this failure happens, I start getting UIDocumentStateSavingError when trying to save the document and the only way to stop getting this error is by killing the app and re-launching it.
I finally figured this out (at least, I think I did). Apparently, on iOS6+ (I have no idea about iOS5) UIManagedDocument takes care of all the merging for you. So, the observer below, which was only responsible for calling "mergeChangesFromContextDidSaveNotification:" was in fact merging what was just merged.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentContentsChanged:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:document.managedObjectContext.persistentStoreCoordinator];
...
- (void)documentContentsChanged:(NSNotification *)notification {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
Calling "mergeChangesFromContextDidSaveNotification:" was the line responsible for triggering the "Optimistic locking failure". I removed it and everything started working as expected. Unfortunately this only lasted a couple of hours. Now I keep getting the "iCloud Timed Out" error, but this one I'm sure it's Apple's fault.
Anyway, after a ton of bugs and three different iCloud + Core Data approaches, I think I will hold on integrating iCloud into my app. It is far too unstable and buggy. I really wish Apple could have fixed this with iOS6, iCloud + Core Data is a very powerful tool, unfortunately, it is not ready yet.
Thanks to everyone who tried to help.
I'm currently developing an iPhone application which needs location services for various use including AR.
I test everything on simulator and on my iPhone 3GS and everything went well.
I recently tested on iPhone4 and on iPad2 and the location service (the little icon in status bar) keeps displaying even when I manually kill the app!
The only way to disable this icon is to manually stop the location service for my app in the settings.
Does anyone know something about this?
If needed I can post my code.
Thank you in advance
Edit :
When I kill the application, go to location services, switch off my app the location icon disappears. But when I switch it back on, it reappears! Is that normal?
I've found the answer! It came from region monitoring, which I enabled before, but removed all code using it weeks ago.
As I had already tested on the iPad, and even if I deleted and re-installed the app, the system seems to have kept information on region I monitored.
Thus, as described by the documentation, the iOS kept on locating for my App, just as startMonitoringSignificantLocationChanges.
Thanks for you answers, it gave me a better understanding of the location system and how to efficiently use it (in particular thanks to progrmr and Bill Brasky)
Sounds like you're app is going into the background and still using CLLocation. You can stop CLLOcationManager when you receive notification that you're app is resigning active, that's the best way. Then resume when it becomes active. The answer in this question show how to do that here
[EDIT] When your app goes into the background or resigns active for any reason (ie: phone call) you should stop location services at that time. You need to subscribe to the notifications and provide a method to stop and start location services, something like this:
-(void)appDidBecomeActiveNotif:(NSNotification*)notif
{
[locationManager startUpdatingLocation];
}
-(void)appWillResignActiveNotif:(NSNotification*)notif
{
[locationManager stopUpdatingLocation];
}
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActiveNotif:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillResignActiveNotif:) name:UIApplicationWillResignActiveNotification object:nil];
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
I ran into this same exact issue when using the region monitoring tools. It didn't matter what I did to disable the regions, the arrow remained. I did finally solve the issue by cleaning up the calls to locationManager. If you are closing your view and don't need the location manager, set it to nil and/or release it. If you are monitoring location in the background, it will stay up there, but if not, make sure you are cleaning up all your location monitoring.
It seems like it is a bug, but as I found out, it is not. Just requires a bit more cleanup.
I have been battling with this problem for a while, and I think I've finally gotten to the bottom of it..
The reason that the location service doesn't stop when you ask it to is not because you haven't stopped it or released it properly. It's actually caused by releasing and re-allocating the CLLocationManager itself, in my experience.
If you have code which releases your CLLocationManager in applicationDidEnterBackground, and then you allocate a brand new one in applicationDidEnterForeground, etc., then you'll probably have this problem.
The solution is this:
Only create your CLLocationManager object once, in applicationDidFinishLaunching.
To start, call startUpdatingLocation, startMonitoringSignificantLocationChanges etc. as normal.
To stop updates, call the appropriate stopUpdatingLocation, stopMonitoringSignificantLocationChanges etc. as normal.
Never, ever release your CLLocationManager or set its' reference to nil (except possibly in applicationWillTerminate, but that probably won't make any difference).
This way, I went from having my app continue to use location services for up to 12 hours after putting my app away in the background, to the location services arrow disappearing within 10 seconds of backgrounding with this new approach.
Note: Tested on iPhone 4S running iOS 5.1.1. To get accurate results on your app's performance in this regard, make sure you go into Settings->Location Services->System Services and turn off the Status Bar Icon switch. That way, the status bar arrow will accurately reflect usage by apps alone.
Presumably this is so users don't need to stare at the bar to notice some mischievous app is using location services. That icon appears when you use any location services and remains for some indeterminate time afterwards.
This is intentional behavior Apple wants users to know which apps are using their locations. It seems this is sensitive data, wouldn't you agree?
This is the solution which fixed this problem for me.
Just stop monitering location changes in
- (void) applicationDidEnterBackground: (UIApplication *)application
{
[locationManager stopMonitoringSignificantLocationChanges];
locationManager.delegate = nil;
}
not in applicationWillEnterForeground: .Still,it takes a few seconds to disappear locating icon.
I don't know why it isn't working in the latter method.
I've run into that problem a while ago and found it useful to apply only one method of applicationDelegate object
- (void)applicationWillEnterForeground:(UIApplication *)application;
If you'll stop your CLLocationManager from receiving updates inside that call, you'll be alright. Of course you'll need to start updating somewhere else, and - (void)applicationDidBecomeActive:(UIApplication *)application; will be a good choice. Also you need to note, that there are two methods of location awareness
the gps based -(void)start/stop_UpdatingLocation;
and the 3g/wi-fi based -(void)start/stop_MonitoringSignificantLocationChanges;
I am writing an iPhone app in which I have three countdown timers, that each set a local notification when they are started by the user (IBAction).
My question is, how can I reference to an existing local notification elsewhere in the code? I have tried referencing it directly like this which crashes:
[[UIApplication sharedApplication] cancelLocalNotification:myNotification];
And like this after scheduling the notification and adding it to the user defaults:
In my scheduling method...
[myDefaults setObject:myNotification forKey:#"myNotification"];
...
And in my cancelling method...
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
[[UIApplication sharedApplication] cancelLocalNotification:[myDefaults objectForKey:#"myNotification"]];
[myDefaults synchronize];
My app crashes with a SIGABRT error on the cancelLocalNotification line above. Can anybody tell me where I am going wrong?
Thanks in advance!
Ok, I've found a working solution to my issue after thinking it through step by step. I found I was going about the whole issue entirely the wrong way!
Essentially, I found you don't need any local notifications when the app is active (i.e. in foreground). So instead of setting the local notification when the timer is started, I set the relevant notifications up when the app is about to resign.
I put these observers in my viewDidLoad;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appWillResign) name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appIsActive) name:UIApplicationDidBecomeActiveNotification
object:nil];
and in my appWillResign method, I set up the notifications for any active timers. When the app is resumed, I simply cancel ALL the notifications.
[[UIApplication sharedApplication] cancelAllLocalNotifications];
In short, you shouldn't need to reference the notifications elsewhere in the code. You only set them up when they are absolutely needed: when the app is backgrounded! So you really don't need to 'store' them anywhere for later use.
Thanks for your contribution #Learner, you helped put me on the right track! :D
It is difficult to predict but generally SIGABRT comes when you access an nil or released object.
So in [[UIApplication sharedApplication] cancelLocalNotification:myNotification]; `
it can be "myNotification" which is nil.
OR if [myDefaults objectForKey:#"myNotification"] returns nil.
Possible resason for that can be that NsUserDefaults stores only few data type as mentioned in following paragraph of class reference.
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
What i can predict is that myNotification which is being stored is of type UILocalNotification. so probably storing of data is failing.
Please debug the code further and post if above cases are not working.
The fix is to sync the NSUserDefaults. I'm assuming you just set the object and did not synchronize it. If so the object will not actually save to NSUserDefaults, rather just to the local NSUserDefaults object. What you need to do is call [defaults synchronize] after setting the object.
I am assuming I need to implement:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(resignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
but am not sure if this is the right notification to determine my app is about to leave active state.
Is this a good place to cancel network connections, along with app termination?
There are cases where UIApplicationWillResignActiveNotification is sent but the application does not enter the background, for example if a call is received but the user chooses not to answer it.
Use UIApplicationDidEnterBackgroundNotification to be notified when entering the background. Be aware that this will sometimes be sent after UIApplicationWillEnterForegroundNotification if the application is quickly opened again.