iOS: Keep an app running like a service iOS: Keep an app running like a service ios ios

iOS: Keep an app running like a service


Basically, there is no such thing as a service type app or functionality in iOS. Even the "background" apps (UIBackgroundMode) cannot run entirely free and without restrictions like a service or daemon etc. on other OSs can.

Here's the situation regarding background execution and notifications and timers etc.

1) An app cannot execute in the background unless:

a) it requests extra time from the OS to do so. This is done using beginBackgroundTaskWithExpirationHandler. It is not specified (intentionally) by Apple how long this extra time is, however in practice it is around 10 minutes.

b) an app has a background mode, the modes are VoIP, audio, location, newsstand. Even if it has one of these types an app cannot execute without restrictions. The rest of this discussion assumes the app does not have a background mode. If you try to use one of these background modes to enable your app to be capable of running in the background but your app does not make legitimate use of the specific functionality then your app will be rejected upon app store submission (i.e. to have a UIBackgroundMode it MUST be either: a VoIP app, NEED to have continual location updates, the ability to play audio in the background continuously is a fundamental feature, or be a newsstand app).

2) When an app is suspended it cannot do ANYTHING to rouse itself directly. It cannot previously have scheduled an NSTimer, it cannot make use of something like performSelector:afterDelay. etc.

The ONLY way the app can become active again is if the USER does something to make it active. The user can do this from via of the following:

a) Launch the app directly from its icon

b) Launch the app in response to a local notification that was previously scheduled by the app while it was active.

c) Launch the app in response to a remote notification sent by a server.

d) A few others: such as URL launching if the app is registered to deal with launching via a URL; or if it's registered to be capable of dealing with a certain type of content.

If an app is in the foreground when a local/remote notification fires then the app receives it directly.

If the app is not currently in the foreground when a local/remote notification fires then the app DOES NOT receive it. There is no code that is executed when the notification fires!

Only IF the user selects the notification will the app become active and it can execute.

Note that the user can disable notifications, either for the entire device or just for a specific application, in which case the user will never see them. If the device is turned off when a notification is due to fire then it is lost.

UPDATE FOR IOS 7

1) there are some new background modes such as background fetch (you still can't be roused by the OS in a deterministic fashion however)

2) there's now a background push notification

3) beginBackgroundTaskWithExpirationHandler time has reduced from 10 minutes to about 3.


Things changed now in iOS7, it is beta now.

From Apple's developer portal:

Keep the content of your app up-to-date by adopting the new multitasking APIs in iOS 7. The new services allow your app to update information and download content in the background without draining the battery unnecessarily. The updates can happen at opportunistic times and are intelligently scheduled according to usage, so your app can update content in the background just when your users need it.


Updated to Swift 5:

Maybe not the right answer to the OP. But this will help you start a background task whenever the app goes into the background. And gets itself killed by iOS after sometime (Hopefully after the processing is finished).

Create an extension

extension Notification.Name {  @discardableResult  func observe(using: @escaping (Notification) -> Void) -> NSObjectProtocol {    return NotificationCenter.default.addObserver(forName: self,                                                  object: nil,                                                  queue: OperationQueue.main,                                                  using: using)  }}

Declare a variable

fileprivate let backgroundTaskStarter = {  // TODO: If this doesn't work, switch to willResignActiveNotification.  UIApplication.didEnterBackgroundNotification.observe() { _ in    UIApplication.shared.beginBackgroundTask {      print("endBackgroundTask called by iOS")    }  }}()

And in Filename.swift's init() method, do:

_ = backgroundTaskStarter