Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

performBackgroundTask not saving when app in background Swift

I'm using Xcode 9 and Swift 4. I start a download of multiple JSON files when the application is in the foreground. The application then parses these files and saves them to CoreData. This works well when the application is in the foreground. However, if the application is in the background, the files still download correctly, but the data is not parsed and saved to CoreData. It's only when the user returns to the foreground that the parsing and saving of data continues.

I have Background Modes turned on - Background Fetch and Remote notifications.

I have around 10 functions that are similar to the one below in which it processes the JSON files concurrently:

func populateStuff(json: JSONDictionary) -> Void {
    let results = json["result"] as! JSONDictionary
    let stuffData = results["Stuff"] as! [JSONDictionary]

    let persistentContainer = getPersistentContainer()

    persistentContainer.performBackgroundTask { (context) in
        for stuff in stuffData {
            let newStuff = Stuff(context: context)
            newStuff.stuffCode = stuff["Code"] as? String
            newStuff.stuffDescription = stuff["Description"] as? String

            do {
                try context.save()
            } catch {
                fatalError("Failure to save context: \(error)")
            }
        }
    }
}

func getPersistentContainer() -> NSPersistentContainer {
    let persistentContainer = NSPersistentContainer(name: "MyProjectName")
    persistentContainer.loadPersistentStores { (_, error) in
        if let error = error {
            fatalError("Failed to load core data stack: \(error.localizedDescription)")
        }
    }
    persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
    persistentContainer.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    return persistentContainer
}

Can anyone advise me on why this might happen and how to over come this?

TIA

like image 952
Adam Altinkaya Avatar asked Aug 30 '18 08:08

Adam Altinkaya


1 Answers

Use the beginBackgroundTaskWithName:expirationHandler: method:

func populateStuff(json: JSONDictionary) -> Void {

    // Perform the task on a background queue.
    DispatchQueue.global().async {

        // Request the task assertion and save the ID.
        self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") {

        // End the task if time expires.
        UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
        self.backgroundTaskID = UIBackgroundTaskInvalid
    }

    // Parse the json files 
    let results = json["result"] as! JSONDictionary
    let stuffData = results["Stuff"] as! [JSONDictionary]

    let persistentContainer = getPersistentContainer()

    persistentContainer.performBackgroundTask { (context) in
        for stuff in stuffData {
            let newStuff = Stuff(context: context)
            newStuff.stuffCode = stuff["Code"] as? String
            newStuff.stuffDescription = stuff["Description"] as? String

            do {
                try context.save()
            } catch {
                fatalError("Failure to save context: \(error)")
            }
        }
    }

    // End the task assertion.
    UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
    self.backgroundTaskID = UIBackgroundTaskInvalid
}

Calling this method gives you extra time to perform important tasks. Notice the use of endBackgroundTask: method right after the task is done. It lets the system know that you are done. If you do not end your tasks in a timely manner, the system terminates your app.

like image 50
ielyamani Avatar answered Sep 30 '22 13:09

ielyamani