Kevin Kevin - 8 days ago 6
HTTP Question

Problems Sending an HTTP Request Using Swift

I'm entirely new to development in Swift or networking. I'm trying to throw something simple together that query a URL:

I'm trying to write a simple program, where I have three buttons, and each time a button is tapped, I send data to the data.sparkfun.com data stream storing service where it will store my variable "test_value"

I need to query this URL:

http://data.sparkfun.com/input/XGyzWVyDdEFKDE42W99r?private_key=KEYVALUE&test_value=1"


Here is the code I'm trying to work with unsuccessfully after reading some simple tutorials:

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

@IBAction func ColorTwoTapped(_ sender: Any) {
print("Color 2")
}

@IBAction func ColorOneTapped(_ sender: Any) {
print("Color 1")

let scriptUrl = "http://data.sparkfun.com/input/XGyzWVyDdEFKDE42W99r"

let urlWithParams = scriptUrl + "?private_key=KEYVALUE&test_value=1"

let myUrl = NSURL(string: urlWithParams);


let task = URLSession.sharedSession().dataTaskWithURL(myUrl!) {(data, response, error) in
print(String(data: data!, encoding: NSUTF8StringEncoding))
}

task.resume()


}

@IBAction func ColorThreeTapped(_ sender: Any) {
print("Color 3")
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


The error I get is

Cannot call value of non-function type 'URLSession'.


Thanks for any advice

Rob Rob
Answer

It looks like the tutorial that you've selected was written for Swift 2.x, whereas you are apparently using Swift 3.

In Swift 3, the method would be:

@IBAction func colorOneTapped(_ sender: Any) {
    print("Color 1")

    let scriptUrl = "http://data.sparkfun.com/input/XGyzWVyDdEFKDE42W99r"

    let urlWithParams = scriptUrl + "?private_key=KEYVALUE&test_value=1"

    let myUrl = URL(string: urlWithParams)

    let task = URLSession.shared.dataTask(with: myUrl!) { data, response, error in
        guard let data = data, error == nil else {
            print("\(error)")
            return
        }

        let string = String(data: data, encoding: .utf8)
        print("\(string)")
    }

    task.resume()
}

Note, it's URL, not NSURL. And it's shared, not sharedSession(). Likewise dataTaskWithURL(_:completionHandler:) is now dataTask(with:completionHandler:). I'd also suggest avoiding ! forced unwrapping and use optional binding (e.g. if let or guard let).


Note, if you sending data to be stored on the server, you'd generally use POST:

@IBAction func colorOneTapped(_ sender: Any) {
    print("Color 1")

    let urlString = "http://data.sparkfun.com/input/XGyzWVyDdEFKDE42W99r"

    let url = URL(string: urlString)
    var request = URLRequest(url: url!)
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.httpBody = "private_key=KEYVALUE&test_value=1".data(using: .utf8)
    request.httpMethod = "POST"

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            print("\(error)")
            return
        }

        let string = String(data: data, encoding: .utf8)
        print("\(string)")
    }

    task.resume()
}

Note, there are tons of other little details here, too, such as needing to percent-escape the keys and values if you'll be sending anything other than a-z, A-Z, 0-9 and -, ., _, or ~ (whether in the URL of GET request or in the body of the POST request). And you'd often set the Accept header to let the server know in what form you're expecting the response and then write the code to parse that sort of response. Likewise, you need to set the NSAppTransportSecurity in the Info.plist if you're using http rather than https.


You might also want to consider using something like Alamofire if you don't want to get too lost in all of these details.