Http Requests in Swift 3

HTTP Requests in Swift 3

There are a couple problems with your code:

  1. By default, your app cannot connect to insecure (i.e. HTTP) site. It's a feature called App Transport Security. You need to make an exception in your app's Info.plist file to connect to HTTP sites.
  2. This: dataTask(urlwith: ! as URL). What are you trying to unwrap with the exclamation mark (!)? What's the variable name?

A lot of class names have changed between Swift 2 and 3 so those answers you've found may not be applicable. Below is an example that connects to httpbin.org to get your IP address:

import PlaygroundSupport
import Foundation

let url = URL(string: "https://httpbin.org/ip")

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

let json = try! JSONSerialization.jsonObject(with: data, options: [])
print(json)
}

task.resume()
PlaygroundPage.current.needsIndefiniteExecution = true

how i can make a HTTP request with Swift 3?

The completion handler's type needs to be something like this:

@escaping ({argument types...})->{result type}

@escaping is needed as the completion handler is executed later when the communication is completed.

{argument types...} needs to be the types you want to pass to the handler, so in your case, a single type String. And you usually do not use the result from the handler, so you need to specify Void (aka ()).

Thus your completion handler's type needs to be:

@escaping (String)->Void

So, your method header becomes:

(You know you need a closing parenthesis for argument list.)

func makeRequest(request: URLRequest, completion: @escaping (String)->Void)

Whole your method would be something like this:

func makeRequest(request: URLRequest, completion: @escaping (String)->Void) {
let task = URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else{
print("error=\(error)")
return
}

if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
print(data as NSData) //<-`as NSData` is useful for debugging
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
//Why don't you use decoded JSON object? (`json` may not be a `String`)
} catch {
print("error serializing JSON: \(error)")
}
//Not sure what you mean with "i need to return the json as String"
let responseString = String(data: data, encoding: .utf8) ?? ""
completion(responseString)
}
task.resume()
}

You can use it as:

    makeRequest(request: request) {response in //<-`response` is inferred as `String`, with the code above.
print(response)
}

what am I doing wrong Swift 3 http request and response

I never used this exact procedure with tasks but rather use the methods with callback. I am not sure if in the background there should be much of a difference though.

So to generate the session (seems pretty close to your):

let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)

Then I generate the request which stupidly enough needs an URL in the constructor:

var request = URLRequest(url: URL(string: "www.nil.com")!) // can't initialize without url
request.url = nil

Adding url with query parameters (you can just set the URL in your case, I have a tool to handle a few cases):

   fileprivate func injectQueryParameters(request: inout URLRequest) {
if let query = queryParameters.urlEncodedString {
let toReturn = endpoint.url + "?" + query
if let url = URL(string: toReturn) {
request.url = url
} else {
print("Cannot prepare url: \(toReturn)")
}
} else {
let toReturn = endpoint.url
if let url = URL(string: toReturn) {
request.url = url
} else {
print("Cannot prepare url: \(toReturn)")
}
}
}

Then the form parameters. We mostly use JSON but anything goes here:

    fileprivate func injectFormParameters( request: inout URLRequest) {
if let data = rawFormData {
request.httpBody = data
} else if let data = formParameters.urlEncodedString?.data(using: .utf8) {
request.httpBody = data
}
}

And the headers:

    fileprivate func injectHeaders(request: inout URLRequest) {
headers._parameters.forEach { (key, value) in
if let stringValue = value as? String {
request.setValue(stringValue, forHTTPHeaderField: key)
}
}
}

So in the end the whole call looks something like:

    class func performRequest(request: URLRequest, callback: (([String: Any]?, NSError?) -> Void)?) {
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)

let task = session.dataTask(with: request) { data, response, error in
// Response is sent here
if let data = data {
callback?((try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as [String: Any]?, error)
} else {
callback?(nil, error)
}
}
task.resume()
}

I hope this puts you on the right track. In general you do have a few open source libraries you might be interested in. Alamofire is probably still used in most cases.

HTTP request in swift 3 Xcode 8.3

Using Block GET/POST/PUT/DELETE:

 let request = NSMutableURLRequest(url: URL(string: "Your API URL here" ,param: param))!,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval:"Your request timeout time in Seconds")
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers as? [String : String]

let session = URLSession.shared

let dataTask = session.dataTask(with: request as URLRequest) {data,response,error in
let httpResponse = response as? HTTPURLResponse

if (error != nil) {
print(error)
} else {
print(httpResponse)
}

DispatchQueue.main.async {
//Update your UI here
}

}
dataTask.resume()

I think you dont mention line
request.httpMethod = "GET"

How to make HTTP request in Swift?

You can use URL, URLRequest and URLSession or NSURLConnection as you'd normally do in Objective-C. Note that for iOS 7.0 and later, URLSession is preferred.

Using URLSession

Initialize a URL object and a URLSessionDataTask from URLSession. Then run the task with resume().

let url = URL(string: "http://www.stackoverflow.com")!

let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}

task.resume()

Using NSURLConnection

First, initialize a URL and a URLRequest:

let url = URL(string: "http://www.stackoverflow.com")!
var request = URLRequest(url: url)
request.httpMethod = "POST"

Then, you can load the request asynchronously with:

NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main) {(response, data, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}

Or you can initialize an NSURLConnection:

let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true)

Just make sure to set your delegate to something other than nil and use the delegate methods to work with the response and data received.

For more detail, check the documentation for the NSURLConnectionDataDelegate protocol

Testing on an Xcode playground

If you want to try this code on a Xcode playground, add import PlaygroundSupport to your playground, as well as the following call:

PlaygroundPage.current.needsIndefiniteExecution = true

This will allow you to use asynchronous code in playgrounds.



Related Topics



Leave a reply



Submit