Search code examples
iosswiftasynchronousgrand-central-dispatchdispatch-async

Swift. Running async download in the background and let user interact with UI


The following function is being used to synchronize the app with json data from a server and this takes up a few a minutes if the content hasn't been updated for a while. As for now, the app blocks the main UI and tells the user that it's updating with new content and a spinning wheel appears. I would like to remove the "blocking overlay with updating data.. text and spinning wheel" and let the app download and sync the json to the db in the background, so that the user could keep on interacting with the UI.

I've tried to use dispatch_async but the UI keeps being blocked until the update is done. Does anyone have any suggestions on how to achieve this?

I stripped down the code to the relevant points and added the updateData function where the ApiWrapper does the networking job.

func syncData(now: Bool) {

    var synced : Bool = false

    if ((NSUserDefaults.standardUserDefaults().objectForKey("lastSyncApp")) != nil) {
        var interval = 0
        if ((NSUserDefaults.standardUserDefaults().objectForKey("intervalSyncValue")) != nil) {
            interval = NSUserDefaults.standardUserDefaults().objectForKey("intervalSyncValue") as! Int
        }
        else{
            interval = 86400
        }
        let date = NSUserDefaults.standardUserDefaults().objectForKey("lastSyncApp") as! NSDate
        var timestamp = UInt64(floor(date.timeIntervalSince1970))
        //Create final date
        let lastsyncTimestampDate : UInt64 = timestamp + UInt64(interval)

        var nowTimestampDate = UInt64(floor(NSDate().timeIntervalSince1970))

        if ((nowTimestampDate > lastsyncTimestampDate) || now) {


                synced = true
                let formatter = NSDateFormatter()
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                //MRProgressOverlayView.showOverlayAddedTo(self.window, title: "Aggiorno dati...", mode: MRProgressOverlayViewMode.Indeterminate, animated: true, tintColor: UIColor.blackColor())

                self.downloadDate = formatter.stringFromDate(date)
                NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("updateData:"), userInfo: nil , repeats: false)



    ….}        








func updateData(timer: NSTimer!) {


    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_async(dispatch_get_global_queue(priority, 0)) {
        // do some task



    var ApiWrapper = API()

    let date = self.downloadDate


            ApiWrapper.model.deleteNews()
            ApiWrapper.model.resetNewsFetch()



            //var ApiWrapper = API()

….. }


Solution

  • I don't see where you are using a background thread in this code, but here is a really concise example of how to do this.

    basically,

    dispatch_async(dispatch_get_global_queue(priority, 0)) {}
    

    will get you on the background thread, and if you need to update the UI from there, make sure you execute that code back on the main thread:

    dispatch_async(dispatch_get_main_queue()) {}
    

    Swift 4 UPDATE

    DispatchQueue.global(qos: [Desired QoSClass]).async {
        // This code is executing on a background thread at 
        // your desired quality of service level
    
        DispatchQueue.main.async {
             // Code to be executed on the main thread here
        }
    }