Reputation: 183
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
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