How do I perform GET and POST requests in Swift?
Asked Answered
M

8

5

I adapted this from Ray Wenderlich's iOS Apprentice tutorial part 4.

This code works as a GET request sent to my Strongloop API with a simple database model, however:

  1. This works, but I don't know why it works, since it invokes no method that I can see to actually send the request.

  2. I see no means to make it into a POST request.

My question is: How do I perform a POST request? Is it done in a completely different way?

Let me know if you need more information.

    class ViewController: UIViewController {


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

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

    @IBAction func getFromDB() {
        let url = urlWithSearchText("")
        println("URL: '\(url)'")
        if let jsonString = performGetRequestWithURL(url) {
            println("Received JSON string '\(jsonString)'")
        }
    }


    func urlWithSearchText(searchText: String) -> NSURL {
        let escapedSearchText = searchText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        let urlString = String(format: "http://localhost:3000/api/Tests", escapedSearchText)
        let url = NSURL(string: urlString)
        return url!
    }    

    func performGetRequestWithURL(url: NSURL) -> String? {
        var error: NSError?
        if let resultString = String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error) {
            return resultString
        } else if let error = error {
            println("Download Error: \(error)")
        } else {
            println("Unknown Download Error")
        }
        return nil
    }

Here is a picture of this working:

https://dl.dropboxusercontent.com/u/14464971/Images/Messages%20Image%281477993527%29.png

Marked answered 26/6, 2015 at 16:29 Comment(0)
L
4

Below are two POST methods. Depending on if you want it synchronous (everything else waits until the post method is completed) or asynchronous (POST method runs in background, other methods run in parallel).

Methods

// POST data to url
func postDataAsynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ()) {
    var URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST";
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){

        response, data, error in

        var output: String!

        if data != nil {
            output = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
        }

        completionHandler(responseString: output, error: error)
    }
}

// Obtain the data
func postDataSynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ())
{
    let URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST"
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
    request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

    var response: NSURLResponse?
    var error: NSError?

    // Send data
    let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)

    var output: String! // Default to nil

    if data != nil{
        output =  NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
    }

    completionHandler(responseString: output, error: error)

}

Using them

You can then call (use) them like so:

postDataSynchronous(url, bodyData: bodyData) {
    responseString, error in

        if error != nil {
            println("Error during post: \(error)")
            return
        }
        else{
            //Success
            println(responseString)
            userType = responseString // Set usertype based on server response
        }        
    }

SWIFT 2.0

func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {

    // Indicate download
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    let url = NSURL(string: url)!
    //        print("URL: \(url)")
    let request = NSMutableURLRequest(URL: url)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    // Verify downloading data is allowed
    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
    } catch let error as NSError {
        print("Error in request post: \(error)")
        request.HTTPBody = nil
    } catch {
        print("Catch all error: \(error)")
    }

    // Post the data
    let task = session.dataTaskWithRequest(request) { data, response, error in
        completionHandler(data: data, response: response, error: error)

        // Stop download indication
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false // Stop download indication

    }

    task.resume()

}
Lentz answered 26/6, 2015 at 17:14 Comment(4)
This is very helpful, but let's say the bodyData is { "name": "Professor Dumbledore", "type": "Wizard" } How would I go about turning that into a string? It's got a lot of quotes in it but I'm not sure how to format it as a string.Marked
Scratch that last comment, I just figured it out: "name=Professor Dumbledore&Type=Wizard"Marked
Happy to see you figured it out - happy coding and a good weekend to you!Lentz
@FelipeCaldas See answer aboveLentz
C
7

Swift 3 & above

GET

 func getRequest() {
    
    // request url
    let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! // change the url
    
    // create URLSession with default configuration
    let session = URLSession.shared
    
    // create dataTask using the session object to send data to the server
    let task = session.dataTask(with: url) { data, response, error in
        
        if let error = error {
            print("GET Request Error: \(error.localizedDescription)")
            return
        }
        
        // ensure there is valid response code returned from this HTTP response
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
                  print("Invalid Response received from the server")
                  return
              }
        
        // ensure there is data returned
        guard let responseData = data else {
            print("nil Data received from the server")
            return
        }
        
        do {
            // serialise the data object into Dictionary [String : Any]
            if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
                print(jsonResponse)
            } else {
                print("data maybe corrupted or in wrong format")
                throw URLError(.badServerResponse)
            }
        } catch let error {
            print("JSON Parsing Error: \(error.localizedDescription)")
        }
    }
    // resume the task
    task.resume()
}

POST

func postRequest() {
    
    // declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
    
    let parameters: [String: Any] = ["name": "abc", "password": "password@123"]
    
    // create the url with URL
    let url = URL(string: "http://myServerName.com/api")! //change the url
    
    // create the session object
    let session = URLSession.shared
    
    // now create the URLRequest object using the url object
    var request = URLRequest(url: url)
    request.httpMethod = "POST" //set http method as POST
    
    // add headers for the request
    request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    
    do {
        // convert parameters to Data and assign dictionary to httpBody of request
        request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
    } catch let error {
        print(error.localizedDescription)
        return
    }
    
    // create dataTask using the session object to send data to the server
    let task = session.dataTask(with: request) { data, response, error in
        
        if let error = error {
            print("Post Request Error: \(error.localizedDescription)")
            return
        }
        
        // ensure there is valid response code returned from this HTTP response
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode)
        else {
            print("Invalid Response received from the server")
            return
        }
        
        // ensure there is data returned
        guard let responseData = data else {
            print("nil Data received from the server")
            return
        }
        
        do {
            // create json object from data or use JSONDecoder to convert to Model stuct
            if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
                print(jsonResponse)
                // handle json response
            } else {
                print("data maybe corrupted or in wrong format")
                throw URLError(.badServerResponse)
            }
        } catch let error {
            print(error.localizedDescription)
        }
    }
    task.resume()
}
Caveat answered 11/12, 2016 at 4:6 Comment(1)
Thanks for sharing Suhit! I strongly recommend putting this as a generic function with url and parameters being passed to it. Can then have handler so developer can handle exceptions/ data when function is called. Will update my answer to contain the same.Lentz
L
4

Below are two POST methods. Depending on if you want it synchronous (everything else waits until the post method is completed) or asynchronous (POST method runs in background, other methods run in parallel).

Methods

// POST data to url
func postDataAsynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ()) {
    var URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST";
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){

        response, data, error in

        var output: String!

        if data != nil {
            output = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
        }

        completionHandler(responseString: output, error: error)
    }
}

// Obtain the data
func postDataSynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ())
{
    let URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST"
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
    request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

    var response: NSURLResponse?
    var error: NSError?

    // Send data
    let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)

    var output: String! // Default to nil

    if data != nil{
        output =  NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
    }

    completionHandler(responseString: output, error: error)

}

Using them

You can then call (use) them like so:

postDataSynchronous(url, bodyData: bodyData) {
    responseString, error in

        if error != nil {
            println("Error during post: \(error)")
            return
        }
        else{
            //Success
            println(responseString)
            userType = responseString // Set usertype based on server response
        }        
    }

SWIFT 2.0

func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {

    // Indicate download
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    let url = NSURL(string: url)!
    //        print("URL: \(url)")
    let request = NSMutableURLRequest(URL: url)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    // Verify downloading data is allowed
    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
    } catch let error as NSError {
        print("Error in request post: \(error)")
        request.HTTPBody = nil
    } catch {
        print("Catch all error: \(error)")
    }

    // Post the data
    let task = session.dataTaskWithRequest(request) { data, response, error in
        completionHandler(data: data, response: response, error: error)

        // Stop download indication
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false // Stop download indication

    }

    task.resume()

}
Lentz answered 26/6, 2015 at 17:14 Comment(4)
This is very helpful, but let's say the bodyData is { "name": "Professor Dumbledore", "type": "Wizard" } How would I go about turning that into a string? It's got a lot of quotes in it but I'm not sure how to format it as a string.Marked
Scratch that last comment, I just figured it out: "name=Professor Dumbledore&Type=Wizard"Marked
Happy to see you figured it out - happy coding and a good weekend to you!Lentz
@FelipeCaldas See answer aboveLentz
D
2
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }

    let session = URLSession.shared
    session.dataTask(with: url) { (data, response, error) in
        if let response = response {
            print(response)
        }

        if let data = data {
            print(data)
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: [])
                print(json)
            } catch {
                print(error)
            }

        }
    }.resume()
}

It's a get method.

Degauss answered 12/3, 2018 at 2:24 Comment(0)
D
1

This method invokes the http request.

String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error) 

Because Swift String has no init signature like this.

This method would be written somewhere in the project, as extension of String

It would be something like this

extension String{
    init(contentsOfURL: NSURL, encoding: NSUTF8StringEncoding, inout error: NSError){
        // load data from url
        self = //parse data to string
    }
}
Duntson answered 26/6, 2015 at 16:54 Comment(3)
I have no extension written for String though. Is that built into the SDK?Marked
Any idea how to make a POST request with that?Marked
You can do it. see this #26365414Duntson
S
1

The String(contentsOfUrl:encoding:error) initializer makes a GET request under the hood and returns the content as a string with the specified encoding.

One way to make a request would be to create an NSURLConnection and use NSMutablrURLRequest set the HTTP method the post. With the NSMutableURLRequest, you can create a NSURLConnection and start it immediately with a delegate or you can call NSURLConnection.sendSynchronousRequest or NSURLConnection.sendAsynchronousRequest to send the request.

Skean answered 26/6, 2015 at 17:20 Comment(0)
D
1
let parameters = ["username": "@Bipin_kumar", "tweet": "HelloWorld"]

guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody

let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
    if let response = response {
        print(response)
    }

    if let data = data {
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            print(json)
        } catch {
            print(error)
        }
    }

}.resume()

It's a post method.

Degauss answered 12/3, 2018 at 2:29 Comment(0)
O
1

GET Request

 func getRequest(with url: URL, callback: @escaping (Any?) -> Swift.Void) -> Void {

    let defaultConfigObject = URLSessionConfiguration.default
    defaultConfigObject.timeoutIntervalForRequest = 30.0
    defaultConfigObject.timeoutIntervalForResource = 60.0

    let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)

    var urlRequest = URLRequest(url: url as URL)
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")

    urlRequest.httpMethod = "GET"
    session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in

        guard    let httpResponse: HTTPURLResponse = response as? HTTPURLResponse
            else {
                print("Error: did not receive data")
                return
              }

        var response : (Any)? = nil

        if httpResponse.statusCode == 200 {
            print(httpResponse)

            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }

            do {
                let responseData = try JSONSerialization.jsonObject(with: responseData, options: [JSONSerialization.ReadingOptions.allowFragments])

                  response = responseData
                 callback(response)
            }
            catch _ as NSError {

                let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
                callback(responseString)
                return
            }
        }
        else {
            print(httpResponse)

            guard error == nil else {
                print("error calling GET on /todos/1")
                print(error ?? "error")
                callback(response!)
                return
            }

        }
    }).resume()
}

POST REQUEST

//MARK: post request
func postRequest(with url:URL, postBody:String, callback: @escaping (Any?) -> Void) -> Void {

    let defaultConfigObject = URLSessionConfiguration.default
    defaultConfigObject.timeoutIntervalForRequest = 30.0
    defaultConfigObject.timeoutIntervalForResource = 60.0

    let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)

    let params: String! = postBody

    var urlRequest = URLRequest(url: url as URL)
    urlRequest.httpMethod = "POST"

    let data = params.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
    urlRequest.httpBody = data

    session.dataTask(with: urlRequest, completionHandler: { (data, urlResponse, error) in


        guard let httpResponse:HTTPURLResponse = urlResponse as? HTTPURLResponse
            else{
                print("did not get any data")
                return
            }
        var response : (Any)? = nil

        if httpResponse.statusCode == 200 {

            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }

            do {
                guard let responseData = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
                    print("error trying to convert data to JSON")
                    return
                }

                response = responseData
                callback(response)
            } catch _ as NSError {

                let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
                callback(responseString)
                return
            }
        }

        else {
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error ?? "error")
            callback(nil)
            return
        }
    }
    }).resume()
}

Always try to check the HTTPURLResponse code

Ossifrage answered 17/5, 2018 at 11:55 Comment(0)
C
0

STEP: #1: APIRequest.swift

//
//  APIRequest.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

enum HTTPMethod: String {
    case GET
    case POST
    case PUT
    case DELETE
}

struct APIRequest {
    let url: URL
    let method: HTTPMethod
    let headers: [String: String]?
    let queryParams: [String: Any]?
    let body: Data?
}

STEP: #2: URLSessionApiClient.swift

//
//  URLSessionApiClient.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

final class URLSessionApiClient: NetworkClient {
    
    private let configuration: URLSessionConfiguration
    private let session: URLSession
    
    init() {
        self.configuration = URLSessionConfiguration.default
        self.configuration.timeoutIntervalForRequest = 30.0
        self.configuration.httpAdditionalHeaders = ["Content-Type": "application/json"]
        
        self.session = URLSession(configuration: self.configuration)
    }
    
    private func prepareURL(_ api: APIRequest) -> URL? {
        var urlComponents = URLComponents(string: api.url.absoluteString)
        let queryItems = api.queryParams?.map({ (key, value) in
            return URLQueryItem(name: key, value: String(describing: value) )
        })
        urlComponents?.queryItems = queryItems
        return urlComponents?.url
    }
    
    func dataTask<T: Codable>(_ api: APIRequest, onCompletion: @escaping (_ result: Result<T, Error>) -> Void) {
        
        guard let url = prepareURL(api) else {
            return onCompletion(.failure(AppError.invalidURL))
        }
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = api.method.rawValue
        urlRequest.allHTTPHeaderFields = api.headers
        urlRequest.httpBody = api.body
        
        self.session.dataTask(with: urlRequest) { data, response, error in
            
            // onFailure
            if let err = error {
                onCompletion(.failure(err))
                return
            }
            
            // Validation
            guard (200...299).contains((response as? HTTPURLResponse)?.statusCode ?? 0) else {
                onCompletion(.failure(AppError.httpFailure))
                return
            }
            
            // onSuccess
            if let data = data {
                // Transform Data to Codable Type
                if let userModel = try? JSONDecoder().decode(T.self, from: data) {
                    onCompletion(.success(userModel))
                } else {
                    onCompletion(.failure(AppError.decodingError))
                }
            } else {
                onCompletion(.failure(AppError.noHttpBody))
            }
        }.resume()
    }
}

Step : #3: AppError.swift

//
//  AppError.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

enum AppError: Error {
    case invalidURL
    case noHttpBody
    case httpFailure
    case decodingError
    case error(Error)
    case custom(String)
}

Step: #4: NetworkClient.swift

//
//  NetworkClient.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

protocol NetworkClient {
    func dataTask<T: Codable>(_ api: APIRequest,
                              onCompletion: @escaping (_ result: Result<T, Error>) -> Void)
}

Step: #5: ViewController.Swift

var client: NetworkClient = URLSessionApiClient()
 
 func getRequest() {
     let postsAPI = URL(string: "https://url/posts")!
     let apiRequest = APIRequest(url: postsAPI, method: .GET, headers: nil, queryParams: nil, body: nil)
     
     client.dataTask(apiRequest) { (_ result: Result<PostModel, Error>) in
         switch result {
         case .failure(let error):
             print(error)
         case .success(let data):
             print("Data: \(data)")
         }
     }
 }
 
 func postRequest() {
     let newPost = PostModel(userId: 1234, id: 1234, title: "My Title", body: "This is Body")
     let newPostData = try? JSONEncoder().encode(newPost)
     
     let postsAPI = URL(string: "https://url/posts")!
     let apiRequest = APIRequest(url: postsAPI, method: .POST, headers: ["Content-Type":"application/json"], queryParams: nil, body: newPostData)
     client.dataTask(apiRequest) { (_ result: Result<PostModel, Error>) in
         switch result {
         case .failure(let error):
             print(error)
         case .success(let data):
             print("Data: \(data)")
         }
     }
 }
Controversy answered 4/5, 2023 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.