My app is downloading JSON objects in when the app enters background mode. The app converts them to core data entities. The issue that I'm running in is that I need to merge the managed object contexts for these core data entities with the main managed object context.
The way to merge these changes is through notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextHasChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
- (void)contextHasChanged:(NSNotification*)notification
{
NSLog(#"received notification with object: %#",[[notification object] description]);
if ([notification object] == [AppUser managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextHasChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[AppUser managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
For some reason my code does not receive these notifications while running in background. Does the app continue to generate notifications while it is running in background mode? Or is it something with the location of where/when I register for such notifications that's wrong?
Thank you for the clarification!
app continues to send notifications either in main or background. you need to take care of
the observer should not be released during add observer and posting notification. i thnk there may some mistak in implementaion read this
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html
Once you press the Home button, your app goes into suspended mode. It won't process the above notifications until you "wake" it up by tapping on its icon.
To ensure that your app continues to do its task, you need to request background task completion. The OS will then give you up to 600 seconds (10 minutes) to complete whatever task your app is doing before suspending it.
You can read more about it here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
Specifically, look for "Background Execution and Multitasking" in the above link.
There are limitations to what type of notifications you can receive while in the background. Also the sending of notifications is something you schedule before entering the background.
If you need to continue doing work when the app enters thebackground you should call beginBackgroundTaskWithExpirationHandler as well.
Main documentation is here:
http://developer.apple.com/library/ios/ipad/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
Scroll down to the section in Background Execution and Multitasking
Related
I have created GPS base Application.
In which App logged GPS data every 1 second.
App used the NSTimer to fetch GPS data every second.
This NSTimer is start in background. The NSTimer is start when app received the silent push notification from APNS.
I have seen a problem in iOS 7 that when Phone attached with power cable at that time timer call appropriately but without attached power cable timer stops while App in background.
Any inputs to resolve this issue greatly appreciated.
An NSTimer is not guaranteed to fire if your app is not in foreground. Once you unplug the cable the system puts your app into background to save battery.
Using an NSTimer is not the supported method to get location data. Your CLLocationManager will tell its delegate when there is a new location. No need to poll it.
If you need to track geolocation in background you need to declare location updates as a background mode from the capabilities tab in Xcode 5 target settings. Otherwise your location manager will stop delivering location updates once your app is not in foreground.
There are only few use cases that you can implement for foreground. If you don't want your application to be refused within review please don't use any hacks. Of course, you can use NSTimer in background, but it must be created in thread (runloop) of background task. But this background task lives for the only certain amount of time, so your timer must fire during this period. In other words, your first goal is to create background task, and only then you can use the timer. There are few cases suitable for your purpose (that allow you to create this task): 1.Monitor significant location changes or region 2.Fetch data (iOs 7). So please refer
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"BG fetch in action");
//the only 30 sec is allowed to perform all your actions
//during this period you can each second track GPS data
[self performSelector:#selector(finishBackgroundFetch:) withObject:completionHandler afterDelay:27];
[[NSNotificationCenter defaultCenter] postNotificationName:#"StartMyGPSRoutine" object:nil];
}
-(void)finishBackgroundFetch:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"BG fetch completion handler");
[[NSNotificationCenter defaultCenter] postNotificationName:#"ForceStopAnyBackgroundTaskCreatedWithStartMyGPSRoutine" object:nil];
completionHandler(UIBackgroundFetchResultNewData);
}
of your application delegate -- this will be useful in your case as well as monitor significant location changes.
You ought to put anywhere:
if ([[UIApplication sharedApplication] respondsToSelector:#selector(setMinimumBackgroundFetchInterval:)]) {
NSLog(#"Set Force BG interval to %ld", interval);
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:interval];
}
In my case I fetch the GPS data each 5 min so 27sec to complete this task is not so long. Anyway you can play with time intervals with XCode. Please refer Main Menu->Debug->Simulate Background Fetch
I'm trying to read push message when app is in background or killed and user tap on application icon(which has badge) instead from notification center.I have seen few questions in stackoverflow regarding this, but non of them solve this problem,
Here is the scenario,
I receive a push message.
And then i ignore the message.
Next i launch the application by tapping icon which has badge(not from the notification center)
It won't call didReceiveRemoteNotification: method.
As per iOS application life cycle, it'll run didFinishLaunchingWithOptions: when app launches, and next time it'll call applicationWillEnterForeground: and applicationDidBecomeActive: methods. my question is how can i read all the push messages from above life cycle methods which i received previously and ignored?
I have already tried below snippet in didFinishLaunchingWithOptions: (From this answer) but i always get null as payload.
NSDictionary *pushNotificationPayload = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(pushNotificationPayload) {
NSLog(#"Payload is not null");
[self application:application didReceiveRemoteNotification:pushNotificationPayload];
}else{
NSLog(#"Payload is null");
}
Any valuable answer appreciated, Thanks.
BTW, i use iOS 6
In my app I'm downloading lots of images on a method.
I'm using a
downloadTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:downloadTask];
downloadTask = UIBackgroundTaskInvalid;
}];
This is working fine, if I press the home or sleep button, the images continue downloading.
I'm showing the progress in a UIProgressView inside an UIAlertView, and when the percent is 100% the alertView is dissmised and I change the viewController to other where I show the donwloaded images.
But I only want this to happen if the app is really active at the moment the download finish.
I have been looking at the app state and while it's downloading with the screen off.
[UIApplication sharedApplication].applicationState
the state is UIApplicationStateActive during all the donwload
How can I can know if the downloading is happening with the screen off or on?
EDITED AFTER ACCEPTING THE ANSWER:
I just discovered, if I tap the home button, the app enters in UIApplicationStateBackground, if I tap the wake/sleep it enters in UIApplicationStateInactive
Following the approach of the correct answer, my app contines donwloading in both cases.
The screen is off in two states (apart from when the app has not been even opened):
suspended : in this case you don't have to worry because the download won't procede until the app gets active again; It will enter this state on
background : it's in this state for a limited amount of time before going in suspend, and the screen is already off in this moment. Here you may want to check then whether to do all the things you said or not, because in this state code can be still executed. In this state the app status is UIApplicationStateBackground, so you could just perform a check like this:
You probably want to check whether the app is in background execution in order to achieve the result. Just like this:
if([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
// Do stuff
}
If it's in background, so the screen is off.
UPDATE: after few test, what I figured out is that the behaviour you are expieriencing is probably due to the execution of the download on the main thread.
You should send the download on (for instance) the global queue. This way the application will enter the background state as expected:
....
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.bti];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doBackgroundStuff];
});
....
This way, if the app is put on background while the download is in progress, the application state will turn into UIApplicationStateBackground, and then you can check it as I wrote initially. If you are doing UI updates during the progress remember to send them back to the main thread (because the download is now on a different one).
You can check whether your app is running in the background or not by setting a flag in the designated application delegate methodsapplicationDidEnterBackground: and applicationWillEnterForeground:. Example:
- (void)applicationDidEnterBackground:(UIApplication *)application
_applicationRunsInForeground = NO;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
_applicationRunsInForeground = YES;
}
If you don't want to have this _applicationRunsInForeground flag inside your application delegate, you could observe the delegate's NSNotifications in your viewcontroller class instead (UIApplicationWillEnterForegroundNotification and UIApplicationDidEnterBackgroundNotification).
I need to schedule a task in background when the application enter background state.
I have to do this in order to call a remote service each x time and then show a local notification when some event happend with the remote service.
(I know it's looks like RPN, yes it is, but for some reason I am not able to use PRM)
I tried this code :
- (void)applicationDidEnterBackground:(UIApplication *)application{
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void){
remoteServiceCallThread = [[NSThread alloc] initWithTarget:self selector:#selector(doRemoteCall:) object:nil];
[remoteServiceCallThread start];
}];
}
- (void)applicationWillEnterForeground:(UIApplication *)application{
[remoteServiceCallThread cancel];
}
I put breakpoint in the doRemoteCall selector, put is not working.
Maybe my approach is not the best one. If you have any other hack to doing this operation like I describe it I'll take it.
Thank you.
You are not starting the thread, it's initialization code is at the expiration handler block which will be called right before the app is shut down with a timeout:
A handler to be called shortly before the application’s remaining
background time reaches 0. You should use this handler to clean up and
mark the end of the background task. Failure to end the task
explicitly will result in the termination of the application. The
handler is called synchronously on the main thread, thus blocking the
application’s suspension momentarily while the application is
notified.
The task should be active for 10 minutes only (that is driven by iOS) if your app is not supporting one of the background modes (gps, audio, voip).
You also need to keep the returned UIBackgroundTaskIdentifier reference to be able to mark it as ended if the user brings the app to foreground or when task time is going to the end (that's when the handler block is called).
I'm trying to detect if user take a screenshot in order to warn them this is a video with copyright. I've tried Darwin Notification, but it didn't work...
NSMetadataQuery might be a solution, but we want the app to run on iOS4 as well. Can somebody help me?
Thanks!:p
It is not possible to detect a screenshot has (or will) be taken on iOS 5.
In iOS 4.0, there was a Darwin notification fired PictureWasTakenNotification, but not anymore.
The documentation does not mention anything, and using NSNotificationCenter to subscribe to all notifications only shows silence after taking a screenshot.
before ios 4.0 you can register for PictureWasTakenNotification .This notification is sent when screenshot is taken.No notifications will be sent before the screenshot was taken.
But after that this has been discontinued and IMHO there is no other way round... :/
you can detect it with adding an observer to UIApplicationUserDidTakeScreenshotNotification .
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
}];