Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP POST error Handling in Swift 2

I'm new here, and this is my first question... I try to write an App in Swift 2 that makes an HTTP POST request but I can't figure out how to use the new error handling of Swift 2. Can anyone tell me please how to implement the "do-try-catch" error handling of Swift 2 to the code snippet below? (This code snippet uses the old error handling of swift 1.2)

func post(params : Dictionary<String, String>, url : String) {
    var request = NSMutableURLRequest(URL: NSURL(string: url)!)
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    var err: NSError?
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil/*, error: &err*/)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        print("Response: \(response)")
        var strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print("Body: \(strData)")
        var err: NSError?
        var json = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves/*, error: &err*/) as? NSDictionary

        // Did the JSONObjectWithData constructor return an error? If so, log the error to the console
        if(err != nil) {
            print(err!.localizedDescription)
            let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print("Error could not parse JSON: '\(jsonStr)'")
        }
        else {
            // The JSONObjectWithData constructor didn't return an error. But, we should still
            // check and make sure that json has a value using optional binding.
            if let parseJSON = json {
                // Okay, the parsedJSON is here, let's get the value for 'success' out of it
                var success = parseJSON["success"] as? Int
                print("Succes: \(success)")
            }
            else {
                // Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Error could not parse JSON: \(jsonStr)")
            }
        }
    })

    task!.resume()
}
like image 277
Michael_mhr Avatar asked Jun 24 '15 15:06

Michael_mhr


2 Answers

You presumably want to wrap your NSJSONSerialization calls in do/try/catch logic as shown below.

In Swift 3:

var request = URLRequest(url: URL(string: urlString)!)

let session = URLSession.shared
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

request.httpBody = try! JSONSerialization.data(withJSONObject: parameters)

// or if you think the conversion might actually fail (which is unlikely if you built `parameters` yourself)
//
// do {
//    request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
// } catch {
//    print(error)
// }

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

    // this, on the other hand, can quite easily fail if there's a server error, so you definitely
    // want to wrap this in `do`-`try`-`catch`:

    do {
        if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
            let success = json["success"] as? Int                                  // Okay, the `json` is here, let's get the value for 'success' out of it
            print("Success: \(success)")
        } else {
            let jsonStr = String(data: data, encoding: .utf8)    // No error thrown, but not dictionary
            print("Error could not parse JSON: \(jsonStr)")
        }
    } catch let parseError {
        print(parseError)                                                          // Log the error thrown by `JSONObjectWithData`
        let jsonStr = String(data: data, encoding: .utf8)
        print("Error could not parse JSON: '\(jsonStr)'")
    }
}

task.resume()

Or, in Swift 2

let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)

let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])

// or if you think the conversion might actually fail (which is unlikely if you built `parameters` yourself)
//
// do {
//    request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
// } catch {
//    print(error)
// }

let task = session.dataTaskWithRequest(request) { data, response, error in
    guard let data = data where error == nil else {
        print("error: \(error)")
        return
    }

    // this, on the other hand, can quite easily fail if there's a server error, so you definitely
    // want to wrap this in `do`-`try`-`catch`:

    do {
        if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
            let success = json["success"] as? Int                                  // Okay, the `json` is here, let's get the value for 'success' out of it
            print("Success: \(success)")
        } else {
            let jsonStr = String(data: data, encoding: NSUTF8StringEncoding)    // No error thrown, but not NSDictionary
            print("Error could not parse JSON: \(jsonStr)")
        }
    } catch let parseError {
        print(parseError)                                                          // Log the error thrown by `JSONObjectWithData`
        let jsonStr = String(data: data, encoding: NSUTF8StringEncoding)
        print("Error could not parse JSON: '\(jsonStr)'")
    }
}

task.resume()

I'd also suggest being a little more careful about forced unwrapping of data, because you want to detect/handle the errors, not crash. For example, above I use a guard statement to unwrap it.

like image 167
Rob Avatar answered Sep 20 '22 07:09

Rob


In general if a function throws you have to write it inside a do catch block or just mark the outer scope function (in this case post) as throws:

func post(params : Dictionary<String, String>, url : String) {
    let request = NSMutableURLRequest(URL: NSURL(string: url)!)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted)
    } catch {
        //handle error. Probably return or mark function as throws
        print(error)
        return
    }
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        // handle error
        guard error == nil else { return }

        print("Response: \(response)")
        let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print("Body: \(strData)")

        let json: NSDictionary?
        do {
            json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
        } catch let dataError {
            // Did the JSONObjectWithData constructor return an error? If so, log the error to the console
            print(dataError)
            let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print("Error could not parse JSON: '\(jsonStr)'")
            // return or throw?
            return
        }


        // The JSONObjectWithData constructor didn't return an error. But, we should still
        // check and make sure that json has a value using optional binding.
        if let parseJSON = json {
            // Okay, the parsedJSON is here, let's get the value for 'success' out of it
            let success = parseJSON["success"] as? Int
            print("Succes: \(success)")
        }
        else {
            // Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
            let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print("Error could not parse JSON: \(jsonStr)")
        }

    })

    task!.resume()
}

If you don't want to handle these errors right in your post function you can just declare it as throws than you don't have to use do catch at all

like image 34
Qbyte Avatar answered Sep 20 '22 07:09

Qbyte