I'm currently working on a simple app that displays data received over the network in a watchOS complication. Notably, this data is only relevant for ~30 minutes before a new network fetch is required.
I'd like to have the complication be up to date when the user unlocks their watch in the morning (this is a common use case presented by Apple).
Is it possible to receive some kind of background task when the user unlocks their watch? If I schedule a background task and the watch is locked and charging when the refresh happens, will the background task still fire? What techniques can I use to have data ready for the user when they wake up and unlock their watch? Is there documentation specifically focusing on background tasks when the watch is locked?
As far as I know, the watch works in its locked state only slightly differently from its unlock state.
One difference is the display of complications:
You can specify the privacy behaviour, i.e. what the clock face displays as complication (you can select what is displayed on the lock screen).
So, to my mind, it is possible to run background tasks as scheduled when the watch is locked and charging. Thus the data will automatically be ready when the watch is unlocked.
For this reason, there is no special documentation what happens when the watch is locked, except for some special cases, as what is displayed on the watch face in the locked state.
I would use the app life cycle docs here, and quite possibly choose:
in applicationDidEnterBackground(), I'd set a flag (time when the
complication was last updated). I'd suggest you use a singleton, so that it's accessible anywhere in your app.
then in applicationDidBecomeActive() i'd pick up the flag, compare it
with the current time, and notify the active ViewController to
refresh its data, if it's greater than 30 minutes.
if the flag doesn't exist, because the app was terminated, or it's a
first launch, then refresh anyway (set a 24h date in the past, to
use the same logic as in 1/,2/)
if you want to make it more permanent, use NSDefaults to store the last time the complication was updated.
Related
I've looked around at answers to questions regarding executing core data saves in the background, but none that I've found have directly addressed the following scenario:
I've set up core data so that when my app launches for the first time, core data is populated from a plist and then saved to the persistent store (all on a background thread). During this time, the app displays a "please wait" popup with an animating activity indicator. This seems to work nicely and generally the app will finish the population even if the user hits the home button during the population, pushing the app to the background. However, it is possible that the app could be completely terminated before this population finishes (I've successfully done this by hitting home, double-tapping home, and removing the app from the multi-tasking tray really really fast). In this scenario, the app quits without saving the data to the store. On the next launch of the app, the app recognizes that the store already exists, so it doesn't populate, thus leaving the user with a completely empty database. So the question(s) is/are:
Is there a way to completely remove the store on app termination if the population hasn't finished? I attempted to do this in -applicationWillTerminate: which didn't seem to properly recognize if the app was in the middle of populating.
Or, is there a better way to recognize when the store requires population? E.g., can I determine if the store is empty upon launch?
I would suggest this general approach to your problem:
at the end of the population phase, write a flag to your core data base or to your app defaults (NSUserDefaults);
(now, when the app is terminated before finishing the population phase, the flag will not be stored);
at startup, check for that flag; if it is there, you know that the population phase completed, otherwise you know something went wrong.
Of course, there is a slight chance that the app is terminated just after the population phase is done and before the flag is stored; aside from this event to be pretty unlikely, in any case, if that happened, it would only force your app to populate the data once again at the next launch, but will not be cause of any data consistency issue.
A more specific approach is base on the use of beginBackgroundTaskWithExpirationHandler:
Your app delegate’s applicationDidEnterBackground: method has approximately 5 seconds to finish any tasks and return. In practice, this method should return as quickly as possible. If the method does not return before time runs out, your app is killed and purged from memory. If you still need more time to perform tasks, call the beginBackgroundTaskWithExpirationHandler: method to request background execution time and then start any long-running tasks in a secondary thread. Regardless of whether you start any background tasks, the applicationDidEnterBackground: method must still exit within 5 seconds.
This will allow you to get more time to complete the population phase before the app is terminated in any "non-pathological" case (i.e., it will not work if the user kills the app like you did in your test, and will not be safe in case something goes wrong in the app itself and it crashes while the background thread is doing the population).
Hope this helps.
Is it possible to call method after n minutes even if application will go to background?
What i should use for this?
You can fire a local notification after a set amount of time. But then you can only handle an event in case the user taps "View" on the dialog. Otherwise you can use one of the background modes (Location, Audio, VoIP) to continue running in the background. Then you have all the freedom you wish except that you probably won't get your app accepted in the app store unless you use your app for the purpose that you describe in your background mode.
I recently got an app rejected twice for polling every n minutes using location background mode (even though I was using the location!).
Good luck
Is it possible to keep a socket connection alive in background, to be able to push new data and alert users at all times?
The answer to this question is a definitive yes. If you are in the background state, then you can keep a connection open and process messages from a server.
Unfortunately the complexity here is that you don't have a lot of control over the state your application is in:
foreground - The user has tapped your icon and the app is running with the UI visible.
suspended - The user was previously running your app in the foreground, but suspended it by hitting the home button or receiving a call. Basically your app is 'freeze dried' and will remain inactive until it is resumed by the user (starting where it left off) or it is terminated by the OS (see below).
background - The app was previously running in the foreground but has moved to the background state as a result of something the user has done. Normally your app will move to the suspended state in this case, but there are things you can do as the developer to prevent the instant 'freeze dry' and go into the background instead (see below). Your app will also be in the background state if it is woken up for a significant change event.
terminated - Your app has been unloaded from memory and the next time it starts will be from scratch. This is what happens when you double click the home button and then tap the x next to your app icon. It moves the app from the suspended state into the terminated state. This will also happen if the OS decides it needs room for more recently running apps and your app has been suspended for a long time.
So obviously the trick here is how do I stay in the background state as a long as possible. There are a couple of ways to do this:
Beg for more time - You can get up to 10 minutes of additional background processing when your app is closed if you ask for it.
Use UIBackgroundMode - You can declare youself a voip, audio or location app by adding the corresponding UIBackgroundMode value to the pList. There are special requirements for these types of apps which you can check out here.
So these approaches are not without their own problems (getting approved on the store being one of them) and as such I tend to agree with the other answers that using push notifications is probably your best approach for notifying your users. With the notification improvements in iOS5 this is going to be the best user experience going forward.
You can keep a socket connection alive (or do whatever else you want) in the background for about 15 minutes after your app closes. There are also more specialized background processing modes (specifically, audio, voip, and location) which Apple supports if your app fits into one of their supported categories. See here.
If you want to keep sending the user notifications indefinitely, you want to use the Apple Push Notification Service. This allows your app to continue to receive notifications when it's not running, and it conserves resources since there's only one connection to the APN service at a time.
You can definitely alert users with local and push notifications.
And as far as I know, you can keep a connection open only for limited time.
Look here for more details.
I am writing an application that records a users position at regular intervals whilst walking. I am using an NSTimers to schedule "startUpdatingLocation" followed by calling "stopUpdatingLocation" shortly afterwards to save as much battery as possible.
I want the user to be able to start the application and lock the phone thereby putting the application in an inactive state. My question is when this happens my application (when running via Xcode) seems to continue as normal, but I am curious if there are any differences in the way the application runs in this state as apposed to when the application is running as active?
From the docs it only mentions "applicationWillResignActive" with regards to the application passing through that state on its way to the background. I am more interested in how an application behaves when a used locks the UI and puts the phone away, I just want to make sure its going to keep doing what it should be doing or do I need to take extra measures?
When you app hits applicationWillResignActive it can continue to receive location updates if you use the UIBackgroundModes option in your apps info.plist.
Add the location key.
It is important that you do not use a timer in the background as timers will run but hold their "fire" until the app becomes active again. Thus you will only get one read when the user comes back to the app. The GPS location accuracy level is what will drive how much the battery is affected.
In your app description in the App Store, Apples requires:
Continued use of GPS running in the
background can dramatically decrease
battery life.
You app will not be allowed to go live until the above text is in the description for the user to read.
You should definitely be testing this on a device. The simulator doesn't auto-lock, for instance.
applicationWillResignActive is called when the user presses the home button, when another app fires a notification the user accepts (including when a call comes in), and when the device locks.
Look into the multitasking documentation, and what it has to say about background location updates.
My suggestion is that you conserve the user's battery by:
- starting a timer, which when fired, starts location updates (and stops the timer)
- when you get an adequately-accurate position record, stop location updates and restart the timer
- if location updates fail, restart the timer for a longer period. Maybe they're underground.
The most efficient approach is using significant location update service while your application is in the background or the screen is locked on your app. You might get one update every ten minutes or so.
Remember also, the user can disable your app's location services permissions. Especially these days...
On devices which support multitasking, I'd like my iOS app to run a function in the 'background' when the app is not running (i.e. it's suspended). I know that iOS supports running tasks in the background, but I'm not sure how to make the function recurring (and only when the app is not in the background). What's the best approach?
I'm not interested in running a long-term function in the background but a short-term function to simply update the application badge #. However this number is dynamically based on the app's data, and needs to run a query against core data.
To further clarify, yes, my core data will not be changing, but the badge represents a number of items due. As time progresses, more items will be due, so I want to update the badge to show the proper items due as time progresses. So if 5 items are due now, but half an hour later 3 more items are due, then by the time the next hour comes around, 8 items will be due even though the core data has not changed at all in and of itself.
It can't, the only task allowed to run in background are: audio, voip and location.
Why do you need to update the badge data every hour if the data stored within Core Data is not changing? ie. the app isn't running?
You can do this using push notifications, like previously posted, or you can use a scheduled local notification based on the data when the app is closed or backgrounded. I think those are pretty well your only options.