Number1
Number1

Reputation: 391

Perform POST request in iOS Swift

I am trying perform a POST request and the request does not go through. I have looked through Perform POST request in Swift already but it does not contain what I'm looking for.

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    var request = NSMutableURLRequest(URL: NSURL(string: "https://us1.lacunaexpanse.com"))
    println("request url https://us1.lacunaexpanse.com")
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    let apikey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    println("apikey",apikey)

    let username = "username"
    let password = "password"

    var login = Array(["username", "password", "apikey"])

    let jsonDictionary = ["2.0", "jsonrpc", "1", "id", "login", "method", "login", "params"]
    println("jsonDictionary",jsonDictionary)

    var writeError: NSError?

    let jsonData = NSJSONSerialization.dataWithJSONObject(jsonDictionary, options: NSJSONWritingOptions(), error: NSErrorPointer())

    var resultAsString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)

    resultAsString = resultAsString.stringByAppendingString("empire")

    let url = NSURL.URLWithString("string")
    println("url",url)

    var request2 = NSMutableURLRequest()
    println("Post url =%@",url)

    request2 = NSMutableURLRequest(URL:url)

    request2.HTTPMethod = "POST"

    var connection = NSURLConnection(request: request, delegate: self, startImmediately: false)

    return true

Upvotes: 2

Views: 12453

Answers (2)

Rob
Rob

Reputation: 437482

There are a whole bunch of tactical issues here:

  1. You're creating URLSession, but then issuing NSURLConnection request. Just use URLSession.

  2. Your “request dictionary” isn't a dictionary, but rather an array. For example, to issue the JSON-RPC request, the proper format of the dictionary is:

    let requestDictionary: [String: Any] = [
        "jsonrpc" : "2.0",
        "id"      : 1,
        "method"  : "login",
        "params"  : ["myuserid", "mypassword", "mykey"]
    ]
    
  3. Minor issue, but you are using a lot of variables (via var) where a constant (via let) would be fine. In the spirit of Swift’s safety, use let wherever possible.

  4. According to the Lacuna Expanse API, your URL should be including the module name.

    So, for example if doing POST requests in the "Empire" module, the URL is:

    let url = URL(string: "https://us1.lacunaexpanse.com/empire")!
    
  5. You're likely to be doing a lot of requests, so I'd suggest putting the bulk of that in a single function that you can call again and again, without repeating code all over the place. Perhaps a function like the following that takes the following parameters:

    • module (e.g. “empire” vs “alliance”);

    • method (e.g. “login” vs “fetch_captcha”);

    • the parameters appropriate for that request (e.g. for “login”, that would be the “name”, “password”, and the “api_key”); and

    • closure that will be called when the asynchronous request finishes.

    This function then prepares the JSON-RPC request and calls the closure when the request finishes:

    @discardableResult
    func submitLacunaRequest(module: String, method: String, parameters: Any, completion: @escaping (Result<[String: Any], Error>) -> Void) -> URLSessionTask? {
        let session = URLSession.shared
        let url = URL(string: "https://us1.lacunaexpanse.com")!
            .appendingPathComponent(module)
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json-rpc", forHTTPHeaderField: "Content-Type")
    
        let requestDictionary: [String: Any] = [
            "jsonrpc": "2.0",
            "id"     : 1,
            "method" : method,
            "params" : parameters
        ]
    
        request.httpBody = try! JSONSerialization.data(withJSONObject: requestDictionary)
    
        let task = session.dataTask(with: request) { data, response, error in
    
            // handle fundamental network errors (e.g. no connectivity)
    
            guard error == nil, let data = data else {
                completion(.failure(error ?? URLError(.badServerResponse)))
                return
            }
    
            // check that http status code was 200
    
            guard
                let httpResponse = response as? HTTPURLResponse,
                200 ..< 300 ~= httpResponse.statusCode
            else {
                completion(.failure(URLError(.badServerResponse)))
                return
            }
    
            // parse the JSON response
    
            do {
                guard let responseObject = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                    throw URLError(.badServerResponse)
                }
                completion(.success(responseObject))
            } catch let parseError {
                completion(.failure(parseError))
            }
        }
        task.resume()
    
        return task
    }
    

    This does all of the necessary wrapping of the method and parameters within a JSON-RPC request. Then, all you need to do to call that method is something like so:

    submitLacunaRequest(module: "empire", method: "login", parameters: ["myuserid", "mypassword", "mykey"]) { result in
    
        switch result {
        case .failure(let error):
            print("error = \(error)")
    
        case .success(let value):
            if let errorDictionary = value["error"] as? [String: Any] {
                print("error logging in (bad userid/password?): \(errorDictionary)")
            } else if let resultDictionary = value["result"] as? [String: Any] {
                print("successfully logged in, refer to resultDictionary for details: \(resultDictionary)")
            } else {
                print("we should never get here")
                print("responseObject = \(value)")
            }
        }
    }
    

    For a request that requires a dictionary, such as "create", just go ahead and supply the dictionary:

    submitLacunaRequest(module:"empire", method: "create", parameters: [
        "name"      : "user",
        "password"  : "password",
        "password1" : "password",
        "captcha_guid" : "305...dd-....-....-....-e3706...73c0",
        "captcha_solution" : "42",
        "email" : "[email protected]"
    ]) { result in
        switch result {
        case .failure(let error):
            print("error = \(error)")
    
        case .success(let value):
            print("responseObject = \(responseObject)")
        }
    }
    

Clearly, in these above, I am just doing minimal error handling, so you could beef this up, but your question was about issuing POST request, and hopefully the above illustrates how that is done.

For Swift 2 version, see previous revision of this answer.

Upvotes: 10

barndog
barndog

Reputation: 7163

As @jcaron points out, this post is full of bad edits. There's a lot of variables that have different names later in the function and so on. Not to mention you should NEVER post your api key in a SO question or anywhere on the internet for that matter.

To answer your question on to do a post request in Swift, unless you need incredibly granular control over the process, take a look at Alamofire (same guy that wrote AFNetworking). A POST request is as simple as Alamofire.request(.POST, "http://someapiurl.com") You can also pass in a dictionary of body parameters if you so choose.

Upvotes: 2

Related Questions