3

iOS is not executing PerformFetch.

I've put the app in the background and just left the device. It's been three days and still PerformFetch is not executed. I know this because I'm using event tracking in MS App Center.

public async override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
{         
        Analytics.TrackEvent($"{DateTime.UtcNow} Background fetch started");

        if (App.IsSyncInProgress)
        {
            Analytics.TrackEvent($"{DateTime.UtcNow} Background fetch failed - Sync in progress");
            completionHandler(UIBackgroundFetchResult.Failed);
        }

        App.IsSyncInProgress = true;

        var loginService = App.IOCContainer.Get<ILoginService>();
        var tokenStorageService = App.IOCContainer.Get<ITokenStorage>();
        var syncService = App.IOCContainer.Get<ISyncService>();
        var userRepo = App.IOCContainer.Get<IUserRepository>();
        var sarService = App.IOCContainer.Get<ISuspiciousActivityReportService>();

        bool? result = null;

        try
        {
            if (await loginService.IsUserAuthorised())
            {
                var user = await userRepo.FetchUser();
                var bearerToken = tokenStorageService.FetchBearerToken(user.UserPrincipalName);

                if (bearerToken != null)
                {
                    result = await syncService.SyncData(bearerToken);
                    Analytics.TrackEvent($"{DateTime.UtcNow} - SyncData result: {result}");

                    result = await sarService.RetrySubmitOutstandingSuspiciousActivityReports(bearerToken);
                    Analytics.TrackEvent($"{DateTime.UtcNow} - RetrySubmitOutstandingSuspiciousActivityReports result: {result}");
                }
                else
                {
                    Analytics.TrackEvent($"{DateTime.UtcNow} - Background fetch silently failed, bearerToken is null");
                }
            }
        }
        catch (System.Net.WebException webEx)
        {
            Analytics.TrackEvent($"{DateTime.UtcNow} - Background fetch failed due to Exception: {webEx.Message}");
            completionHandler(UIBackgroundFetchResult.Failed);
        }
        catch (System.Net.Http.HttpRequestException httpEx)
        {
            Analytics.TrackEvent($"{DateTime.UtcNow} - Background fetch failed due to Exception: {httpEx.Message}");
            completionHandler(UIBackgroundFetchResult.Failed);
        }
        catch (Exception ex)
        {
            Analytics.TrackEvent($"{DateTime.UtcNow} - Background fetch failed due to Exception: {ex.Message}");
            completionHandler(UIBackgroundFetchResult.Failed);
        }
        finally
        {
            App.IsSyncInProgress = false;
        }

        var response = result == null ? UIBackgroundFetchResult.Failed : result.Value ? UIBackgroundFetchResult.NewData : UIBackgroundFetchResult.NoData;

        Analytics.TrackEvent($"Background fetch finished {DateTime.UtcNow} - result: {result}");

        //Console.WriteLine($"PeformFetch - Completed: {result}");

        // Inform system of fetch results
        completionHandler(response);
    }

I know that iOS determines when to execute the background fetch but really?! Three days?!

Pretty sure all my code is correct because I can simulate a Background Fetch in the Simulator successfully, but get nothing when on a real device.

I've checked through the device logs and notice that iOS is actually determining that the background fetch should not run

Jan  8 10:09:28 my-iPad dasd(DuetActivitySchedulerDaemon)[105] <Notice>: com.apple.fetch.com.imptob.ituk.sara:F276C2:[
    {name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Must Not Proceed, Score: 0.00}}
 ], FinalDecision: Must Not Proceed}

Jan  8 10:10:09 my-iPad dasd(DuetActivitySchedulerDaemon)[105] <Notice>: Submitted Activity: com.apple.fetch.com.imptob.ituk.sara:20D953 <private>

Jan  8 10:10:09 my-iPad dasd(DuetActivitySchedulerDaemon)[105] <Notice>: CANCELED: com.apple.fetch.com.imptob.ituk.sara:F276C2 <private>!

Jan  8 10:10:20 my-iPad dasd(DuetActivitySchedulerDaemon)[105] <Notice>: com.apple.fetch.com.imptob.ituk.sara:20D953:[
        {name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Must Not Proceed, Score: 0.00}}
     ], FinalDecision: Must Not Proceed}

Jan  8 10:12:07 my-iPad assertiond[67] <Notice>: [sara.ios:666] pid_shutdown_sockets(2) success

Jan  8 10:12:09 my-iPad dasd(DuetActivitySchedulerDaemon)[105] <Notice>: com.apple.fetch.com.imptob.ituk.sara:20D953:[
    {name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Must Not Proceed, Score: 0.00}}
 ], FinalDecision: Must Not Proceed}

Previous apps I've developed have performed the background fetch without issue although those apps were used quite regularly.

Does this seem normal?

empo
  • 1,133
  • 5
  • 21
  • 41
  • You aren't calling the completion handler with a background fetch result. If you don't play by the rules, iOS won't schedule you for background fetch. Also, it is unlikely that your analytics upload will complete before the background fetch function exits, so you won't get the analytics event recorded. Try posting a local notification when background fetch occurs. – Paulw11 Jan 08 '18 at 12:06
  • @Paulw11my bad, have updated question with full code sample. As you can see completionHandler is being called. The point I was trying to make is that PerformFetch is not being called. As for the analytics upload not completing, I have successfully got full analytics and PerformFetch is called when simulating a BackgroundFetch – empo Jan 08 '18 at 12:16
  • The simulator isn't the same as the real device. Have you set the background fetch interval on your UIApplication instance? https://developer.apple.com/documentation/uikit/uiapplication/1623100-setminimumbackgroundfetchinterva – Paulw11 Jan 08 '18 at 12:51
  • yes, using minimumBackgroundFetchInterval – empo Jan 08 '18 at 13:34
  • Don't know if you found out of this already, but I just noticed something strange about the Analytics.TrackEvent method. I have not quite figured it out yet, but it seems like there might be problems related to recording events when the app is in the background or suspended state. I need to investigate more, but I can say for sure that some events I tracked were lost. The way I figured it out, was that in a geofence event (that is supposed to wake up the app from background) I called TrackEvent and did a http request to requestb.in. The last one showed up, but not the TrackEvent. – TMan Mar 07 '18 at 10:33
  • This seemed to occur more frequently when the app is in background/supspended mode. My theory is that either the TrackEvent does not work in suspended mode, or that the app goes to sleep again before the TrackEvent is finished (while the http request manages to complete). Could you try making a http request from your background method? Maybe it is run without you knowing? Yeah, it sounds strange, I know... – TMan Mar 07 '18 at 10:37
  • Any update since then? I'm just seeing the same behavior with my PerformFetch not being called on the device... – Fred Aug 30 '18 at 10:14
  • 1
    @Fred no sorry, I gave up and instead implemented silent push notification and performed data fetch when notification was received. – empo Sep 03 '18 at 08:12

0 Answers0