mir
mir

Reputation: 183

Swift 3- Update UI from main thread

I wanted to load data in background thread and update tableview/UI on main thread. Based on what's indicated here about threading, I was wondering if the code below is the way to go about it. I'm trying to load more data as user scrolls to a specific index and wanted to make sure the UI is not freezing due to threading. Thank you!

 func loadMore () {
    guard !self.reachedEndOfItems else {
        return
    }

    self.offset = self.offset! + 10
    print("load more offset: \(self.offset)")

    var start = 0
    var end = 0


    isPullToRefresh = false

    let userCreds = UserDefaults.standard

    var getReqString = ""


    if userCreds.bool(forKey: "client_journal") == true || userCreds.bool(forKey: "user_journal") == true {

        var pageNum = ""
        if let pgNum = currentPgNum {
            print(pgNum)
            pageNum = String(pgNum)
        }
        var filterEntryType = ""
        if let entryTypeStr = filtEntryType {
            filterEntryType = entryTypeStr
        }

        var filterUserId = ""
        if let userId = filtUserId {
            filterUserId = userId

        }

        getReqString = "https://gethealthie.com/selected_entries.json?page=\(pageNum)&user_id=\(filterUserId)&entry_type=\(filterEntryType)&entry_filter="


    } else {

        if let pgNum = currentPgNum {
            print(pgNum)

            getReqString = "https://gethealthie.com/entries.json?page=\(pgNum)"
        }

    }


        BXProgressHUD.showHUDAddedTo(self.view)
        let request = Alamofire.request(getReqString, method: .get, headers: [
            "access-token": userCreds.object(forKey: "access-token")! as! String,
            "client": userCreds.object(forKey: "client")! as! String,
            "token-type": userCreds.object(forKey: "token-type")! as! String,
            "uid": userCreds.object(forKey: "uid")! as! String,
            "expiry": userCreds.object(forKey: "expiry")! as! String
            ]).responseJSON { (response:DataResponse<Any>) in
                print(response.response)

                let json = JSON(data: response.data!)
                print(json)

                print("yes")
                print(json.count)


                if userCreds.bool(forKey: "client_journal") == true || userCreds.bool(forKey: "user_journal") == true {
                    self.totalEntries = json["entries"].count

                    let totalEntryCount = json["entries"].count
                    start = 0
                    end = totalEntryCount
                } else {
                    self.totalEntries = json["entries"].count

                    let totalEntryCount = json["entries"].count
                    start = 0
                    end = totalEntryCount
                }

                if  self.totalEntries == 0 {
                    BXProgressHUD.hideHUDForView(self.view);

                } else if end <= self.totalEntries {
                    var jourIdx = 0

                    let newPatient = Patient()
                    let newDietitian = Dietitian()


                    for i in start ..< end {


                        let allEntries = json["entries"]
                        print(allEntries)
                        print("Entry count in loadMore is \(allEntries.count)")

                        let entry = allEntries[i]
                        print(entry)

                        let category = entry["category"]
                        print(category)


                        let name = entry["entry_comments"]
                        let k = name["id"]


                        var indexStr = String(i)

                        //entry attributes
                        self.jsonIdx.add(indexStr)

                        self.type.add(entry["type"].stringValue)
                        self.desc.add(entry["description"].stringValue)
                        self.category.add(entry["category"].stringValue)
                        //food cell- metric stat == healthy int
                        self.metric_stat.add(entry["metric_stat"].stringValue)
                        self.dateCreate.add(entry["created_at"].stringValue)
                        self.viewed.add(entry["viewed"].stringValue)
                        self.seenStatusArr.add(entry["viewed"].stringValue)
                        self.comments.add(entry["entry_comments"].rawValue)
                        self.entryType.add(entry["category"].stringValue)
                        //   "category" : entryType as AnyObject]

                        let posterInfo = entry["poster"]
                        let first = posterInfo["first_name"].stringValue
                        let last = posterInfo["last_name"].stringValue
                        let full = first + " " + last
                        self.captionName.add(full)


                        //food cell subcat
                        self.hungerInt.add(entry["percieved_hungriness"].stringValue)
                        self.prehunger.add(entry["ed_prehunger_string"].stringValue)
                        self.posthunger.add(entry["ed_posthunger_string"].stringValue)
                        self.emotions.add(entry["emotions_string"].stringValue)
                        self.reflection.add(entry["reflection"].stringValue)


                        print(self.comments)
                        self.id.add(entry["id"].stringValue)
                        self.entryImages.add(entry["image_url"].stringValue)


                        if i == end - 1 {
                            userCreds.set(json.count, forKey: "oldJsonCount")

                               BXProgressHUD.hideHUDForView(self.view)

                            DispatchQueue.main.async {
                                self.tableView.reloadData()

                            }
                    }


                } else {
                    var reachedEndOfItems = true
                    BXProgressHUD.hideHUDForView(self.view);
                    print("reached the end")
                }

    }

Upvotes: 4

Views: 3621

Answers (1)

Rob
Rob

Reputation: 437632

In your code sample here, you are dispatching the reloadData to the main queue. But that is unnecessary because the closure of responseJSON is already running on the main queue, so there's no need to dispatch anything. So, you should remove that dispatch of reloadData to the main queue.

Now, if you were using URLSession, which defaults to running closures on a background queue or if you explicitly provided a background queue as the queue parameter of responseJSON, then, yes, you'd dispatch reloadData to the main queue. But that's not the only thing you'd need to make sure to dispatch to the main queue, as your model updates and the HUD updates should run on the main queue, too. But that's moot, because this responseJSON is already running its completion handler on the main queue.

Then, in comments, you later ask if all of this is running on the main queue, whether you should dispatch this all to a background queue like you did in a previous question (presumably with the intent of wanting to avoid blocking the main queue).

It turns out that this is not necessary (nor desirable) because while the processing of the response in the responseJSON completion handler runs on the main queue, the network request, itself, is performed asynchronously. You would only dispatch the completion handler code to a background queue (or specify a background queue as a parameter to responseJSON) if you were doing something computationally intensive in the closure. But you don't have to worry about the network request blocking the main queue.

Bottom line, Alamofire makes this simple for you, it runs the request asynchronously but runs its completion handlers on the main queue. It eliminates much of the manual GCD code that you mess around with when using URLSession.

Upvotes: 3

Related Questions