You cannot make NSTimer work NSTimer this while your application is in the background. NSTimer are not real time mechanisms. From the official documentation:
Timers work in conjunction with trigger cycles. To use the timer effectively, you need to know how run loops work - see NSRunLoop and Thread Programming Guide . Note, in particular, that run loops support strong references to their timers, so you do not need to maintain your own strong reference to a timer after you add it to the run loop.
A timer is not a real-time mechanism; it only fires when one of the run cycle modes to which the timer has been added is running, and it can check whether the timers have elapsed. Due to various input sources, a typical trigger cycle is controlled, the effective resolution of the time interval for the timer is limited to about 50-100 milliseconds. If the response time of the timers occurs during a long callout or while the execution cycle is in a mode that the timer does not control, the timer does not work until the next timer check cycle . Therefore, the actual time during which the timer is triggered potentially can be a significant period of time after the planned firing time.
Emphasize mine.
An important departure from this is that while your application is in the background, any startup cycle that your timer was scheduled for does not work actively.
As soon as your application returns to the forefront, this launch cycle will start, see that your timer has expired, and send a message to the selector.
In iOS 7 and forward, if you want to perform operations in the background, you can tell the OS that you want to perform “background selections”.
To establish this, we must first tell the OS how often we want to receive data, so in didFinishLaunching... add the following method:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) return true }
Here we can go through any period of time (for example, if we want to check only once a day). The value we pass defines only the minimum time that must elapse between checks. Unable to tell OS maximum time between checks.
Now we have to implement a method that is actually called when the OS gives us the opportunity to do background work:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // do background work }
We can do whatever we want in this method. However, there are two catches.
- This method is called when our application is in the background. The OS limits us (I think) thirty seconds. Thirty seconds later, our time is over.
- We should call
completionHandler() (or the OS will think that we have used all our time).
The passed completionHandler accepts an enumeration, UIBackgroundFetchResult . We must pass it either .Failed , .NewData , or .NoData , depending on our actual results (this approach is usually used to check the server for fresh data).
So, our method might look like this:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // do stuff if let _ = error { completionHandler(.Failed) } else if results.count > 0 { completionHandler(.NewData) } else { completionHandler(.NoData) } }
Keep in mind that we absolutely have no control over how often the OS actually allows us to run this code in the background. The OS uses several indicators to optimize the user experience.
I think that if your application reports .Failed the completion handler, the OS may soon give you a second chance, however, if you abuse .Failed , the OS may possibly cross out your application from the use of background settings (and Apple may reject your application).
If your application does not report .NewData , the OS will allow your application to do the job less often. I am not talking about this because I recommend that you simply report .NewData . You must accurately report this. The OS is very smart at planning work. If you transfer .NewData when there is no new data, the OS will allow your application to run more often than it may be necessary, which drains the user's battery faster (and can lead to the application completely deleting your application).
There are other indicators when your application is working on background work. OS is very unlikely that any application will run in the background while the user is actively using his device, and it is more likely that applications will run in the background while the user is not using his device. In addition, the OS is more likely to do background work while it is on WiFi, and while it is connected to a charger.
The OS will also consider how regularly a user uses your application or when he regularly uses it. If a user uses your application every day at 18:00 and never at any other time, most likely your application will always be able to do background work from 17:30 to 18:00 (just before the user uses the application) and never for any other part of the day. If a user rarely uses your application, it could be days, weeks, or months between opportunities to work in the background.