Unable to ensure Background Fetch is enabled in my app

Hi all as titled, I am creating an app (Lets give this app name as 'TM').



TM is a GPS Location Navigatator app that will trigger an alarm when user is reaching at some point near the destination.


So it needs MapKit & CoreLocation.

TM also receives Tweets from Public Transport Operator's official twitter account about the Subway's availability.

It should send a notification to user whenever a service disruption occured.

In either case, I need to have 'Background Fetch' capability enabled right?


This was what I did:


import UIKit
import Foundation
import CoreLocation
class Planner: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        //I start a Background Fetch function check when I start to load this page (One of the 4 tabs in my app).
        backgroundThread(0.0, background: {
            let bm = BackgroundManager()
            bm.checkBackground()
        })

    }
    override func viewDidAppear(animated: Bool) {}
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    //I wanted to create a Background Thread that can consistently check for Background Refresh permission is enabled.
    func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
        dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) {
            if(background != nil){ background!(); }

            let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            dispatch_after(popTime, dispatch_get_main_queue()) {
                if(completion != nil){ completion!(); }
            }
        }
    }
}



With such code, I still cannot get the dialog box to show even I had disabled Background Refresh on my device.



import Foundation
import UIKit
class BackgroundManager: UIViewController {
    func checkBackground(){
  
        let status = UIApplication.sharedApplication().backgroundRefreshStatus
  
        switch(status)
        {
      
        case UIBackgroundRefreshStatus.Denied:
        //Background Refresh denied. So show a popup to request user to enable it.
            let alert = UIAlertController(title: “Background Fetch is disabled”, message: “Turn on Background Fetch for best Experience”, preferredStyle: UIAlertControllerStyle.Alert)
      
          //OK button goes to relevant settings
      
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler:
                {
                    action in
                    /
                   UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
                }
            ))
           //Cancel button close this dialog
            alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
      
            //Show user this popup
            self.presentViewController(alert, animated: true, completion: nil)
      
            break
      
      
        case UIBackgroundRefreshStatus.Restricted:
            //TM is running on Restricted mode, perhaps blocked by Parental Controls
            //Show an alert to user for this fact
            let alert = UIAlertController(title: "TM running on restricted mode“, message: “TM cannot enable Background Fetch for best experience”, preferredStyle: UIAlertControllerStyle.Alert)
         
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
         
            //Shows a 1 button Popup to dismiss this message.
            self.presentViewController(alert, animated: true, completion: nil)
         
            break
  
        default:
            //Do nothing when the feature is enabled
            break
        }
    }
}



However, I still get the following error messages:



Warning: Unable to create restoration in progress marker file
Warning: Attempt to present UIAlertController: 0x13debaa90 on TM.BackgroundManager: 0x13df24ce0 whose view is not in the window hierarchy!





Thus, what should I do now in order to ensure this app gets the Background Refresh permission except when Parental Controls is running?

Answered by junkpile in 120791022

That error message indicates you're trying to present the alert controller from a view controller that's not being displayed. Rather than calling presentViewController on "self", you will need to use a view controller that's actually visible, such as your main UIWindow's rootViewController.


Also none of the code you've shown has anything to do with background fetch. Having that permission does NOT give your app permission to run a background thread forever. That background thread you're starting will be suspended along with the rest of the app eventually. Background fetch is a hint to the OS that it should wake your app up periodically with a call to your app delegate's performFetchWithCompletionHandler whenever it feels like it.

Accepted Answer

That error message indicates you're trying to present the alert controller from a view controller that's not being displayed. Rather than calling presentViewController on "self", you will need to use a view controller that's actually visible, such as your main UIWindow's rootViewController.


Also none of the code you've shown has anything to do with background fetch. Having that permission does NOT give your app permission to run a background thread forever. That background thread you're starting will be suspended along with the rest of the app eventually. Background fetch is a hint to the OS that it should wake your app up periodically with a call to your app delegate's performFetchWithCompletionHandler whenever it feels like it.

Oh, thanks for you reply.


So what is the correct way to implement an Alarm Clock with GPS functionality?


Basically, I intend to get the Alarm Clock ring only when user reaches some point near destination.


Correct me if I am wrong, I am thinking to eventually:


1) When the app start, I need the app to retrieve Tweets from a particular Twitter account about public transport availability (More importantly is on the impromptu disruptions in Subway / Metro). Hence, I am thinking to use a background thread for this purpose.


2) After confirming user's current location and destination, I will start another background thread that keeps updating on User's location via GPS so that the Alarm Clock knows when to ring. This thread should end after the user has acknowledged the alarm.


This includes the time where user lock screen or uses other app.


Thinking further, what would happen if the user switch into low power mode in iOS 9 (My app is intend to support only iOS 8 and above) or the user quits the app after the alarm is set to ring at sometime later (Point 2 above)?

I haven’t looked at this whole thread (sorry) but this definitely struck of a chord:

TM also receives Tweets from Public Transport Operator's official twitter account about the Subway's availability.

You should be doing this server side and notifying the user (via a push notification) or your app (via a silent push notification). Monitoring a network feed on the device is wasteful because all of your user’s devices need to wake up, fetch the same data, process it in the same way, and make the same decisions based on it.

Share and Enjoy

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

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

Hi eskimo, but is this option feasible for developers who do not own their own server / website as in this case?

Back in your original post you wrote:

"TM also receives Tweets from Public Transport Operator's official twitter account about the Subway's availability.

It should send a notification to user whenever a service disruption occured."


Normally, when someone writes that second sentence they've already committed to setting up their own server to send those notifications. Or they're working with the people involved with the other server (the twitter server, or the public transport office's server) to avoid having to set up a second server.

😢


Currently, it was more of a school project now and I had only just realized that appstore has this app SG NextStop - Best bus navigator in Singapore that is closest to what I intend to do, except I would like to include MRT (Subway) journeys which can be served as Alarm, One Stop Journey Planner (Apple Map has such feature but unfortunately it has not arrived into Singapore yet if I am not wrong), Tracking of transport fares (Transport Fare can be calculated from the authorities' API and store them into Core Data).


Since I would like to add Subway feature into this alarm app, I thought it was a nice idea to have a tab dedicated to service update similar to this fashion (And mistakenly assumed that I could just launch a popup using alert whenever a train disruption happens):


http://i104.photobucket.com/albums/m173/multiplex77/MRTBreakdown7July15_zps6ql3qlxe.jpg



Regardless still, I guess the best bet is to on-hold this push notification of service updates until I can get contact with relevant authorities to see how it can be done.

but is this option feasible for developers who do not own their own server / website as in this case?

No. But in a situation like this, where the server workload is constant regardless of how many users your app has, running your own server is generally both easy and cheap.

Share and Enjoy

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

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

I see, it seemed like I have shown a bad example of using Background Fetch when technically there are better and more elegant solutions. So, I would just mark the reply that explains where my error message came from and the hint how Background Process != Background Fetch != Use for live updates as answer.


Also thanks Eskimo for suggesting a more appropirate solution for this too.

Unable to ensure Background Fetch is enabled in my app
 
 
Q