iOS 網(wǎng)絡(luò)編程實(shí)戰(zhàn)手冊(cè)

本文是我學(xué)習(xí) iOS 網(wǎng)絡(luò)編程整理的筆記,目的在于以后需要用到時(shí)可以直接有代碼示例幫我解決問題。
還在不斷完善當(dāng)中。
歡迎朋友們糾錯(cuò)。

基本網(wǎng)絡(luò)數(shù)據(jù)獲取

分三步走:建立URL,獲取數(shù)據(jù),使用數(shù)據(jù)。

// 1. 建立地址URL,這里是在蘋果官網(wǎng)上隨便找了個(gè)圖片。
// 2. 獲取地址的Data數(shù)據(jù),當(dāng)然也可以是NSString或者其他格式的數(shù)據(jù),但是這里是圖片。因此獲取下來是NSData數(shù)據(jù)。
// 3. 使用數(shù)據(jù),這里直接用data建立UIImage并使用它。只是掩飾個(gè)用法,具體根據(jù)業(yè)務(wù)需求。
let url = NSURL(string: "https://devimages.apple.com.edgekey.net/assets/elements/icons/os-x-10-11-white/os-x-10-11-white-128x128.png")!
let data = NSData(contentsOfURL: url)
self.imageView.image = UIImage(data: data!)

當(dāng)然實(shí)際操作中并不會(huì)像上面這樣來獲取數(shù)據(jù),因?yàn)檫@樣做會(huì)直接在主線程當(dāng)中進(jìn)行網(wǎng)絡(luò)獲取,從而導(dǎo)致線程被堵塞。因此需要加入異步處理。

// 1. 建立地址URL
let url = NSURL(string: "https://devimages.apple.com.edgekey.net/assets/elements/icons/os-x-10-11-white/os-x-10-11-white-128x128.png")!
// 2. 調(diào)用異步線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // 3. 在異步線程中進(jìn)行網(wǎng)絡(luò)請(qǐng)求
    let data = NSData(contentsOfURL: url)
    // 4. 返回主線程(如果不設(shè)置UI操作,其實(shí)也可以不返回,要根據(jù)實(shí)際業(yè)務(wù)決定。)
    dispatch_async(dispatch_get_main_queue()) {
        // 5. 對(duì)獲取的數(shù)據(jù)進(jìn)行操作。
        self.imageView.image = UIImage(data: data!)
    })
}

</br>

NSURLSession


基礎(chǔ)知識(shí)

基本使用步驟:

  1. 建立 URL 地址。
  2. 建立 Request 請(qǐng)求。
  3. 獲取或生成 NSURLSession。
  4. 建立 NSURLSessionTask 任務(wù)。
  5. 開始下載并接收數(shù)據(jù)。

NSURLSessionTask 類型

這里寫圖片描述

NSURLSessionTask 常用操作

var state: NSURLSessionTaskState { get } // 當(dāng)前狀態(tài)

cancel() // 取消
resume() // 恢復(fù)
suspend() // 暫停

Get請(qǐng)求示例

// 1. 建立URL
let url = NSURL(string: "http://www.itdecent.cn")!
// 2. 建立Request
let request = NSURLRequest(URL: url)
// 3. 獲取系統(tǒng)提供的Session
let session = NSURLSession.sharedSession()
// 4. 建立Task
/* Block中提供的三個(gè)參數(shù)分別是
    元數(shù)據(jù);
    響應(yīng)信息(如果是 HTTP 或 HTTPS 的話,這其實(shí)是一個(gè)NSHTTPURLResponse 對(duì)象。);
    錯(cuò)誤信息。*/
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
    // 5. 類型轉(zhuǎn)換
    let httpResponse = response as! NSHTTPURLResponse
    // 6. 判斷是否請(qǐng)求正確
    if httpResponse.statusCode == 200 {
        // 7. 進(jìn)行數(shù)據(jù)處理。如果涉及到UI,需要回調(diào)主線程。這里用webView加載獲取到的HTML數(shù)據(jù)。
        dispatch_async(dispatch_get_main_queue()) {
            let htmlString = String(data: data!, encoding: NSUTF8StringEncoding)
            let webView = UIWebView(frame: self.view.frame)
            webView.loadHTMLString(htmlString!, baseURL: nil)
            self.view.addSubview(webView)
        }
    }
})
// 8. 啟動(dòng)任務(wù)
task.resume()

POST請(qǐng)求示例

let url = NSURL(string: "http://www.itdecent.cn")!
// 與 Get 的不同點(diǎn),使用 NSMutableURLRequest 并根據(jù)具體任務(wù)設(shè)置其屬性。
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = "username=xxxx&pwd=xxxx".dataUsingEncoding(NSStringEncoding.min)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
    let httpResponse = response as! NSHTTPURLResponse
    if httpResponse.statusCode == 200 {
        // ...
    }
})
task.resume()

NSURLSessionDataDelegate 小文件下載示例

// -------  配置部分  ------
let url = NSURL(string: "http://www.itdecent.cn")!
// Get
let request = NSURLRequest(URL: url)
/* Post
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = "username=xxxx&pwd=xxxx".dataUsingEncoding(NSStringEncoding.min) 
*/
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
// 設(shè)置超時(shí)時(shí)長(zhǎng)
configuration.timeoutIntervalForRequest = 10
// 設(shè)置是否允許使用窩蜂網(wǎng)絡(luò)
configuration.allowsCellularAccess = false

let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: NSOperationQueue())
let task = session.dataTaskWithRequest(requsst)

task.resume()
// MARK: - NSURLSessionDataDelegate 常用方法
// 1. 接收到服務(wù)器的響應(yīng),必須給 completionHandler 傳值,才能根據(jù)你傳遞的值繼續(xù)下一步操作。
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
    let httpResponse = response as! NSHTTPURLResponse
    switch httpResponse.statusCode {
    case 200 ..< 300:
        // 作業(yè)繼續(xù)正常進(jìn)行
        completionHandler(NSURLSessionResponseDisposition.Allow)
    case 500 ..< 700:
        // 作業(yè)取消
        completionHandler(NSURLSessionResponseDisposition.Cancel)
    default:
        // 代理會(huì)調(diào)用 URLSession:dataTask:didBecomeDownloadTask: 方法讓你開始一個(gè)下載作業(yè)來代替當(dāng)前通訊作業(yè)。
        completionHandler(NSURLSessionResponseDisposition.BecomeDownload)
    }
}

// 1.* 當(dāng)在 URLSession:dataTask:DidReceiveResponse:completionHandler: 方法中傳入 NSURLSessionResponseDisposition.BecomeDownload 時(shí)會(huì)調(diào)用此代理。用于重置下載作業(yè)。
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
    
}

// 2. 每次接收到服務(wù)器的數(shù)據(jù)就會(huì)調(diào)用并返回?cái)?shù)據(jù)。(將多次被調(diào)用)
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
    // 此處的 data 每次只會(huì)返回當(dāng)前接收到的數(shù)據(jù)。之前已經(jīng)發(fā)送的數(shù)據(jù)就不會(huì)重復(fù)發(fā)送,因此需要另外設(shè)置變量整合數(shù)據(jù)。
    // 由于 NSData 對(duì)象往往是由許多不同的對(duì)象組合而成,因此最好使用 NSData 的 enumerateByteRangesUsingBlock: 來遍歷數(shù)據(jù)。
}

// 3. 請(qǐng)求完成。如果失敗的話,error有值。
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {

}

NSURLSessionDownloadDelegate 大文件下載示例(不支持?jǐn)帱c(diǎn)續(xù)傳)

// -------  配置部分  ------
let url = NSURL(string: "http://www.itdecent.cn")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = "username=xxxx&pwd=xxxx".dataUsingEncoding(NSStringEncoding.min)
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 10
configuration.allowsCellularAccess = false
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: NSOperationQueue())
let task = session.downloadTaskWithRequest(request)
task.resume()
// MARK: - NSURLSessionDownloadDelegate 常用方法

// 1. 已經(jīng)恢復(fù)下載。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
    
}
// 2. 每次寫入數(shù)據(jù)到臨時(shí)文件就會(huì)調(diào)用此方法。totalBytesExpectedToWrite 總大小。totalBytesWritten 總共寫入多少。bytesWritten 本次寫入多少。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    
}
// 3. 下載完畢會(huì)調(diào)用此方法。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
    // 把下載好的零食文件放置到新文件當(dāng)中。
    let newFilePath = NSHomeDirectory()
    try! NSFileManager.defaultManager().moveItemAtURL(location, toURL: NSURL(string: newFilePath)!)
}
// 4. 下載完成
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
}

NSURLSessionDownloadDelegate 大文件下載示例(支持?jǐn)帱c(diǎn)傳送)

由于GitHub不支持?jǐn)帱c(diǎn)傳送,所以這段代碼未驗(yàn)證其正確性。
基本思路如下:

  1. 計(jì)算已經(jīng)下載的文件大小。并通過配置 NSMutableRequest 來下載后續(xù)部分。
  2. 配置 NSURLSessionDataTask。
  3. 配置NSOutputStream 在每次下載到數(shù)據(jù)后就立即保存到本地。
  4. 下載完成后,將文件從臨時(shí)路徑轉(zhuǎn)移到目標(biāo)路徑。
import UIKit

class ViewController: UIViewController, NSURLSessionDataDelegate {

    @IBAction func start(sender: AnyObject) {
        createDownloadTask("https://github.com/huangmubin/GithubFile/archive/master.zip", fileName: "myron.zip")
    }
    @IBAction func stop(sender: AnyObject) {
        task.suspend()
        task.cancel()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print(NSHomeDirectory())
    }
    
    var url: NSURL!
    var request: NSMutableURLRequest!
    var configuration: NSURLSessionConfiguration!
    var session: NSURLSession!
    var task: NSURLSessionDataTask!
    
    var filePath: String!
    
    var fileData: NSMutableData!
    
    var stream: NSOutputStream!
    
    var tmpPath: String!
    
    func createDownloadTask(urlString: String, fileName: String) {
        filePath = "\(NSHomeDirectory())/Library/Caches/\(fileName)"
        tmpPath = "\(NSHomeDirectory())/tmp/\(fileName)"
        stream = NSOutputStream(toFileAtPath: tmpPath, append: true)
        
        // 下載地址
        url = NSURL(string: urlString)!
        request = NSMutableURLRequest(URL: url)
        
        if let fileAttributes = try? NSFileManager.defaultManager().attributesOfItemAtPath(tmpPath) {
            let size = fileAttributes[NSFileSize]!.integerValue
            print(size)
            request.setValue("bytes=\(size)-", forHTTPHeaderField: "Range")
        }
        
        configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: NSOperationQueue())
        
        task = session.dataTaskWithRequest(request)
        
        print("start")
        task.resume()
    }
    
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
        if let httpResponse = response as? NSHTTPURLResponse {
            if httpResponse.statusCode == 200 {
                print("didReceiveResponse - Allow")
                stream.open()
                print(httpResponse.allHeaderFields["Content-Length"]?.integerValue)
                completionHandler(NSURLSessionResponseDisposition.Allow)
                return
            }
        }
        print("didReceiveResponse - Cancel")
        completionHandler(NSURLSessionResponseDisposition.Cancel)
    }
    
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        stream.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
        print("didReceiveData")
    }
    
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        if error == nil {
            print("didCompleteWithError - Ok")
            stream.close()
            stream = nil
            
            do {
                try NSFileManager.defaultManager().moveItemAtPath(tmpPath, toPath: filePath)
            } catch {
                print("File Move Error")
            }
        }
        print("didCompleteWithError - Error")
    }
}

</br>

HTTP 響應(yīng)碼


1字頭:消息
2字頭:成功
3字頭:重定向
4字頭:請(qǐng)求錯(cuò)誤
5、6字頭:服務(wù)器錯(cuò)誤

</br>

錯(cuò)誤處理


App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

原因是在iOS9中,蘋果將原h(huán)ttp協(xié)議改成了https協(xié)議,使用 TLS1.2 SSL加密請(qǐng)求數(shù)據(jù)。

解決方法:在Info.plist中加入關(guān)鍵字NSAppTransportSecurity字典,以及它的NSAllowsArbitraryLoads關(guān)鍵字,選擇YES.
具體顯示為:

這里寫圖片描述

參考資料

百度百科 HTTP 響應(yīng)碼
簡(jiǎn)書作者:zhazha的《NSURLSession》
簡(jiǎn)書作者:華子_24的《NSURLSession》
Wangrui's Blog:NSInputStream 和 NSOutputStream
博客園:HTTP Header 詳解
Steak OverFlow 中關(guān)于 Writing a String to an NSOutputStream in Swift 的討論
王巍的 Swift Tips 中關(guān)于指針的介紹
Apple文檔 URL Session Programming Guide
Apple文檔 NSURLSession Class Reference
Apple文檔 NSURLSessionTask Class Reference
Apple文檔 NSURLSessionDataDelegate Protocol Reference
Apple文檔 NSURLSessionDownloadDelegate Protocol Reference
Apple文檔 NSOutputStream Class Reference
Apple文檔 NSFileManager Class Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容