13

I've got a question with objective-c and background task request.

Restricted to background modes with ios 13.

My App does not run in the background more than 30 seconds.

Background modes changed with ios 13.

I need to register a background task with objective-c like this:

BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.SO.apprefresh", using: nil) { task in
    self.scheduleLocalNotification()
    self.handleAppRefreshTask(task: task as! BGAppRefreshTask)
}

and I need schedule when app goes to background

func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.SO.apprefresh")
    request.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60) // App Refresh after 2 minute.
    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule app refresh: \(error)")
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Hakan Turkmen
  • 119
  • 1
  • 6

3 Answers3

7

There is hardly any complete example for a BGProcessingTaskRequest in Objective-C. This is my implementation, which leans on the other answers in this question.

The following steps are needed.

Add the BackgroundTasks.framework to you app, under Build Phases - Link Binary With Libraries

Enable Background processing in Signing & Capabilities - Background Modes. I enabled Background fetch & Remote notifications as well.

Edit your Info.plist. Enter a new row 'Permitted background task scheduler identifiers'. Add items for that row with the identifiers of your background tasks. In my case: com.toolsforresearch.compress and a second item com.toolsforresearch.upload.

#import <BackgroundTasks/BackgroundTasks.h>

in your AppDelegate.m

Add this to willFinishLaunchingWithOptions in AppDelegate.m:

if (@available(iOS 13.0, *)) {
    NSLog(@"configureProcessingTask");
    [self configureProcessingTask];
}

Add this to applicationDidEnterBackground in your AppDelegate.m

if (@available(iOS 13.0, *)) {
    NSLog(@"scheduleProcessingTask");
    [self scheduleProcessingTask];
}

Finally, add this to your AppDelegate.m, before the @end

static NSString* uploadTask = @"com.toolsforresearch.upload";

-(void)configureProcessingTask {
    if (@available(iOS 13.0, *)) {
        [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:uploadTask
                                                              usingQueue:nil
                                                           launchHandler:^(BGTask *task) {
            [self scheduleLocalNotifications];
            [self handleProcessingTask:task];
        }];
    } else {
        // No fallback
    }
}

-(void)scheduleLocalNotifications {
    //do things
}

-(void)handleProcessingTask:(BGTask *)task API_AVAILABLE(ios(13.0)){
    //do things with task
}

-(void)scheduleProcessingTask {
    if (@available(iOS 13.0, *)) {
        NSError *error = NULL;
        // cancel existing task (if any)
        [BGTaskScheduler.sharedScheduler cancelTaskRequestWithIdentifier:uploadTask];
        // new task
        BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:uploadTask];
        request.requiresNetworkConnectivity = YES;
        request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:5];
        BOOL success = [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
        if (!success) {
            // Errorcodes https://stackoverflow.com/a/58224050/872051
            NSLog(@"Failed to submit request: %@", error);
        } else {
            NSLog(@"Success submit request %@", request);
        }
    } else {
        // No fallback
    }
}

When I run my app and push it to the background these messages are NSLogged:

2020-05-07 19:12:45.179834+0200 SessionVideo[819:171719] scheduleProcessingTask
2020-05-07 19:12:45.219117+0200 SessionVideo[819:171719] Success submit request <BGProcessingTaskRequest: com.toolsforresearch.upload, earliestBeginDate: 2020-05-07 17:12:50 +0000, requiresExternalPower=0, requiresNetworkConnectivity=1>

I did no processing yet in the BGProcessingTask, but the fact that the request submits fine is a good starting point. Comments are more than welcome, for instance how to add a completion handler to the background task.

Jan Ehrhardt
  • 395
  • 1
  • 8
  • 20
  • 1
    At the end of "handleProcessingTask:" don't forget to add the call to setTaskCompletedWithSuccess: on BGTask otherwise framework logs warning "dealloc'd without completing" – gheni4 Jul 11 '22 at 16:05
3

The register function looks like this.

static NSString* TaskID = @"com.SO.apprefresh";

-(void)configure {
    [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:TaskID
                                                          usingQueue:nil
                                                       launchHandler:^(BGTask *task) {
        [self scheduleLocalNotifications];
        [self handleAppRefreshTask:task];
    }];
}

-(void)scheduleLocalNotifications {
    //do things
}

-(void)handleAppRefreshTask:(BGTask *)task {
    //do things with task
}

The swift closure signature converts from:

{ task in
 //....
}

to an Objective-C block signature:

^(BGTask *task) {
  //...
}

And the other function looks like this.

-(void)scheduleAppRefresh {
    BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:TaskID];
    request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:2*60];
    NSError *error = NULL;
    BOOL success = [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
    if (!success) {
        NSLog(@"Failed to submit request: %@",error);
    }
}

Any throwing function in Swift

func submit(_ taskRequest: BGTaskRequest) throws

Will always convert to a function that returns BOOL and passes an error by reference.

- (BOOL)submitTaskRequest:(BGTaskRequest *)taskRequest error:(NSError **)error

Warren Burton
  • 17,451
  • 3
  • 53
  • 73
3

Also don't forget to whitelist your Task Id in info.plist file under Permitted background task scheduler identifiers (BGTaskSchedulerPermittedIdentifiers)

Grisu
  • 51
  • 2
  • 1
    can you elaborate this – Abhishek Jan 31 '20 at 08:04
  • Sure, I found it here: https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler "The system runs only tasks registered with identifiers on a whitelist of task identifiers. To add the whitelist, add the identifiers to the Info.plist file" If I remember correctly I had problems with running background tasks on iOS13 before I added task ID to BGTaskSchedulerPermittedIdentifiers. – Grisu Feb 01 '20 at 09:42