Swift/iOS Refreshing App Data When in Background

Stop app from refreshing when going into background. (iOS)

makeUIView is called to create the view.
updateUIView is called multiple times to update the view

So anything you want to do just once, do it in the makeUIView

Move the code from updateUIView to makeUIView and check if things work as expected.

swift/ios refreshing app data when in background

Use iOS Background Fetch feature where you can specify minimum background fetch interval. But actual interval between successive invocation of your code will be determined by iOS framework. For details checkout this link: http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520

I use this approach in my app and I think it is a preferred way of doing.

Does background app refresh have an effect on a background URLSession?

I finally found an answer to this problem. Posting it here for anyone having the same issue.

Thanks to this thread, see Quinn's response below :

iOS 13 introduces new behaviour (r. 47718087) whereby, if the user has turned Background App Refresh off in Settings — either globally or for your app, assuming it has this setting — your app won’t be resumed (or relaunched) when the tasks complete in an NSURLSession background session. That is, your session will behave as if sessionSendsLaunchEvents were set to false.

How to configure Background App Refresh using Swift?

You will need to move your downloading code from the view controller and into another class or at least modify you current background refresh method to instantiate the view controller if required. Background refresh can be triggered when your app hasn't been launched in the foreground, so the if let will fall through.

Consider the code in your question:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
}
}

If the if let... doesn't pass then you exit from the function without calling the completionHandler, so you get the runtime warning that the completion handler was not called.

You could modify your code to include a call to the completionHandler in an else case, but in this case no fetch will have taken place:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
} else {
completionHandler(.noData)
}

Or you could instantiate the view controller (or I would suggest another data fetching class) if required:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let vc = (window?.rootViewController as? SearchVC) ?? SearchVC()
// Update JSON data
vc.downloadJSON()
completionHandler(.newData)
}

You should also modify your downloadJSON function to include a completion handler argument, which you invoke when the JSON download is complete. This will let you call the background fetch completion handler once you have actually downloaded the data:

func downloadJSON(completion: ((Bool,Error?) -> Void )? = nil)) {

guard let url = URL(string: "myURL") else {
completion?(false, nil)
return
}

URLSession.shared.dataTask(with: url) { (data, response, err) in

guard nil == err else {
completion?(false, err)
return
}

guard let data = data else {
completion?(false, nil)
return
}

do {
let downloadedCurrencies = try JSONDecoder().decode([Currency].self, from: data)

// Adding downloaded data into Local Array
Currencies = downloadedCurrencies
completion(true,nil)
} catch let jsonErr {
print("Here! Error serializing json", jsonErr)
completion?(false,jsonErr)
}

}.resume()
}

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let vc = (window?.rootViewController as? SearchVC) ?? SearchVC()
// Update JSON data
vc.downloadJSON() { (newData,error) in
if let err = error {
NSLog("Background fetch error: \(err.localizedDescription)")
completionHandler(.fail)
} else {
completionHandler(newData ? .newData:.noData)
}
}
}

Update September 2019

Note that iOS 13 introduces new background fetch and processing functionality. Refer to this WWDC session for more details



Related Topics



Leave a reply



Submit