iOS 網絡編程實戰(zhàn)手冊

本文是我學習 iOS 網絡編程整理的筆記忍捡,目的在于以后需要用到時可以直接有代碼示例幫我解決問題形葬。
還在不斷完善當中擦秽。
歡迎朋友們糾錯码荔。

基本網絡數據獲取

分三步走:建立URL,獲取數據感挥,使用數據缩搅。

// 1. 建立地址URL,這里是在蘋果官網上隨便找了個圖片触幼。
// 2. 獲取地址的Data數據硼瓣,當然也可以是NSString或者其他格式的數據,但是這里是圖片置谦。因此獲取下來是NSData數據堂鲤。
// 3. 使用數據,這里直接用data建立UIImage并使用它媒峡。只是掩飾個用法瘟栖,具體根據業(yè)務需求。
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!)

當然實際操作中并不會像上面這樣來獲取數據谅阿,因為這樣做會直接在主線程當中進行網絡獲取半哟,從而導致線程被堵塞酬滤。因此需要加入異步處理。

// 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. 調用異步線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // 3. 在異步線程中進行網絡請求
    let data = NSData(contentsOfURL: url)
    // 4. 返回主線程(如果不設置UI操作寓涨,其實也可以不返回敏晤,要根據實際業(yè)務決定。)
    dispatch_async(dispatch_get_main_queue()) {
        // 5. 對獲取的數據進行操作缅茉。
        self.imageView.image = UIImage(data: data!)
    })
}

</br>

NSURLSession


基礎知識

基本使用步驟:

  1. 建立 URL 地址嘴脾。
  2. 建立 Request 請求。
  3. 獲取或生成 NSURLSession蔬墩。
  4. 建立 NSURLSessionTask 任務译打。
  5. 開始下載并接收數據。

NSURLSessionTask 類型

這里寫圖片描述

NSURLSessionTask 常用操作

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

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

Get請求示例

// 1. 建立URL
let url = NSURL(string: "http://www.reibang.com")!
// 2. 建立Request
let request = NSURLRequest(URL: url)
// 3. 獲取系統(tǒng)提供的Session
let session = NSURLSession.sharedSession()
// 4. 建立Task
/* Block中提供的三個參數分別是
    元數據拇颅;
    響應信息(如果是 HTTP 或 HTTPS 的話奏司,這其實是一個NSHTTPURLResponse 對象。)樟插;
    錯誤信息韵洋。*/
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
    // 5. 類型轉換
    let httpResponse = response as! NSHTTPURLResponse
    // 6. 判斷是否請求正確
    if httpResponse.statusCode == 200 {
        // 7. 進行數據處理。如果涉及到UI黄锤,需要回調主線程搪缨。這里用webView加載獲取到的HTML數據。
        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. 啟動任務
task.resume()

POST請求示例

let url = NSURL(string: "http://www.reibang.com")!
// 與 Get 的不同點鸵熟,使用 NSMutableURLRequest 并根據具體任務設置其屬性副编。
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.reibang.com")!
// 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()
// 設置超時時長
configuration.timeoutIntervalForRequest = 10
// 設置是否允許使用窩蜂網絡
configuration.allowsCellularAccess = false

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

task.resume()
// MARK: - NSURLSessionDataDelegate 常用方法
// 1. 接收到服務器的響應,必須給 completionHandler 傳值流强,才能根據你傳遞的值繼續(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ù)正常進行
        completionHandler(NSURLSessionResponseDisposition.Allow)
    case 500 ..< 700:
        // 作業(yè)取消
        completionHandler(NSURLSessionResponseDisposition.Cancel)
    default:
        // 代理會調用 URLSession:dataTask:didBecomeDownloadTask: 方法讓你開始一個下載作業(yè)來代替當前通訊作業(yè)。
        completionHandler(NSURLSessionResponseDisposition.BecomeDownload)
    }
}

// 1.* 當在 URLSession:dataTask:DidReceiveResponse:completionHandler: 方法中傳入 NSURLSessionResponseDisposition.BecomeDownload 時會調用此代理打月。用于重置下載作業(yè)队腐。
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
    
}

// 2. 每次接收到服務器的數據就會調用并返回數據。(將多次被調用)
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
    // 此處的 data 每次只會返回當前接收到的數據奏篙。之前已經發(fā)送的數據就不會重復發(fā)送柴淘,因此需要另外設置變量整合數據。
    // 由于 NSData 對象往往是由許多不同的對象組合而成报破,因此最好使用 NSData 的 enumerateByteRangesUsingBlock: 來遍歷數據悠就。
}

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

}

NSURLSessionDownloadDelegate 大文件下載示例(不支持斷點續(xù)傳)

// -------  配置部分  ------
let url = NSURL(string: "http://www.reibang.com")!
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. 已經恢復下載。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
    
}
// 2. 每次寫入數據到臨時文件就會調用此方法盹靴。totalBytesExpectedToWrite 總大小炸茧。totalBytesWritten 總共寫入多少瑞妇。bytesWritten 本次寫入多少。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    
}
// 3. 下載完畢會調用此方法梭冠。
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
    // 把下載好的零食文件放置到新文件當中辕狰。
    let newFilePath = NSHomeDirectory()
    try! NSFileManager.defaultManager().moveItemAtURL(location, toURL: NSURL(string: newFilePath)!)
}
// 4. 下載完成
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
}

NSURLSessionDownloadDelegate 大文件下載示例(支持斷點傳送)

由于GitHub不支持斷點傳送,所以這段代碼未驗證其正確性控漠。
基本思路如下:

  1. 計算已經下載的文件大小蔓倍。并通過配置 NSMutableRequest 來下載后續(xù)部分。
  2. 配置 NSURLSessionDataTask盐捷。
  3. 配置NSOutputStream 在每次下載到數據后就立即保存到本地偶翅。
  4. 下載完成后,將文件從臨時路徑轉移到目標路徑碉渡。
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 響應碼


1字頭:消息
2字頭:成功
3字頭:重定向
4字頭:請求錯誤
5聚谁、6字頭:服務器錯誤

</br>

錯誤處理


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加密請求數據形导。

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

這里寫圖片描述

參考資料

百度百科 HTTP 響應碼
簡書作者:zhazha的《NSURLSession》
簡書作者:華子_24的《NSURLSession》
Wangrui's Blog:NSInputStream 和 NSOutputStream
博客園:HTTP Header 詳解
Steak OverFlow 中關于 Writing a String to an NSOutputStream in Swift 的討論
王巍的 Swift Tips 中關于指針的介紹
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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末朵耕,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子序愚,更是在濱河造成了極大的恐慌憔披,老刑警劉巖等限,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爸吮,死亡現場離奇詭異,居然都是意外死亡望门,警方通過查閱死者的電腦和手機形娇,發(fā)現死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筹误,“玉大人桐早,你說我怎么就攤上這事〕簦” “怎么了哄酝?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祷膳。 經常有香客問我陶衅,道長,這世上最難降的妖魔是什么直晨? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任搀军,我火速辦了婚禮膨俐,結果婚禮上,老公的妹妹穿的比我還像新娘罩句。我一直安慰自己焚刺,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布门烂。 她就那樣靜靜地躺著乳愉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屯远。 梳的紋絲不亂的頭發(fā)上匾委,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音氓润,去河邊找鬼赂乐。 笑死,一個胖子當著我的面吹牛咖气,可吹牛的內容都是我干的挨措。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼崩溪,長吁一口氣:“原來是場噩夢啊……” “哼浅役!你這毒婦竟也來了?” 一聲冷哼從身側響起伶唯,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤觉既,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后乳幸,有當地人在樹林里發(fā)現了一具尸體瞪讼,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年粹断,在試婚紗的時候發(fā)現自己被綠了符欠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓶埋,死狀恐怖希柿,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情养筒,我是刑警寧澤曾撤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站晕粪,受9級特大地震影響挤悉,放射性物質發(fā)生泄漏。R本人自食惡果不足惜兵多,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一尖啡、第九天 我趴在偏房一處隱蔽的房頂上張望橄仆。 院中可真熱鬧,春花似錦衅斩、人聲如沸盆顾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽您宪。三九已至,卻和暖如春奠涌,著一層夾襖步出監(jiān)牢的瞬間宪巨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工溜畅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捏卓,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓慈格,卻偏偏與公主長得像怠晴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子浴捆,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容

  • NSURLSession 使用步驟使用NSURLSession對象創(chuàng)建Task蒜田,然后執(zhí)行Task -(void)g...
    BEYOND黃閱讀 900評論 0 0
  • iOS開發(fā)系列--網絡開發(fā) 概覽 大部分應用程序都或多或少會牽扯到網絡開發(fā),例如說新浪微博选泻、微信等冲粤,這些應用本身可...
    lichengjin閱讀 3,644評論 2 7
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現页眯,斷路器梯捕,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • 網絡 http請求方式? 通常餐茵,HTTP的請求方式有3種科阎,分別是:POST、GET忿族、HEAD。POST和GET方法...
    b485c88ab697閱讀 7,220評論 1 36
  • 使用NSURLConnection實現下載 1. 小文件下載 第一種方式(NSData) 第二種方式(NSURLC...
    擱淺的青蛙閱讀 1,946評論 3 10