I have an iOS game in which I track the number of levels a player plays in a session. A session corresponds to the period between the user pressing the app icon on the home screen and pressing the home button to leave the app.
Surprisingly I don't seem to be getting the leave event for a large fraction of my users. I have implemented the log transmission as follows:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
...
[[EventLogger sharedEventLogger] pushEvent:LEAVE_GAME];
}
...where EventLogger sends an asynchronous HTTP POST request using the ASIHTTP library.
Could it be that the system preempts the transmission before it finishes? It could be that the app crashes and never gets to this point, but that is unlikely.
Have you read the documentation for this method?
" You should use this method to release shared resources, save user data, invalidate timers, and store enough app state information to restore your app to its current state in case it is terminated later. You should also disable updates to your app’s user interface and avoid using some types of shared system resources (such as the user’s contacts database)...Your implementation of this method has approximately five seconds to perform any tasks and return. If you need additional time to perform any final tasks, you can request additional execution time from the system by calling beginBackgroundTaskWithExpirationHandler:. In practice, you should return from applicationDidEnterBackground: as quickly as possible. If the method does not return before time runs out your app is terminated and purged from memory."
Sounds to me like what you're doing is not suggested for this method, but you can try using this beginBackgroundTaskWithExpirationHandler.
I can't be certain that this is the explanation for your issue, but it seems plausible and is consistent with the fact that it isn't being logged only for a portion of your users.
May also be of use:
The system sends a UIApplicationDidGoToBackground notification when the home button is pressed.
Related
I'm working on an iOS application and I want the app to terminate after hanging in the background for more than 5 minutes and when the user opens the app again it should display the login. For now the app terminates immediately when it reaches the background and the login screen when the user re-opens the app. I did this by adding a property called "Application does not run in background" to the info.plist file and I set the value to "YES". As I mentioned at the start what I want to do is to terminate the app after 5 minutes in background but not immediately. And the login screen appearing after the user re-opens the app is working fine.
Application does not run in background property in info.plist
I guess you this is managed by the operating system that for how long the app runs in the background, and the os terminates it when it require resources to get free.
A workaround that could be done is, store the time value in UserDefaults whenever the app goes to background, and whenever the app comes foreground, check for the previous time value. If the difference is greater than 5 minutes, you may direct to login.
You can request background time when you are told that you are being suspended. I believe the max you will get is 3 minutes, not 5. That will prevent your app from being terminated, and keep your app getting CPU time during that time. (Which will use extra battery power.) You will get a notice when that time expires, and you can save your app state and log off at that point.
Search on "Extending Your App's Background Execution Time" in the Xcode help system or Apple's docs for more information. The overview section of that document reads:
Overview
Extending your app’s background execution time ensures that you have adequate time to perform critical tasks. For tasks that require more background time, use Background Tasks.
When your app moves to the background, the system calls your app delegate’s applicationDidEnterBackground(_:) method. That method has five seconds to perform any tasks and return. Shortly after that method returns, the system puts your app into the suspended state. For most apps, five seconds is enough to perform any crucial tasks, but if you need more time, you can ask UIKit to extend your app’s runtime.
You extend your app’s runtime by calling the beginBackgroundTask(withName:expirationHandler:) method. Calling this method gives you extra time to perform important tasks. (You can find out the maximum background time available using the backgroundTimeRemaining property.) When you finish your tasks, call the endBackgroundTask(_:) method right away to let the system know that you are done. If you do not end your tasks in a timely manner, the system terminates your app.
The key bits of that are:
"When your app moves to the background, the system calls your app delegate’s applicationDidEnterBackground(_:) method."
And "You extend your app’s runtime by calling the beginBackgroundTask(withName:expirationHandler:) method"
Note that you should really implement an app level "heartbeat" where the app sends periodic "I'm still here" messages to the server to keep the user logged in. Your server should log the user off if they miss more than one heartbeat message.
I want to fetch location in background even if user forcefully quit the app. Currently my applicationDidEnterBackground method not called when user quit the app. It only works when user simply click on the home button.
Please refer to applicationWillTerminate
By Apple documentation it says:
This method lets your app know that it is about to be terminated and purged from memory entirely. You should use this method to perform any final clean-up tasks for your app, such as freeing shared resources, saving user data, and invalidating timers. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app. For apps that support background execution, this method is generally not called when the user quits the app because the app simply moves to the background in that case. However, this method may be called in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
After calling this method, the app also posts a
UIApplicationWillTerminate
notification to give interested objects a chance to respond to the transition.
If you want to have user's location when app is not in memory, short answer: Impossible.
I'm working with the Flurry API for iOS and I've come across a use case that doesn't seem to be supported:
I'm starting a timed event with -logEvent:timed: to track how long a user spends on a view. While the user is on that view a few parameters are being tracked that I plan to pass along to -endTimedEvent:withParameters: when the user navigates away from the view).
Here is the tricky part, if the user backgrounds the app while on the view, Flurry will automatically end the timed event after 10s without setting any parameters. One solution of course is to observe UIApplicationWillResignActiveNotification and call -endTimedEvent:withParameters: myself. However, I'd like to respect Flurry's setSessionContinueSeconds property and not end the event until the session is over. This way if the user returns to the app within 10s they are still under the same event that is tracking their time on the view.
Is there a way to do this?
For instance, is there a delegate method called when the session willEnd/didEnd where I could manually call -endTimedEvent:withParameters: before Flurry does? or alternatively is there a way to append parameters to a timed event while it is active (without ending it). That way when Flurry ends the session, the event already has the parameters set.
1) Flurry has a feature (only on iOS) to allow sessions to continue into the background that you can try for this use case.
[Flurry setBackgroundSessionEnabled:Yes]
You can find more details about these and other methods in the Analytics readme document that is included with our SDK.
[Flurry setBackgroundSessionEnabled:(BOOL)backgroundSessionEnabled];
This option is disabled by default. When enabled, Flurry will not finish the session if the app is paused for longer than the session expiration timeout. The session report will not be sent when the application is paused and will only be
sent when the application is terminated. This allows for applications that run in the background to keep collecting events data. The time application spends in the background contributes to the length of the application session reported when the application terminates.
[Flurry pauseBackgroundSession];
This method is useful if setBackgroundSessionEnabled: is set to YES. It can be called when application finishes all background tasks (such as playing music) to pause the session. A session report is sent if setSessionReportsOnPauseEnabled is set to YES. If the app is resumed before the session expiration timeout, the session will continue, otherwise a new session will begin.
2) You can keep the data in an array and place the parameters into a separate event that triggers after the timed event instead.
In my application I want to get notified when the instance of the application is deleted.
Even though I can easily get what I am asking simply by googling, I can't find anything useful to me. May be the synonym i'm using deleting the app instance (deleting the app from the list shown at the bottom of the screen when double-clicking the home button in IPad and I'm looking for the UIAppdelegate method called in response to that action).
Is there a way of knowing this?
This is when your app gets killed, either by the user (removing from the task bar) or by the system, to reclaim memory. There's no callback for it. You never get informed that it's going to happen or has happened.
Yes. The - (void)applicationWillTerminate:(UIApplication *)application will be called.
It'll call when user quits the application from the taskbar if and only if your application supports background tasks else it'll be called when user presses the home button to hide your app.
applicationWillTerminate:
Tells the delegate when the application is about to terminate.
- (void)applicationWillTerminate:(UIApplication *)application
Parameters
application
The delegating application object.
Discussion
This method lets your application know that it is about to be
terminated and purged from memory entirely. You should use this method
to perform any final clean-up tasks for your application, such as
freeing shared resources, saving user data, and invalidating timers.
Your implementation of this method has approximately five seconds to
perform any tasks and return. If the method does not return before
time expires, the system may kill the process altogether.
For applications that do not support background execution or are
linked against iOS 3.x or earlier, this method is always called when
the user quits the application. For applications that support
background execution, this method is generally not called when the
user quits the application because the application simply moves to the
background in that case. However, this method may be called in
situations where the application is running in the background (not
suspended) and the system needs to terminate it for some reason.
After calling this method, the application also posts a
UIApplicationWillTerminateNotification notification to give interested
objects a chance to respond to the transition. Availability
Available in iOS 2.0 and later.
Reference : UIApplicationDelegate
Our iOS app retrieves new app data every time it is freshly started i.e. not resumed from the background. App data is updated periodically every couple of months via web services so this is generally fine.
However, there may be edge cases where the user's iOS device - iPad, specifically - may keep the app suspended in the background for an extended period of time - potentially indefinitely.
Is it possible to mitigate this edge case by telling iOS "please release this app if it has been suspended for more than a few hours"?
The issue you describe is due to poor app design or a poor understanding of app architecture. If you need to refresh app data whenever the app becomes active you can simply call your update function off of the UIApplicationDelegate event (or register for a notification), specifically:
applicationDidBecomeActive:
Tells the delegate that the application has become active.
- (void)applicationDidBecomeActive:(UIApplication *)application Parameters
application
The singleton application instance.
Discussion
This method is called to let your application know that it moved from
the inactive to active state. This can occur because your application
was launched by the user or the system. Applications can also return
to the active state if the user chooses to ignore an interruption
(such as an incoming phone call or SMS message) that sent the
application temporarily to the inactive state.
You should use this method to restart any tasks that were paused (or
not yet started) while the application was inactive. For example, you
could use it to restart timers or throttle up OpenGL ES frame rates.
If your application was previously in the background, you could also
use it to refresh your application’s user interface.
After calling this method, the application also posts a
UIApplicationDidBecomeActiveNotification notification to give
interested objects a chance to respond to the transition. Availability
When the app is suspended it shouldn't be refreshing. Per Apple's documentation, unless your app has registered for one of the specific background processes, the app is essentially frozen until it resumes. There shouldn't be any network calls made.
However, if you DO want to kill the app once it's been suspended for too long, you could implement a hack that registers a background timer for 10 minutes, then after 10 minutes call some garbage code that you know will crash. Problem solved :)