Hi, all!
I just followed a (rather poor) tutorial by Intel that has some (rather poor) source code for the project.
Intel builds a small app with some sliders that send their values to an internet server, (The Intel Edison attached to some motors).
On the swift side, the only thing I'm confused about how the sliders work. Here is the code:
@IBAction func leftSliderMoved(sender: UISlider) {
//Updatng the UILabel for the slider
let currentVal = Int(sender.value);
leftSliderLabel.text = "\(currentVal)";
//The actual internet stuff
let url = NSURL(string: "http://\(ipField.text):8080/change?left=\(leftMotorSlider.value)");
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {
(data, response, error) in
print(NSString(data: data!, encoding: NSUTF8StringEncoding));
}
task.resume();
}The textBox ipField contains the text for the local ip address on the wifi network for the web server. We use this text to go to a url at port 8080. A few questions:
- What am I doing in the rest of the url? It looks like I'm sending an order to change the left motor to a specific value. What's up with the syntax?
- Notice on line 10 that data! is a force-unwrapped optional. This does no good. If the wrong ip address is in the box or a connection fails, the app crashes. Is there a way to safely unwrap dataand display a 'connection failed' message instead of crashing the entire app?
- Lastly, if someone could explain to me what's happening in let task = and downwards, I would really appreciate it. The tutorial explains nothing.
Feel free to explain things or just drop a helpful link if you're short on time. I'm really interested in this stuff and would love to learn more.
Thanks so much!
1. What am I doing in the rest of the url?
This is forming a URL via string interpolation. The stuff inside
\() is rendered to a string and then is inserted into the top-level string. For example, if
ipField.text was
1.2.3.4 and
leftMotorSlider.value was
5, this would yield the URL
http://1.2.3.4:8080/change?left=5.
Building URLs by manipulating strings is fine for a simple test project but for real code I strongly recommend NSURLComponents; it handles a world of special cases that are a pain to handle yourself. Here’s how I do this:
let c = NSURLComponents(string: "http://xxx:8080/change")!
c.host = "1.2.3.4"
c.queryItems = [NSURLQueryItem(name: "left", value: "5")]
print(c.URL!) // prints "http://1.2.3.4:8080/change?left=5"For example, NSURLComponents will do the right thing if the
host property is set to an IPv6 address.
2. Notice on line 10 that data! is a force-unwrapped optional. This does no good.
Quite.
You should check the
error value to detect network transport errors. You should check the HTTP status code in
response to detect server-side errors. Here’s how I usually do this:
….dataTaskWithURL(…) { (data, response, error) in
guard let error = error else {
// … handle transport error …
return
}
let response = response as! NSHTTPURLResponse
guard (response.statusCode / 100) == 2 else {
// … handle server error …
return
}
// … success …
}Some things to note here:
I force cast (
)as!
to NSHTTPURLResponse because NSURLSession should always give you an NSHTTPURLResponse in response to anresponse
orhttp:
request.https:HTTP status codes in the 200 range are usually associated with successful completion. You may need to adjust that check depending on how your server responds to errors.
3. Lastly, if someone could explain to me what's happening in let task = and downwards, I would really appreciate it.
The
let itself (line 8) creates a task.
You pass a closure (lines 9 and 10) when you create the task and that closure is run by the session when the task completes.
The call to
resume() starts the task. By default tasks are started in the suspended state so that, once you’ve created the task, you can use it to set up the bookkeeping necessary to handle the task completing. In this case there’s no such bookkeeping, so you immediately resume the task.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"