Reputation: 4292
I am very new to learning iOS and Swift, so there may be something very basic here that I don't understand. I am using the agent library to send HTTP requests in Swift. Right now I'm just getting the hang of creating the request and parsing the JSON response.
I have a very simple UI with a button and a UILabel
that I want to update with the results of some JSON.
Here is my code:
func updateWithJson() -> Void {
let req = Agent.get("http://xkcd.com/info.0.json")
req.end({ (response: NSHTTPURLResponse!, data: Agent.Data!, error: NSError!) -> Void in
let json = data! as Dictionary<NSString, NSString>
// this takes almost 30 seconds to propagate to the UI...
self.updateMe.text = json["safe_title"]!
})
}
The part I don't understand is the self.updateMe.text = json["safe_title"]!
statement which is just updating some text on a UILabel
called updateMe
. It takes almost a full 30 seconds for this to reflect in the UI.
I know that the HTTP request itself is very fast - if I curl that URL it comes back instantaneously. It also seems like the Agent
object is making the connection and returning the response pretty fast.
Am I missing something very basic? Should I be updating the UI in a different way? All pointers are appreciated!
UPDATE
Through another SO post it's my understanding that the block inside of req.end
is a background thread and I need to do UI updates on the main thread. I updated my function to look like this and I get the desired result. However, now I'm wondering if there's something incorrect about doing it this way?
func updateWithJson() -> Void {
let req = Agent.get("http://xkcd.com/info.0.json")
req.end({ (response: NSHTTPURLResponse!, data: Agent.Data!, error: NSError!) -> Void in
let json = data! as Dictionary<NSString, NSString>
var text = json["safe_title"]!
dispatch_sync(dispatch_get_main_queue()) {
self.updateMe.text = text
}
})
}
Upvotes: 1
Views: 1265
Reputation: 42325
What's happening is that your HTTP request is running on a background thread. When it calls the callback you provide, you're still on that background thread. In iOS all UI work must be done on the main thread. The easiest way to do this is by using GCD's dispatch_async
like so:
dispatch_async(dispatch_get_main_queue()) {
[weak self] in
self?.updateMe.text = json["safe_title"]!
return
}
So your entire function would look like:
func updateWithJson() -> Void {
let req = Agent.get("http://xkcd.com/info.0.json")
req.end({ (response: NSHTTPURLResponse!, data: Agent.Data!, error: NSError!) -> Void in
let json = data! as Dictionary<NSString, NSString>
dispatch_async(dispatch_get_main_queue()) {
[weak self] in
self?.updateMe.text = json["safe_title"]!
return
}
})
}
The bit about [weak self] in
is so you don't create a strong reference cycle. The return
at the end is there because you have a closure with only one expression in it. In that case, Swift will try to implicitly return a value based on that statement and in this case that's not what you want.
Upvotes: 6