Ad

IOS Background Task Not Completing

- 1 answer

I understand that background tasks can be implemented to perform certain processes that need more than the allotted 3-4 seconds applicationDidEnterBackground gives you using something like such:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{

        // Clean up your background stuff 

        NSLog(@"Finsihed background task.");
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Do something...like update Parse DB
        NSLog(@"Doing something...");
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

Unfortunately, I seem to be missing something here. While I am receiving Doing something... which is fairly obvious from the above code, I am never receiving any sort of indication that the background task expires or ever enters the expirationHandler to properly finish the task.

I am confused first off how the "do something" dispatch is even related to the background task, or is linked with the task to let the application know to allow this process to complete in the ~10 minutes the application allows for BG Tasks.

NOTE: I have ensured that I have allowed for Background Modes in my info.plist, as many posted solutions point out may be the problem for others.

If someone can help me properly allow a background task to be completed I would most appreciative.

FYI: The main purpose of this is to update my Parse database with a query to set the users new (local to the device) values to the DB. This is what I would be using in case I am not being clear enough:

// Do something...like update Parse DB
NSLog(@"Doing something...");
. . .
PFQuery *query = [PFQuery queryWithClassName:@"_User"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:<SOME_OBJECT_ID> block:^(PFObject *userInfo, NSError *error) {
    // Save user info to current user info on Parse
    [userInfo saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"Could not update Parse with error: %@", error);
        }
        else {
            NSLog(@"Updated Parse successfully.");
        }
   }];
}];

UPDATE: I have tried to call [application endBackgroundTask:bgTask]; within the Parse block but it does not get called. A few times it did assuming it was quickest both of those times, but I am trying to handle situations (like all but those 2) where updating Parse successfully will take longer than the closing the application. Here is what I did:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        NSLog(@"Good for you.");
    }

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    self.backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId];
        NSLog(@"done");
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Do something...like update Parse DB
        NSLog(@"Doing something...");

        PFQuery *query = [PFQuery queryWithClassName:@"_User"];
        // Retrieve the object by id
        [query getObjectInBackgroundWithId:<SOME_OBJECT_ID> block:^(PFObject *userInfo, NSError *error) {
            // Save user info to current user info on Parse
            userInfo[@"test"] = [NSNumber numberWithInteger:12345];

            [userInfo saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
                if (error != nil) {
                    NSLog(@"Could not update Parse with error: %@", error);
                    [application endBackgroundTask:self.backgroundTaskId];
                    self.backgroundTaskId = UIBackgroundTaskInvalid;
                }
                else {
                    NSLog(@"Updated Parse successfully.");
                    [application endBackgroundTask:self.backgroundTaskId];
                    self.backgroundTaskId = UIBackgroundTaskInvalid;
                }
            }];
        }];
    });
}
Ad

Answer

You have the right structure generally, except that you are calling [application endBackgroundTask:self.backgroundTaskId]; from outside your dispatch_async block, so the background task will be ended before the Parse operations will complete. Also, if the user your are updating is the currently logged in user, you can use PFUser.currentUser rather than having to search.

So, your applicationDidEnterBackground should be something like:

- (void)applicationDidEnterBackground:(UIApplication *)application {

    self.bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{

        // Clean up your background stuff

        NSLog(@"Expired background task.");
        [application endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];

    // Begin your background stuff...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSLog(@"Doing something...");

        // Do something...like update Parse DB
        PFUser *user=[PFUser currentUser];
        // Update User fields as required
        [user saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
            if (succeeded) {
                NSLog(@"Saved user");
            } else {
                NSLog(@"Error:%@",error.localizedDescription);
            }
            NSLog(@"Background time remaining=%f",UIApplication.sharedApplication.backgroundTimeRemaining);
            [application endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }];


    });
}
Ad
source: stackoverflow.com
Ad