相信写过Swift的人应该都知道Alamofire,它是AFNetworking的Swift版本,同一个作者写的。之前在项目中我也一直使用Alamofire,但是升级到Xcode7之后旧版的Alamofire不能用了,最新版的又只支持iOS8之后的系统,而公司项目还得兼容iOS7,所以接下来不打算用它了。
我的需求比较简单,只要能发送GET请求获取数据以及发送POST请求提交数据就好了,大致看了一下Alamofire的源码又上网查了点资料之后,花了不到半天写了几个简单的函数,项目又能正常跑起来了。
iOS7之后的系统都支持NSURLSession
,我们就把它稍微封装一下好了。
func getDataFrom(urlString: String, method: HTTPMethod, parameter: [String: String]?, completionHandler: Callback) throws { // let config = NSURLSessionConfiguration.defaultSessionConfiguration() // config.timeoutIntervalForRequest = 20 // let session = NSURLSession(configuration: config) guard let url = NSURL(string: urlString) else { throw Error.InvalidURL } let session = NSURLSession.sharedSession() let request = NSMutableURLRequest(URL: url) request.HTTPMethod = method.rawValue switch method { case .POST: //如果参数为nil或者字典中没有元素,则抛出异常 guard let param = parameter else { throw Error.NoParameter } guard param.isEmpty else { throw Error.NoParameter } request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept") do { request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(param, options: []) } catch { print(error) } case .GET: break } let task = session.dataTaskWithRequest(request) {data, response, error in guard let result = data where error == nil else { printLog("no data: \(error)") return } completionHandler(data: result) } //启动 task.resume() }
这个函数声明的时候在->
前加了一个throws
,表明这个函数是可以抛出异常的。其实以往iOS开发比较推崇"Let it crash!"的哲学,不过Swift一直很强调安全性,Apple显然也并不仅仅满足于让Swift困守iOS开发领域,加上早就公布了年底要开源,大家也很期待它作为一门通用编程语言在其他领域的作为。从各方面来看,Swift2.0增加了对异常处理的支持都在情理之中。从此你的App就不能轻易的狗带了~
ha~.jpg
我对异常处理的理解很浅薄,说实话平常自己也不怎么喜欢用。在我看来异常处理最重要的用途有两点:
写底层框架的时候可以抛出一些异常让框架的使用者去处理,这样框架会显得更加灵活。
保存错误日志,便于查询和调试。
像我上面那个函数,如果纯粹是自己用的话,其实我会选择在出错的地方直接处理错误或者打印错误信息,譬如把throw Error.InvalidURL
改成
printLog("Invalid URL")return
这样也省得调用函数的时候一堆try-catch
。当然有些错误当前函数确实是处理不了,那该抛还得抛。
上面那个函数还可以封装一下,分成两个,一个用来发送 GET
请求接收JSON
数据,一个用来POST
JSON
数据并接收返回信息。具体如下:
func getJsonFrom(url: String, completion: (json: JSON) -> Void) { do { try getDataFrom(url, method: HTTPMethod.GET, parameter: nil) { data in let json = JSON(data: data) //主线程进行UI操作 dispatch_sync(dispatch_get_main_queue()) { completion(json: json) } } } catch Error.InvalidURL { printLog("GET: invalid url") } catch { printLog("Unknown error") } } func postJson(dict: [String: String], toUrl url: String, completion: (json: JSON) -> Void) { do { try getDataFrom(url, method: HTTPMethod.POST, parameter: dict) { data in let json = JSON(data: data) //主线程进行UI操作 dispatch_sync(dispatch_get_main_queue()) { completion(json: json) } } } catch Error.InvalidURL { printLog("POST: invalid url") } catch Error.NoParameter { printLog("Parameter is empty") } catch { printLog("Unknown error") } }
完整代码在这里,里面还有一个图片缓存的函数,有兴趣的话可以看看。如果跟我有同样需求的同学可以把HttpManager.swift
clone下来直接拖到项目里,建议配合SwiftyJSON(一个很好用的第三方JSON解析库)使用,直接把Source文件夹里的SwiftyJSON.swift
这个文件也一起拖到项目中好了,要用Cocoapods导入framework的话似乎只能支持iOS8之后的系统了。
对了还有一点,我一开始用JSON(data: data)
来初始化JSON
数据的时候总是不成功,于是我看了下SwiftyJSON中JSON
这个struct
的构造函数,它先调用了苹果提供的class func JSONObjectWithData(_ data: NSData, options opt: NSJSONReadingOptions) throws -> AnyObject
函数,然后把返回的AnyObject
对象赋值给自身属性object
:
public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) { do { let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt) self.init(object) } catch let aError as NSError { if error != nil { error.memory = aError } self.init(NSNull()) } }public init(_ object: AnyObject) { self.object = object}
opt
这个参数有三个可选值:MutableContainers
, MutableLeaves
, AllowFragments
,分别表示:
可以把数组或者字典转化成可变对象;
可以把JSON对象树中作为叶子节点的字符串转化成可变字符串;
允许解析最外层对象不是NSArray
或NSDictionary
实例的JSON
数据
三种我都试了,都不行,最后我试了下[]
,也就是传入一个空值,居然行了。于是我索性把JSON
的初始化函数改了:
public init(data:NSData, options opt: NSJSONReadingOptions = [], error: NSErrorPointer = nil) { //其余不变