IOS background fetch time glitch

I set up a background selection using the schema from NSScreencast episode # 92 .

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
  ...
  [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
  ...
}

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
{
    if ([[MyAppAPIClient manager] reachable] && [[Session manager] userSignedIn])
    {
        [[[Session manager] user] fetchBackgroundWithSuccess:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
            completionHandler(UIBackgroundFetchResultNewData);
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            completionHandler(UIBackgroundFetchResultFailed);
        }];
    }
    else
        completionHandler(UIBackgroundFetchResultNoData);
}

      

For the method, fetchBackgroundWithSuccess:failure

I am using AFNetworking NSURLSessionDataTask

.

Unfortunately, sometimes I get the error

MyApp[3344] has active assertions beyond permitted time:
{(
    <BKProcessAssertion: 0x178279c00> identifier: Background Content Fetching (1596) process: MyApp[3344] permittedBackgroundDuration: 30.000000 reason: backgroundContentFetching owner pid:16 preventSuspend      preventThrottleDownUI  preventIdleSleep  preventSuspendOnSleep 
)}

      

and it shuts down my app completely, logs out my user and all user data is cleared.

Not sure how to fix this, but any direction would be much appreciated.

UPDATE:

The way I store data is using NSKeyedArchiver archiveRootObject:toFile

and unarchiveObjectWithFile

. It keeps track of if a user has signed up, their user_id and other important information. All of this is cached in memory using a singleton object of the class User

.

archiveRootObject:toFile

called during

  • applicationWillResignActive

  • applicationDidEnterBackground

  • applicationWillTerminate

  • applicationDidReceiveMemoryWarning

unarchiveObjectWithFile

called as needed if singleton is zero.

So by logging out and cleaning up the cleanup, the singleton is zero and unarchiveObjectWithFile

won't be able to get any data.

+3


source to share


2 answers


Each background fetch has a time limit that I think is 30 seconds. Unlike a normal background task, which can use an expiration handler to automatically clean up and do whatever you need to do when you run out of time, background fetching does not currently have this ability. One way you can handle this is perhaps by starting the NSTimer, which should take about 20-25 seconds into your background selection, and this timer will call a function that processes if you haven't finished the background job yet. to stop whatever you "do and clear everything so the app can return to its normal background. From the docs:

When this method is called, your application has up to 30 seconds of wall time to perform the load operation and call the specified completion handler block. In practice, your application should block a completion handler as soon as possible after loading the required data. If you don't call the completion handler in time, your application is complete.



https://developer.apple.com/library/ios/documentation/uikit/reference/uiapplicationdelegate_protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler :

The bold part sounds like what happens. Either way, you need to do what you are trying to do in 30 seconds, otherwise you need to cancel it or your application will be terminated.

+2


source


This question is quite old, but perhaps the following code can help some developers interested in a code snippet based on Mike's answer. As Mike suggested, I also use NSTimer to cancel background fetch when it takes too long. In my background, I am accessing my webservice using AFNetworking to get some new data. If the time (I chose 25 seconds) is up, I just canceled all my open web service requests in the NSOperationQueue. Here is my code:

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {

    // This Handler accesses my webservice using AFNetworking and creates NSOperationQueue to enqueue the requests
    backgroundSearchHandler = BackgroundFetchSearchHandler()

    let cancelTimer = NSTimer.scheduledTimerWithTimeInterval(25, target: self, selector: "cancelBackgroundFetch", userInfo: nil, repeats: false)

    backgroundSearchHandler!.findNewResultsForSavedSearches { (completed, newResultCount) -> Void in

        // This block is also called when the timer is over and the requests were cancelled

        // Stop the timer
        cancelTimer.invalidate()

        if !completed
        {
            completionHandler(.Failed)
            return
        }

        if newResultCount <= 0
        {
            completionHandler(.NoData)
            return
        }

        completionHandler(.NewData)
    }
}

func cancelBackgroundFetch()
{
    backgroundSearchHandler?.cancel()
    // This is calling cancelAllOperations() on the NSOperationQueue in my background search handler
}

      



Hope this helps!

+1


source







All Articles