搜索
您的当前位置:首页正文

iOS 网络编程实战手册

来源:二三娱乐

本文是我学习 iOS 网络编程整理的笔记,目的在于以后需要用到时可以直接有代码示例帮我解决问题。
还在不断完善当中。
欢迎朋友们纠错。

基本网络数据获取

分三步走:建立URL,获取数据,使用数据。

// 1. 建立地址URL,这里是在苹果官网上随便找了个图片。
// 2. 获取地址的Data数据,当然也可以是NSString或者其他格式的数据,但是这里是图片。因此获取下来是NSData数据。
// 3. 使用数据,这里直接用data建立UIImage并使用它。只是掩饰个用法,具体根据业务需求。
let url = NSURL(string: 
let data = NSData(contentsOfURL: url)
self.imageView.image = UIImage(data: data!)

当然实际操作中并不会像上面这样来获取数据,因为这样做会直接在主线程当中进行网络获取,从而导致线程被堵塞。因此需要加入异步处理。

// 1. 建立地址URL
let url = NSURL(string: 
// 2. 调用异步线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // 3. 在异步线程中进行网络请求
    let data = NSData(contentsOfURL: url)
    // 4. 返回主线程(如果不设置UI操作,其实也可以不返回,要根据实际业务决定。)
    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 } // 当前状态

cancel() // 取消
resume() // 恢复
suspend() // 暂停

Get请求示例

// 1. 建立URL
let url = NSURL(string: 
// 2. 建立Request
let request = NSURLRequest(URL: url)
// 3. 获取系统提供的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: 
// 与 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: 
// 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 传值,才能根据你传递的值继续下一步操作。
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
    let httpResponse = response as! NSHTTPURLResponse
    switch httpResponse.statusCode {
    case 200 ..< 300:
        // 作业继续正常进行
        completionHandler(NSURLSessionResponseDisposition.Allow)
    case 500 ..< 700:
        // 作业取消
        completionHandler(NSURLSessionResponseDisposition.Cancel)
    default:
        // 代理会调用 URLSession:dataTask:didBecomeDownloadTask: 方法让你开始一个下载作业来代替当前通讯作业。
        completionHandler(NSURLSessionResponseDisposition.BecomeDownload)
    }
}

// 1.* 当在 URLSession:dataTask:DidReceiveResponse:completionHandler: 方法中传入 NSURLSessionResponseDisposition.BecomeDownload 时会调用此代理。用于重置下载作业。
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask) {
    
}

// 2. 每次接收到服务器的数据就会调用并返回数据。(将多次被调用)
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
    // 此处的 data 每次只会返回当前接收到的数据。之前已经发送的数据就不会重复发送,因此需要另外设置变量整合数据。
    // 由于 NSData 对象往往是由许多不同的对象组合而成,因此最好使用 NSData 的 enumerateByteRangesUsingBlock: 来遍历数据。
}

// 3. 请求完成。如果失败的话,error有值。
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {

}

NSURLSessionDownloadDelegate 大文件下载示例(不支持断点续传)

// -------  配置部分  ------
let url = NSURL(string: 
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 来下载后续部分。
  2. 配置 NSURLSessionDataTask。
  3. 配置NSOutputStream 在每次下载到数据后就立即保存到本地。
  4. 下载完成后,将文件从临时路径转移到目标路径。
import UIKit

class ViewController: UIViewController, NSURLSessionDataDelegate {

    @IBAction func start(sender: AnyObject) {
         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>

错误处理


解决方法:在Info.plist中加入关键字NSAppTransportSecurity字典,以及它的NSAllowsArbitraryLoads关键字,选择YES.
具体显示为:

这里写图片描述

参考资料

Top