Background task stopped running when screen is off

Hi,


I have an application which is running properly in the background before 10.3.3.


Since iOS 10.3.3, whenever the app goes to background AND the screen is turned off AND it is not hooked up with the charging cable, the background task is stopped running.


I started the background task when the app goes to background and do some background handling by using:


if (self.taskId == UIBackgroundTaskInvalid)

{

_isSuspended = NO;

UIApplication *app = [UIApplication sharedApplication];

self.taskId = [app beginBackgroundTaskWithExpirationHandler:^

{

[self stopAllTimer];

[app endBackgroundTask:self.taskId];

self.taskId = UIBackgroundTaskInvalid;

_isSuspended = YES;

}];

}


I have put a log to check how much time is left for the background task like this:


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSLog(@"Background task started");

while (true) {

NSLog(@"background time remaining: %8.2f", [UIApplication sharedApplication].backgroundTimeRemaining);

[NSThread sleepForTimeInterval:1];

}

});


If I run the app in the backgrpound in a physical phone with screen on or if the phone is hooked up with a charging cable, it is working fine and I can see the background time remaining log statement every second for the whole 180s background time period.


But as long as when the phone is not hooked up with the charging cable and the screen is locked, I can't see the background time remaining log statement. As the phone is not hooked up with the cable, I am using the Profile and Log (https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=ios) to check the sysdiagnose log. And the log is like:


2017-10-24 15:46:49.078108 xxxxxxxxx background time remaining: 177.95

.....

2017-10-24 15:46:50.289287 assertiond System will sleep.

.....

2017-10-24 15:46:50.293813 xxxxxxxxx background time remaining: 176.74


2017-10-24 15:47:47.386712 locationd system will power on

2017-10-24 15:47:47.386716 locationd system exiting sleep

........

2017-10-24 15:47:47.636843 xxxxxxxxx background time remaining: 175.64


I run the app and put it to background and leave it running for a while and then turn off the screen at 15:46:50. And from the log, you can tell the background task is stopped since 15:46:50.293813. It is because I have got a local notification and it turns on the screen so it "wakes" up the background task at 15:47:47.636843.


From the log, it seems like iOS has stopped the background task running when the screen is locked and resume the task when the screen is on again.


As my app is required to track a location at every minute in the background, this behaviour cause my app not working since 10.3.3.


Does anyone of you know how to solve this issue?


Thanks,


Kenneth

Have you tried reproducing this outside of your main application? My recommendation is that you pull your background task code out into a simple test app so that you can verify the problem occurs independently of everything else in your app.

What hardware are you testing this on?

Do you see the same problems on iOS 11?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,


I have tried creating a simple app and only do the background task testing and it is reproduced every time in both iOS 10.3.3 and iOS 11.


For iOS 10.3.3, I am testing by using iphone 5C and iphone 6. For iOS 11, I am testing by using iphone 5S.


They all behaves the same. When I hook up the phone with power or turn on the screen, they are working properly.


Thanks,


Kenneth

Thanks for the info. Based on our conversation so far I sat down to try to reproduce the problem you’re seeing. Here’s what I did specifically:

  1. I created a test app using the code pasted in below.

  2. I built the app with Xcode 9.1 and ran it on an iPhone SE running iOS 11.0.3.

  3. I hit stop in Xcode.

  4. I disconnected the USB cable.

  5. I ran the app from the Home screen.

  6. I tapped the Start row in the table view.

  7. I screen locked the device.

  8. I left it for about a minute.

  9. I took a sysdiagnose using the instructions on the Bug Reporting > Profiles and Logs page.

  10. After syncing the sysdiagnose over to my Mac, I unpacked it and opened

    system_logs.logarchive
    in the Console utility.
  11. I searched for “qqq”.

Here’s what I saw:

… 09:06:06.828303 … QQQ will start
… 09:06:16.829714 … QQQ ping
… 09:06:26.918622 … QQQ ping
… 09:06:36.921969 … QQQ ping
… 09:06:46.859642 … QQQ ping
… 09:06:56.843140 … QQQ ping
… 09:07:06.889711 … QQQ ping
… 09:07:16.849831 … QQQ ping
… 09:07:26.849747 … QQQ ping
… 09:07:36.859260 … QQQ ping
… 09:07:46.926023 … QQQ ping
… 09:07:57.078681 … QQQ ping
… 09:08:06.880002 … QQQ ping

As you can see, I got ‘ping’ logs every 10 seconds even while the iPhone was screen locked.

I’m not sure what’s going on with your test. My recommendation is that you run exactly the steps above (well, as near as possible given the hardware you have available) and let us know what you see.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
#import "MainViewController.h"

@interface MainViewController ()

@property (nonatomic, assign, readwrite) UIBackgroundTaskIdentifier task;
@property (nonatomic, strong, readwrite) NSTimer *                  timer;

@end

@implementation MainViewController

- (void)awakeFromNib {
    [super awakeFromNib];
    self.task = UIBackgroundTaskInvalid;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    #pragma unused(tableView)
    #pragma unused(indexPath)
    if (self.task == UIBackgroundTaskInvalid) {
        NSLog(@"QQQ will start");
        [self start];
    } else {
       [self stop];
       NSLog(@"QQQ did stop");
    }

    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (void)start {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(didFireTimer:) userInfo:nil repeats:true];
    self.task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
       [self stop];
       NSLog(@"QQQ did expire");
    }];
    assert(self.task != UIBackgroundTaskInvalid);
}

- (void)didFireTimer:(NSTimer *)timer {
    #pragma unused(timer)
    NSLog(@"QQQ ping");
}

- (void)stop {
   [self.timer invalidate];
   self.timer = nil;
   [[UIApplication sharedApplication] endBackgroundTask:self.task];
   self.task = UIBackgroundTaskInvalid;
}

@end

Hi Eskimo,


Thanks for you testing and info. Yes you are right, if I run the sample code you have attached, the background task is working properly.


But as my app is required to track location at every minute in the background, so my simple testing also include the location update in the background task and I believe this cause the issue. If I have added the location update in your attached sample code. I can repriduce the issue every time. I am following the same steps as you mentioned in your post and I see this from the log:


2017-11-02 17:46:51.060335 ...... QQQQQ will start
2017-11-02 17:46:52.061665 ...... QQQ ping
2017-11-02 17:46:53.061726 ...... QQQ ping
2017-11-02 17:46:54.061970 ...... QQQ ping
2017-11-02 17:46:55.157650 ...... QQQ ping
2017-11-02 17:46:56.124572 ...... QQQ ping
2017-11-02 17:46:57.153343 ...... QQQ ping
2017-11-02 17:46:58.095444 ...... QQQ ping
2017-11-02 17:46:59.149048 ...... QQQ ping
2017-11-02 17:47:00.192524 ...... QQQ ping
2017-11-02 17:47:01.124783 ...... QQQ ping
2017-11-02 17:47:02.072186 ...... QQQ ping
2017-11-02 17:47:03.081260 ...... QQQ ping
2017-11-02 17:47:04.068874 ...... QQQ ping
2017-11-02 17:47:05.142025 ...... QQQ ping
2017-11-02 17:47:06.126055 ...... QQQ ping
2017-11-02 17:47:07.094092 ...... QQQ ping
2017-11-02 17:50:23.743124 ...... QQQ ping
2017-11-02 17:50:23.928219 ...... QQQ ping
2017-11-02 17:50:24.884800 ...... QQQ ping
2017-11-02 17:50:25.843891 ...... QQQ ping
2017-11-02 17:50:26.878474 ...... QQQ ping
2017-11-02 17:50:27.885854 ...... QQQ ping
2017-11-02 17:50:28.934817 ...... QQQ ping
2017-11-02 17:50:29.872281 ...... QQQ ping
2017-11-02 17:50:31.221556 ...... QQQ ping


At 17:46:53 the app goes to background and I have core location manager running in the background for 10s. After a while, the background task stop at 17:47:07. From the testing, I guess it is related to the location manager that was running in the background. But it was working in the previous iOS version.


For my testing app, I have turned on the background location update capabilities. Here is my testing code:


#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, assign, readwrite) UIBackgroundTaskIdentifier task;
@property (nonatomic, strong, readwrite) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    myArray = [[NSMutableArray alloc]initWithObjects: @"abc",@"efg", nil];
  
    /
    _mainLocationManager = [CLLocationManager new];
    _mainLocationManager.pausesLocationUpdatesAutomatically = NO;
    _mainLocationManager.activityType = CLActivityTypeOther;
    _mainLocationManager.allowsBackgroundLocationUpdates = YES;
    [_mainLocationManager requestAlwaysAuthorization];
    _mainLocationManager.delegate = self;
    _mainLocationManager.desiredAccuracy = kCLLocationAccuracyBest;
    _mainLocationManager.distanceFilter = kCLDistanceFilterNone;
    [_mainLocationManager startUpdatingLocation];
  
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
- (void) awakeFromNib
{
    [super awakeFromNib];
    self.task = UIBackgroundTaskInvalid;
}
-(void)applicationEnterBackground{
  
    NSLog(@"######### APP goes to background!!!!");
  
    [self performSelector:@selector(stopLocationManager) withObject:nil afterDelay:10];
}
#pragma mark - Table View Data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section{
    return [myArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath{
    static NSString *cellId = @"SimpleTableId";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellId];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle: UITableViewCellStyleDefault reuseIdentifier:cellId];
    }
    NSString *stringForCell;
    stringForCell= [myArray objectAtIndex:indexPath.row];
    [cell.textLabel setText:stringForCell];
    return cell;
}
/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}
#pragma mark - TableView delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
  
    if (self.task == UIBackgroundTaskInvalid)
    {
        NSLog(@"QQQQQ will start");
        [self startTimer];
      
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"### Background task started");
          
            while (true) {
                NSLog(@"### background time remaining: %8.2f", [UIApplication sharedApplication].backgroundTimeRemaining);
                [NSThread sleepForTimeInterval:1];
            }
          
        });
    }
    else
    {
        [self stopTimer];
        NSLog(@"QQQQQ did stop");
    }
  
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void) startTimer
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(didFireTimer:) userInfo:nil repeats:true];
    self.task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self stopTimer];
        NSLog(@"QQQ did expire");
    }];
    assert(self.task != UIBackgroundTaskInvalid);
}
- (void) stopTimer
{
    [self.timer invalidate];
    self.timer = nil;
    [[UIApplication sharedApplication] endBackgroundTask:self.task];
    self.task = UIBackgroundTaskInvalid;
}
- (void)didFireTimer:(NSTimer *)timer {
    #pragma unused(timer)
    NSLog(@"QQQ ping");
}
- (void) stopLocationManager
{
    [_mainLocationManager stopUpdatingLocation];
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    CLLocation *newLocation = [locations lastObject];
  
    NSLog(@"+++++++++++ New Location lat: %f, lng: %f at timestamp %f", newLocation.coordinate.latitude, newLocation.coordinate.longitude, [newLocation.timestamp timeIntervalSince1970]);
}
@end



Thank you very much for your help!


Kenneth

But as my app is required to track location at every minute in the background … If I have added the location update in your attached sample code. I can repriduce the issue every time.

Well, that’s weird. Alas, location tracking is largely outside of my area of expertise. I recommend that you open a DTS tech support incident and talk this over with DTS’s location specialist.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello Kenneth,


I'm having the same issue with my app. Have you been able to resolve the issue?


Thanks,

Joseph.

Background task stopped running when screen is off
 
 
Q