Hi I’m currently working on a cross-platform (native Java and Swift/Objective-C) mobile app where the users may frequently have limited access to a network connection, but we want to record and send up statistics when a network becomes available, even if our app has been closed. We’ve already done this on Android fairly easily with their AndroidX.WorkManager library like so:
String URL = "https://myexampleurl.com";
Data inputData = new Data.Builder()
.putString("URL", URL)
.build();
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(NetworkWorker.class)
.setInputData(inputData)
.setConstraints(constraints)
.build();
WorkManager.getInstance(context).enqueueUniqueWork("com.lkuich.exampleWorker", ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest);
This works great, when our app invokes this method, our request is added to a queue, and even if the user is offline and closes our app, when they reconnect to a network our request still gets made.
I’ve been struggling however to find an iOS equivalent to this API, and I am fairly new to native iOS development. We've enabled the Background Task capability and registered our identifiers in Plist.info in our project.
First we’ve tried the URLSession "background" API like so:
class NetworkController: NSObject, URLSessionTaskDelegate {
func buildTask() -> URLSession {
let config = URLSessionConfiguration.background(withIdentifier: "com.lkuich.exampleWorker")
config.waitsForConnectivity = true
config.shouldUseExtendedBackgroundIdleMode = true
return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
}
func makeNetworkRequest(session: URLSession) {
let url = URL(string: "https://myexampleurl.com")!
let task = session.dataTask(with: url)
task.resume()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("We're done here")
}
}
This works well when the WiFi is disabled and re-enabled, but if the app is closed the request never gets sent it seems.
So we tried the BGTaskScheduler instead, but I'm having a hard time testing if it's working in the background with the app closed, since it can often take hours to actually run (forcing it to run immediately with a network connection works):
static let bgIdentifier = "com.lkuich.exampleWorker"
// Invoked on app start (didFinishLaunchingWithOptions)
static func registerBackgroundTasks() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: bgIdentifier, using: nil) { (task) in
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
// Make my network request in BG
let url = URL(string: "https://myexampleworker.com")!
let t = URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
// Print the response
print(response)
task.setTaskCompleted(success: true)
})
t.resume()
}
}
static func makeRequest() {
do {
let bgRequest = BGProcessingTaskRequest(identifier: bgIdentifier)
bgRequest.requiresNetworkConnectivity = true
bgRequest.requiresExternalPower = false
try BGTaskScheduler.shared.submit(bgRequest)
print("Submitted task request")
} catch {
print("Failed to submit BGTask")
}
}
So overall, my question is, is it possible to schedule a network request to be run in the background when a network is available, even if the invoking app is closed? And if so, how would I pass input data (like URL args for example) to my background task, since my task has to be registered on app start?
Thank you, Loren K