我在iBooks上读了苹果的编程语言Swift,但不知道如何在Swift中发出HTTP请求(类似cURL)。我需要导入Obj-C类还是只需要导入默认库?或者不能基于原生Swift代码进行HTTP请求?


当前回答

你可以使用Just,一个python-requests风格的HTTP库。

使用Just发送HTTP请求的示例:

// synchronous GET request with URL query a=1
let r = Just.get("https://httpbin.org/get", params:["a":1])

// asynchronous POST request with form value and file uploads
Just.post(
    "http://justiceleauge.org/member/register",
    data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
    files: ["profile_photo": .URL(fileURLWithPath:"flash.jpeg", nil)]
) { (r)
    if (r.ok) { /* success! */ }
}

在这两种情况下,可以通过类似于python-request的方式访问请求r的结果:

r.ok            // is the response successful?
r.statusCode    // status code of response
r.content       // response body as NSData?
r.text          // response body as text?
r.json          // response body parsed by NSJSONSerielization

你可以在这个操场上找到更多的例子

在操场上以同步模式使用这个库是Swift中最接近cURL的东西。

其他回答

Swift 4及以上:使用URLSession API进行数据请求

   // create the url
   let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! //change the url
        
   // create the session object 
   let session = URLSession.shared
        
   // now create the URLRequest object using the url object
   let request = URLRequest(url: url)
        
   // create dataTask using the session object to send data to the server
   let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            
       guard error == nil else {
           return
       }
            
       guard let data = data else {
           return
       }
            
      do {
         //create json object from data
         if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
            print(json)
         }
      } catch let error {
        print(error.localizedDescription)
      }
   })

   task.resume()

Swift 4及以上,可解码和结果enum

// APIError enum which shows all possible Network errors
enum APIError: Error {
    case networkError(Error)
    case dataNotFound
    case jsonParsingError(Error)
    case invalidStatusCode(Int)
    case badURL(String)
}

// Result enum to show success or failure
enum Result<T> {
    case success(T)
    case failure(AppError)
}

// dataRequest which sends request to given URL and convert to Decodable Object
func dataRequest<T: Decodable>(with url: String, objectType: T.Type, completion: @escaping (Result<T>) -> Void) {
    
    // create the url with NSURL
    guard let dataURL = URL(string: url) else {
       completion(.failure(APIError.badURL(url))
       return
    }
    
    // create the session object
    let session = URLSession.shared
    
    // now create the URLRequest object using the url object
    let request = URLRequest(url: dataURL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
    
    // create dataTask using the session object to send data to the server
    let task = session.dataTask(with: request, completionHandler: { data, response, error in
        
        guard error == nil else {
            completion(Result.failure(APIError.networkError(error!)))
            return
        }
        
        guard let data = data else {
            completion(Result.failure(AppError.dataNotFound))
            return
        }
        
        do {
            // create decodable object from data
            let decodedObject = try JSONDecoder().decode(objectType.self, from: data)
            completion(Result.success(decodedObject))
        } catch let error {
            completion(Result.failure(APIError.jsonParsingError(error as! DecodingError)))
        }
    })
    
    task.resume()
}

例子:

//如果我们想从占位符API获取todo,那么我们定义todo结构体并调用dataRequest并传递“https://jsonplaceholder.typicode.com/todos/1”字符串url。

struct ToDo: Decodable {
    let id: Int
    let userId: Int
    let title: String
    let completed: Bool
    
}

dataRequest(with: "https://jsonplaceholder.typicode.com/todos/1", objectType: ToDo.self) { (result: Result) in
    switch result {
    case .success(let object):
        print(object)
    case .failure(let error):
        print(error)
    }
}

//输出结果:

ToDo(id: 1, userId: 1, title: "delectus aut autem", completed: false)

下面是在Linux上使用Swift进行HTTP请求的从头到尾的说明。

首先创建一个SwiftPM包

mkdir swift-http && cd swift-http && swift package init --type executable

然后替换。/Sources/swift-http/main.swift 代码如下:

import Foundation
import FoundationNetworking

let sema = DispatchSemaphore(value: 0)

URLSession.shared.dataTask(with: URL(string: "http://numbersapi.com/42")!) {(data, response, error) in
    print(String(data: data!, encoding: .utf8) ?? String(describing: error))
    sema.signal()
}.resume()

sema.wait()

然后运行代码

swift run

输出的例子:

[6/6] Build complete!
42 is the answer to the Ultimate Question of Life, the Universe, and Everything.

注意:使用DispatchSemaphore是为了使程序在得到响应之前不会退出。

你也可以这样做:

import Foundation
import FoundationNetworking

var done = false

URLSession.shared.dataTask(with: URL(string: "http://numbersapi.com/42")!) {(data, response, error) in
    print(String(data: data!, encoding: .utf8) ?? String(describing: error))
    done = true
}.resume()

while !done { Thread.sleep(forTimeInterval: 1) }

下面给出了一个“GET”请求示例。

let urlString = "YOUR_GET_URL"
let yourURL = URL(string: urlstring)
let dataTask = URLSession.shared.dataTask(with: yourURL) { (data, response, error) in
do {
    let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
    print("json --- \(json)")
    }catch let err {
    print("err---\(err.localizedDescription)")
    }
   }
dataTask.resume()

你可以使用URL URLRequest URLSession或者NSURLConnection就像你在Objective-C中通常做的那样。注意,对于iOS 7.0及更高版本,URLSession是首选。

使用URLSession

初始化URL对象和URLSession中的URLSessionDataTask。然后使用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()

使用NSURLConnection

首先,初始化URL和URLRequest:

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

然后,你可以异步加载请求:

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

或者你可以初始化一个NSURLConnection:

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

只是要确保将委托设置为nil以外的东西,并使用委托方法处理接收到的响应和数据。

要了解更多细节,请查看NSURLConnectionDataDelegate协议的文档

在Xcode操场上测试

如果你想在Xcode游乐场上尝试这段代码,添加import PlaygroundSupport到你的游乐场,以及下面的调用:

PlaygroundPage.current.needsIndefiniteExecution = true

这将允许你在操场上使用异步代码。

你可以使用Just,一个python-requests风格的HTTP库。

使用Just发送HTTP请求的示例:

// synchronous GET request with URL query a=1
let r = Just.get("https://httpbin.org/get", params:["a":1])

// asynchronous POST request with form value and file uploads
Just.post(
    "http://justiceleauge.org/member/register",
    data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
    files: ["profile_photo": .URL(fileURLWithPath:"flash.jpeg", nil)]
) { (r)
    if (r.ok) { /* success! */ }
}

在这两种情况下,可以通过类似于python-request的方式访问请求r的结果:

r.ok            // is the response successful?
r.statusCode    // status code of response
r.content       // response body as NSData?
r.text          // response body as text?
r.json          // response body parsed by NSJSONSerielization

你可以在这个操场上找到更多的例子

在操场上以同步模式使用这个库是Swift中最接近cURL的东西。