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


当前回答

我已经完成了HTTP请求两种方法GET和POST与JSON解析的方式:

在viewDidLoad ():

override func viewDidLoad() {
super.viewDidLoad()

    makeGetRequest()
    makePostRequest()

}

func makePostRequest(){

    let urlPath: String = "http://www.swiftdeveloperblog.com/http-post-example-script/"
    var url: NSURL = NSURL(string: urlPath)!
    var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)

    request.HTTPMethod = "POST"
    var stringPost="firstName=James&lastName=Bond" // Key and Value

    let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)

    request.timeoutInterval = 60
    request.HTTPBody=data
    request.HTTPShouldHandleCookies=false

    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
        let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary

         if (jsonResult != nil) {
            // Success
           println(jsonResult)

           let message = jsonResult["Message"] as! NSString

           println(message)
         }else {
            // Failed
            println("Failed")
        }

    })

}

func makeGetRequest(){
    var url : String = "http://api.androidhive.info/contacts/"
    var request : NSMutableURLRequest = NSMutableURLRequest()
    request.URL = NSURL(string: url)
    request.HTTPMethod = "GET"
    request.timeoutInterval = 60

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
        let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary

        if (jsonResult != nil) {
            // Success
            println(jsonResult)

            let dataArray = jsonResult["contacts"] as! NSArray;

            for item in dataArray { // loop through data items

                let obj = item as! NSDictionary

                for (key, value) in obj {

                    println("Key: \(key) - Value: \(value)")

                    let phone = obj["phone"] as! NSDictionary;

                    let mobile = phone["mobile"] as! NSString
                    println(mobile)
                    let home = phone["home"] as! NSString
                    println(home)
                    let office = phone["office"] as! NSString
                    println(office)
                }
            }

        } else {
            // Failed
            println("Failed")
        }

    })
}

Done

其他回答

另一个选择是Alamofire库,它提供了可链请求/响应方法。

https://github.com/Alamofire/Alamofire

提出请求

import Alamofire

Alamofire.request(.GET, "http://httpbin.org/get")

响应处理

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
         .response { request, response, data, error in
              print(request)
              print(response)
              print(error)
          }

你可以使用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

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

细节

Xcode 9.2, Swift 4 Xcode 10.2.1 (10E1001)

Info.plist

NSApp传输安全

添加到info plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Alamofire样本

Alamofire

import Alamofire

class AlamofireDataManager {
    fileprivate let queue: DispatchQueue
    init(queue: DispatchQueue) { self.queue = queue }

    private func createError(message: String, code: Int) -> Error {
        return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
    }

    private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((Result<[String: Any]>) -> Void)?) {
        Alamofire.request(request).responseJSON { response in
            let complete: (Result<[String: Any]>) ->() = { result in DispatchQueue.main.async { closure?(result) } }
            switch response.result {
                case .success(let value): complete(.success(value as! [String: Any]))
                case .failure(let error): complete(.failure(error))
            }
        }
    }

    func searchRequest(term: String, closure: ((Result<[String: Any]>) -> Void)?) {
        guard let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))") else { return }
        let request = URLRequest(url: url)
        make(request: request) { response in closure?(response) }
    }
}

Alamofire样品的使用

private lazy var alamofireDataManager = AlamofireDataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
//.........

alamofireDataManager.searchRequest(term: "jack johnson") { result in
      print(result.value ?? "no data")
      print(result.error ?? "no error")
}

URLSession样本

import Foundation

class DataManager {

    fileprivate let queue: DispatchQueue
        init(queue: DispatchQueue) { self.queue = queue }

    private func createError(message: String, code: Int) -> Error {
        return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
    }

    private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
        let task = session.dataTask(with: request) { [weak self] data, response, error in
            self?.queue.async {
                let complete: (_ json: [String: Any]?, _ error: Error?) ->() = { json, error in DispatchQueue.main.async { closure?(json, error) } }

                guard let self = self, error == nil else { complete(nil, error); return }
                guard let data = data else { complete(nil, self.createError(message: "No data", code: 999)); return }

                do {
                    if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                        complete(json, nil)
                    }
                } catch let error { complete(nil, error); return }
            }
        }

        task.resume()
    }

    func searchRequest(term: String, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
        let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
        let request = URLRequest(url: url!)
        make(request: request) { json, error in closure?(json, error) }
    }
}

URLSession示例的使用

private lazy var dataManager = DataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
// .......
dataManager.searchRequest(term: "jack johnson") { json, error  in
      print(error ?? "nil")
      print(json ?? "nil")
      print("Update views")
}

结果

下面是在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) }

答:吻

URLSession.shared.dataTask(with: URL(string: "https://google.com")!) {(data, response, error) in
    print(String(data: data!, encoding: .utf8))
}.resume()