Alamofire 是目前github上Swift版star最多的一个网络库。网络几乎是任何一个有实用价值的APP都绕不过的话题。Object-C写网络库,看AFNetworking是必须的;同样的,用Swift写网络库,学Alamofire也是必须的。这两者虽然是一个人写的,不过由于语言的差异,两者的实现思路差异很大。
Alamofire.swift
是否对AFNetworking.h还有印象?只要包含这个头文件,AF中的一切都随便用。这个文件的目的同样也是为了方便使用,大多数情况下,只要看这个文件,用这里提供的函数,就能完成任务了。
这个文件只是一个透传薄层,具体的事情由其他类完成。提供了“全局函数”作为调用接口。由于是封装成framework的,所以调用的时候以Alamofire.request()的样式出现,看上去像类方法,但是实际上不是,而是“全局函数”。在实际使用中,一般要import Alamofire,那么上面的调用可以简单地以request()出现,这就是“赤裸裸的全局函数了”。这种处理方式,确实让人脑洞大开,可以省去创建对象的步骤,也可以省去具体工作的类名。当然,为了体现“看上去的面向对象”,每个函数钱都可以带上公共的Alamofire,也不用担心“命名冲突”这个在Object-C中令人头疼的问题。只要能够克服,“对全局函数的恐惧”,这种做法还是有一定的借鉴意义。当然,如果处理不好,还是老老实实地把属性和方法包在一个类中,再让别人调用比较好。
对于普通的Https数据通讯,只要用下面这个函数接口就可以了
/**
Creates a request using the shared manager instance for the specified method, URL string, parameters, and
parameter encoding.
- parameter method: The HTTP method.
- parameter URLString: The URL string.
- parameter parameters: The parameters. `nil` by default.
- parameter encoding: The parameter encoding. `.URL` by default.
- parameter headers: The HTTP headers. `nil` by default.
- returns: The created request.
*/
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
return Manager.sharedInstance.request(
method,
URLString,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
- 文档说明中的一个使用例子,看上去就是链式调用,一直“点”下去。能做到这样,是因为request、validate、responseJSON等函数的返回值都是Request类,而他们本身又是这个类的成员函数。成员函数(比如validate)的返回值是自己所属的类(比如Request),这种思路也是比较巧妙的,值得借鉴。
Alamofire.request(.GET, "https://httpbin.org/get") .validate() .responseJSON { response in debugPrint(response) }
request函数定义中有5个参数,但是例子中只提供了2个。原因是parameters、encoding、headers等参数提供了默认值。对于不常用的参数提供默认值,这也是值得学习的一种做法。
URLString参数的类型是URLStringConvertible,这是一个协议,并且通过扩展系统默认类型,提供了一些默认实现。这样就实现了参数类型的多样化,这种方式值得效仿。
// MARK: - URLStringConvertible
/**
Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to
construct URL requests.
*/
public protocol URLStringConvertible {
/**
A URL that conforms to RFC 2396.
Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808.
See https://tools.ietf.org/html/rfc2396
See https://tools.ietf.org/html/rfc1738
See https://tools.ietf.org/html/rfc1808
*/
var URLString: String { get }
}
extension String: URLStringConvertible {
public var URLString: String {
return self
}
}
extension NSURL: URLStringConvertible {
public var URLString: String {
return absoluteString
}
}
extension NSURLComponents: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
}
extension NSURLRequest: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
}
Manager.swift
- 顶层管理类,提供单例模式。Alamofire.swift中的方便函数都是用的这个类的单例形态。使用静态常量属性的方式来做单例,比较简单。利用{}()结构还能执行一些额外的代码。这个方式值得借鉴。
/**
A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly
for any ad hoc requests.
*/
public static let sharedInstance: Manager = {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
return Manager(configuration: configuration)
}()
- 构造函数,都提供了默认参数。NSURLSessionConfiguration使用了系统默认的,ServerTrustPolicyManager参数是nil。如果不需要修改超时时间,不需要使用自定义的证书文件,那么使用默认的单例就可以了,不需要用到这个构造函数。由于manager是单例,那么NSURLSession就只有一个,整个数据通讯都用这一个,效果就相当于用了NSURLSession sharedSession单例一样。这种处理方法是值得借鉴的。占网络80%左右的数据通讯,用这种单例的方式就可以了。长时间的通讯,归类到Upload或者download,这两种就不要用单例了,用这个构造函数,创建manager的实例,自己在外部再写一个管理类,进行统一处理。
/**
Initializes the `Manager` instance with the specified configuration, delegate and server trust policy.
- parameter configuration: The configuration used to construct the managed session.
`NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
- parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by
default.
- parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
challenges. `nil` by default.
- returns: The new `Manager` instance.
*/
public init(
configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
{
self.delegate = delegate
self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
}
- 数据业务主要是下面的函数完成的。由session和task创建一个自定义的Request对象(这个和NSURLRequest差异很大),来管理具体的事务。
/**
Creates a request for the specified method, URL string, parameters, parameter encoding and headers.
- parameter method: The HTTP method.
- parameter URLString: The URL string.
- parameter parameters: The parameters. `nil` by default.
- parameter encoding: The parameter encoding. `.URL` by default.
- parameter headers: The HTTP headers. `nil` by default.
- returns: The created request.
*/
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
let mutableURLRequest = URLRequest(method, URLString, headers: headers)
let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
return request(encodedURLRequest)
}
/**
Creates a request for the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
- parameter URLRequest: The URL request
- returns: The created request.
*/
public func request(URLRequest: URLRequestConvertible) -> Request {
var dataTask: NSURLSessionDataTask!
dispatch_sync(queue) { dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest) }
let request = Request(session: session, task: dataTask)
delegate[request.delegate.task] = request.delegate
if startRequestsImmediately {
request.resume()
}
return request
}
SessionDelegate是Manager的一个内部类,是一个代理,实现了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate。这是一个比较大的类,数据、上传、下载三项业务的代理实现都是它。这也是一个大的容器类,具体工作由Reques得内部类TaskDelegate来做。在实际使用中,最好不要这样做,要求高了点,耦合度过大。
一个Session可以包含多个Request,一个Request可以包含多个task。task的类型可以是数据、上传、下载中的任意一种。
Request
- 这是一个请求、响应、时间、进度等综合事务管理的一个综合体,功能很全面。也是实现函数链式调用的一个中间体。
// MARK: - Properties
/// The delegate for the underlying task.
public let delegate: TaskDelegate
/// The underlying task.
public var task: NSURLSessionTask { return delegate.task }
/// The session belonging to the underlying task.
public let session: NSURLSession
/// The request sent or to be sent to the server.
public var request: NSURLRequest? { return task.originalRequest }
/// The response received from the server, if any.
public var response: NSHTTPURLResponse? { return task.response as? NSHTTPURLResponse }
/// The progress of the request lifecycle.
public var progress: NSProgress { return delegate.progress }
var startTime: CFAbsoluteTime?
var endTime: CFAbsoluteTime?
- 从构造函数可以看出,task可以是数据、上传、下载中的任意一种
init(session: NSURLSession, task: NSURLSessionTask) {
self.session = session
switch task {
case is NSURLSessionUploadTask:
delegate = UploadTaskDelegate(task: task)
case is NSURLSessionDataTask:
delegate = DataTaskDelegate(task: task)
case is NSURLSessionDownloadTask:
delegate = DownloadTaskDelegate(task: task)
default:
delegate = TaskDelegate(task: task)
}
delegate.queue.addOperationWithBlock { self.endTime = CFAbsoluteTimeGetCurrent() }
}
任务的启动,暂停,取消,是通过其包含的task来完成的。可以把Request看作是对task又包了一层的一个自定义类。不同的Request是用不同的task.taskIdentifier来区分的。
代理TaskDelegate,以及3个子类都是内部类,做具体的代理工作。
Error.swift
这是一个结构体struct
封装了自定义的domain和code
通过提供静态函数,构造NSError对象
Result.swift
这是一个枚举enum,这个案例是enum的经典写法,值得借鉴
分为成功、失败两种case
成功的数据value,失败的error,以附属变量的形式跟在相应的case后面
数据和出错信息采用了泛型
用一些计算属性,方便判断是否成功,获取数据或者失败信息
通过扩展,提供描述信息
/**
Used to represent whether a request was successful or encountered an error.
- Success: The request and all post processing operations were successful resulting in the serialization of the
provided associated value.
- Failure: The request encountered an error resulting in a failure. The associated values are the original data
provided by the server as well as the error that caused the failure.
*/
public enum Result {
case Success(Value)
case Failure(Error)
/// Returns `true` if the result is a success, `false` otherwise.
public var isSuccess: Bool {
switch self {
case .Success:
return true
case .Failure:
return false
}
}
/// Returns `true` if the result is a failure, `false` otherwise.
public var isFailure: Bool {
return !isSuccess
}
/// Returns the associated value if the result is a success, `nil` otherwise.
public var value: Value? {
switch self {
case .Success(let value):
return value
case .Failure:
return nil
}
}
/// Returns the associated error value if the result is a failure, `nil` otherwise.
public var error: Error? {
switch self {
case .Success:
return nil
case .Failure(let error):
return error
}
}
}
// MARK: - CustomStringConvertible
extension Result: CustomStringConvertible {
/// The textual representation used when written to an output stream, which includes whether the result was a
/// success or failure.
public var description: String {
switch self {
case .Success:
return "SUCCESS"
case .Failure:
return "FAILURE"
}
}
}
// MARK: - CustomDebugStringConvertible
extension Result: CustomDebugStringConvertible {
/// The debug textual representation used when written to an output stream, which includes whether the result was a
/// success or failure in addition to the value or error.
public var debugDescription: String {
switch self {
case .Success(let value):
return "SUCCESS: \(value)"
case .Failure(let error):
return "FAILURE: \(error)"
}
}
}
Response.swift
这是一个结构体struct
将NSURLRequest、NSHTTPURLResponse、数据、结果、时间等组合到在一起。相当于一个容器。
Notifications.swift
这是一个结构体struct
作用相当于与宏定义 #define,比宏方便好用。这种方式值得借鉴
/// Contains all the `NSNotification` names posted by Alamofire with descriptions of each notification's payload.
public struct Notifications {
/// Used as a namespace for all `NSURLSessionTask` related notifications.
public struct Task {
/// Notification posted when an `NSURLSessionTask` is resumed. The notification `object` contains the resumed
/// `NSURLSessionTask`.
public static let DidResume = "com.alamofire.notifications.task.didResume"
/// Notification posted when an `NSURLSessionTask` is suspended. The notification `object` contains the
/// suspended `NSURLSessionTask`.
public static let DidSuspend = "com.alamofire.notifications.task.didSuspend"
/// Notification posted when an `NSURLSessionTask` is cancelled. The notification `object` contains the
/// cancelled `NSURLSessionTask`.
public static let DidCancel = "com.alamofire.notifications.task.didCancel"
/// Notification posted when an `NSURLSessionTask` is completed. The notification `object` contains the
/// completed `NSURLSessionTask`.
public static let DidComplete = "com.alamofire.notifications.task.didComplete"
}
}
ParameterEncoding.swift
这是一个枚举enum
输入参数编码方式
ResponseSerialization.swift
这是返回结果处理的地方
主要是对Request类的扩展;这种写法在Swift中比较普遍;可以借鉴(文件名和类名毫不相关)
// MARK: - JSON
extension Request {
/**
Creates a response serializer that returns a JSON object constructed from the response data using
`NSJSONSerialization` with the specified reading options.
- parameter options: The JSON serialization reading options. `.AllowFragments` by default.
- returns: A JSON object response serializer.
*/
public static func JSONResponseSerializer(
options options: NSJSONReadingOptions = .AllowFragments)
-> ResponseSerializer
{
return ResponseSerializer { _, response, data, error in
guard error == nil else { return .Failure(error!) }
if let response = response where response.statusCode == 204 { return .Success(NSNull()) }
guard let validData = data where validData.length > 0 else {
let failureReason = "JSON could not be serialized. Input data was nil or zero length."
let error = Error.error(code: .JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
do {
let JSON = try NSJSONSerialization.JSONObjectWithData(validData, options: options)
return .Success(JSON)
} catch {
return .Failure(error as NSError)
}
}
}
/**
Adds a handler to be called once the request has finished.
- parameter options: The JSON serialization reading options. `.AllowFragments` by default.
- parameter completionHandler: A closure to be executed once the request has finished.
- returns: The request.
*/
public func responseJSON(
queue queue: dispatch_queue_t? = nil,
options: NSJSONReadingOptions = .AllowFragments,
completionHandler: Response -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: Request.JSONResponseSerializer(options: options),
completionHandler: completionHandler
)
}
}
Validation.swift
对Request类的功能扩展
函数又返回Request类本身,实现链式调用
验证网络传输本身是否正常
// MARK: - Automatic
/**
Validates that the response has a status code in the default acceptable range of 200...299, and that the content
type matches any specified in the Accept HTTP header field.
If validation fails, subsequent calls to response handlers will have an associated error.
- returns: The request.
*/
public func validate() -> Self {
let acceptableStatusCodes: Range = 200..<300
let acceptableContentTypes: [String] = {
if let accept = request?.valueForHTTPHeaderField("Accept") {
return accept.componentsSeparatedByString(",")
}
return ["*/*"]
}()
return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
}
其他文件
MultipartFormData.swift 多表单数据拼接后通过POST上传
Download.swift下载
Upload.swift 上传
NetworkReachabilityManager.swift检查是否有网络
ServerTrustPolicy.swift 网络安全策略
Stream.swift流的方式
基本上是通过扩展Manager和Request这两个类完成相应功能