Search code examples
httpposterror-handlingnsjsonserializationswift2

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()
}

Solution

  • 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.