本文是我學習 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
基礎知識
基本使用步驟:
- 建立 URL 地址嘴脾。
- 建立 Request 請求。
- 獲取或生成 NSURLSession蔬墩。
- 建立 NSURLSessionTask 任務译打。
- 開始下載并接收數據。
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不支持斷點傳送,所以這段代碼未驗證其正確性控漠。
基本思路如下:
- 計算已經下載的文件大小蔓倍。并通過配置 NSMutableRequest 來下載后續(xù)部分。
- 配置 NSURLSessionDataTask盐捷。
- 配置NSOutputStream 在每次下載到數據后就立即保存到本地偶翅。
- 下載完成后,將文件從臨時路徑轉移到目標路徑碉渡。
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