Repeating a task when app is in background (e.g. p

2019-07-20 19:26发布

问题:

Is there a good, Apple-approved alternative to using push notifications to trigger an app to run some code (in particular: pull new messages from a server) in a regular interval?

  • Disguising as VoIP app is not a good option (won't be approved, see iPhone: repeating background task)
  • I cannot use location updates, it should work if the user does not move around

In the simulator, using beginBackgroundTaskWithExpirationHandler with dispatch_async gives you 10 minutes of background time, and I found out that if the background "restarts itself", the backgroundTimeRemaining property always seems to be reset to 10 minutes. Here's the code.

- (void) work
{
    UIApplication *application = [UIApplication sharedApplication];

    NSLog(@"bg %@ (T-%.1f seconds)",
          [NSDate date],
          [application backgroundTimeRemaining]);
    sleep(10);

    [application endBackgroundTask:_bgTask];
    _bgTask = UIBackgroundTaskInvalid;

    [self startTask];
}

- (void)startTask
{
    UIApplication *application = [UIApplication sharedApplication];
    _bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"expired at %@", [NSDate date]);

        [application endBackgroundTask:_bgTask];
        _bgTask = UIBackgroundTaskInvalid;
    }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,
                                             0),
                   ^{ [self work]; });
}

- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    /* ... */

    [self startTask];

    /* ... */
}

Would that be an accepted practice to run something in the background? I mean, most of the time I would just sleep(...) until I want to repeat a network request or do something useful, but the app would never really enter full background mode. Does anyone have experience with this approach? Hint: Only tested on simulator without other running apps.

回答1:

You can't do this on a normal iphone. And if you found out a way to do it, Apple would eventually fix the loophole and reject your app.

I don't know about jailbroken iphones, I suspect it might be possible on them somehow.



回答2:

The Apple-approved alternatives are listed in https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

Looking at "Table 3-1 Background modes for apps", the two relevant alternatives for repeatedly getting info from a server are "Background fetch" or "remote-notification".

"remote-notification" is push notifications, which you say you don't want to use.


Therefore, "Background fetch" is the relevant choice. For example, see https://blog.newrelic.com/2016/01/13/ios9-background-execution "Downloading remote content opportunistically".

However, this does not give you (the developer) the degree of control you would have in Android. There is "setMinimumBackgroundFetchInterval", but notice that this is a MINIMUM: iOS decides when to call into your app for the next background fetch. (Apple is focused on overall battery usage and device responsiveness; once your app is in the background, design to work gracefully with however little attention it is given.)

NOTE: If the user kills your app, "Background fetch" will be killed with it. This is by design. (And is a good thing, from the user's viewpoint: most apps should stay dead if killed.)


Consider using a combination of "push notification" (remote-notification) and "Background fetch". For example, if a user permits "push notifications" by your app, then do one push daily, with a text notification to user. If they open that notification, that will open your app. Then begin the data fetching. If they hit Home button, use "Background fetch" to continue data fetching periodically through the day.

Users who are very battery-conscious may kill all their apps periodically. If they don't want to be bothered by your app today, they will ignore or delete your app's push notification. Consider this a good thing: you won't annoy users by draining battery on days that they aren't actively using your app.

Users who like to have tight control over what is running on their phone WILL NOT permit push notifications for your app. (For instance, I hate to receive daily text notifications. Don't call me, I'll call you.) In this case, once your app is killed, there is nothing that you can do (since push notification is the only way to resurrect your dead app, and I, the user, have said "No" to that). Be sure to consider how you will serve such users. (Your content may be stale when they first re-open your app.)



回答3:

I think you can use "local notifications" to trigger something to occur at a specific time.

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SchedulingandHandlingLocalNotifications.html#//apple_ref/doc/uid/TP40008194-CH5-SW1