iOS: Keep an app running like a service
Asked Answered
G

4

37

In iOS, how would I instruct the OS to keep my app running even if it is no longer in the foreground?

And many more apps does this.

Glucinum answered 15/6, 2012 at 3:2 Comment(3)
iOS only allows certain type of application to do that. What kind of application are you going to write that needs to keep running in background?Layton
Duplicate: https://mcmap.net/q/426321/-running-apps-in-backgroundLayton
If your question is voip-app related (since you mention Skype) take a look at links here: https://mcmap.net/q/426322/-how-to-handle-socket-connection-39-s-events-when-app-is-in-background But service and app eunning in the background are not exactly the same thing.Moorehead
M
64

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.

Monagan answered 15/6, 2012 at 3:5 Comment(12)
voip apps can run in background (with certain limitations) - they just have to register with OS as a voip app (and they actually have to provide voip functionality or they will not pass the approval process)Moorehead
Now if I don't need continuous location updates, but rather periodical, would it get the app rejected for using background services, or is there a different way to wake up app every 5-10 minutes or so?Baalman
@rokjarc, yes as it says in my answer: "b) an app has a background mode, the modes are: voip ...."Monagan
@Sanjeev, Skype and other apps use one of the background modes I mention.Monagan
@sekytch, yes the app would get rejected, you can't use one of the background modes just to keep running. There is NO way to wake up every 5-10 minutes unless you have a background mode, but you cannot have a background mode JUST to execute in the background. This is a limitation of iOS you just have to live with.Monagan
Hi, I make sure that what you said is absolutely right. But could you give me some page on Apple.com about that. My customer asked me evident about that. ThanksHatchway
This deserves to be revisited. Is this still the case even today? How is it that something like Google Maps not only runs and informs you of directions, and also has a bar across your screen when locked?Parsec
@Parsec yes its still the case, and regarding Maps did you read the first sentence of b)Monagan
Doesn't account for how app developers are adding the blue bar across the top, or lock screen directions. I think some turn by turn directions apps might be using geofencing.Parsec
What part of "1) An app cannot execute in the background unless: .... b) an app has a background mode, the modes are: voip, audio, location, newstand." is confusing? And what makes you think the app is displaying that blue bar and not the OS.Monagan
Qubii can do background backup, don't know how they achieve this. assets.kogan.com/files/usermanuals/QUB-MKPQ-W.pdfMoser
Fu Jiantao There is an external accessory background mode, Qubii may be using thatMonagan
H
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.

Huebner answered 5/9, 2013 at 9:0 Comment(0)
K
3

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
Kemp answered 7/5, 2019 at 10:53 Comment(0)
O
0

Yes we can do this,

Create a private field to track our status:

UIBackgroundTaskIdentifier _bgTask;

Now call this method, to run your app like a service

if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) 
{
   _bgTask = UIBackgroundTaskInvalid;
   [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(doBackground:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
}

Now create a selector-

- (void) doBackground:(NSNotification *)aNotification
{
   UIApplication *app = [UIApplication sharedApplication];

   if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)])
   {
      _bgTask = [app beginBackgroundTaskWithExpirationHandler:^
      {
         dispatch_async(dispatch_get_main_queue(), ^
         {
            if (_bgTask != UIBackgroundTaskInvalid)
            {
               [app endBackgroundTask:_bgTask];
               _bgTask = UIBackgroundTaskInvalid;
            }
         });
      }];
   }
}

When you finished your task, call below code-

UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:@selector(endBackgroundTask:)]) 
{
   if (_bgTask != UIBackgroundTaskInvalid)
   {
      [app endBackgroundTask:_bgTask];
      _bgTask = UIBackgroundTaskInvalid;
   }
}
Outplay answered 5/9, 2013 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.