Alamofire 网络请求

前言

  • Alamofire 是 Swift 语言的 HTTP 网络开发工具包,相当于 Swift 实现 AFNetworking 版本。当然,AFNetworking 非常稳定,在 Mac OSX 与 iOS 中也能像其他 Objective-C 代码一样用 Swift 编写。不过 Alamofire 更适合 Swift 语言风格习惯(Alamofire 与 AFNetworking 可以共存一个项目中,互不影响)。Alamofire 取名来源于 Alamo Fire flower。

  • Alamofire 的核心主要是试图简化 iOS 中 HTTP 网络连接,它通过使用 NSURLSession 以及 Foundation URL Loading System 来创建一个 Swift 本地的网络访问接口,从而实现令人难以置信效率的任务。

1、Alamofire

  • Alamofire 功能:

    • Chainable Request / Response methods
    • URL / JSON / plist Parameter Encoding
    • Upload File / Data / Stream
    • Download using Request or Resume data
    • Authentication with NSURLCredential
    • Progress Closure & NSProgress
    • cURL Debug Output
  • Alamofire 系统需求:

Alamofire Version Minimum iOS Target Target Notes
3.4.x iOS 8.0+ Xcode 7.3+ is required.
3.1.4 -> 3.3.1 iOS 8.0+ Xcode 7.2+ is required.
3.1.0 -> 3.1.3 iOS 8.0+ Xcode 7.1+ is required.
2.0.0 -> 3.0.1 iOS 8.0+ Xcode 7.0+ is required.
1.3.0 -> 1.3.1 iOS 7.0+ Xcode 6.4 is required.
1.2.1 -> 1.2.3 iOS 7.0+ Xcode 6.3 is required.
1.1.0 -> 1.2.0 iOS 7.0+ Xcode 6.1 is required.
1.0.0 -> 1.0.1 iOS 7.0+ Xcode 6.0 is required. For Xcode 6.1, use the xcode-6.1 branch.
  • Alamofire 有许多让程序猿信服去使用它的理由。在 iOS 开发中,使用 NURLSession 是 HTTP 网络的未来趋势, 相比 NSURLConnection 来说,它的功能更加丰富:

    • 后台上传和下载
    • 暂停以及重新开始网络操作的能力
    • 可配置的容器(Container)
    • 子类和私有存储
    • 改进的认证处理
    • 对每个基础连接进行身份验证
    • 多种代理模式 – NSURLConnection 拥有异步代码块的基本方法, 但是不能用它们的代理,NSURLSession 具有一种混合型的方法。
  • 对 AFNetworking 能做而 Alamofire 不能做的有以下几点:

    • UIKit 扩展
    • TLS 验证
    • NSOperation/NSURLConnection/AFURLConnectionOperation 调用
    • 多重 HTTP 网络请求构架

2、Alamofire 的添加

  • Github 网址:Alamofire

  • Alamofire 使用 ARC

  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 将第三方库文件复制到工程目录下
    Alamofire

    // 将第三方库文件中的 xcodeproj 添加到工程中
    Alamofire.xcodeproj

    // 在 TARGETS -> General -> Embedded Binaries 下添加静态库文件(添加上边的)
    Alamofire.framework

    // 添加头文件
    import Alamofire

3、Alamofire 的设置

  • Swift

    • 请求超时时间设置

      1
      2
      3
      4
      5
      6
      7
      8
      // 必须设置为全局的
      var alamofireManager: Manager!

      let config = NSURLSessionConfiguration.defaultSessionConfiguration()
      config.timeoutIntervalForRequest = 5 // 秒

      self.alamofireManager = Manager(configuration: config)
      self.alamofireManager.request(.GET, "http://120.25.226.186:32812/video?type=JSON")
    • HTTP 方法(Medthods)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      Alamofire.Method enum 列表出在 RFC 2616 中定义的 HTTP 方法:

      public enum Method: String {

      case OPTIONS = "OPTIONS"
      case GET = "GET"
      case HEAD = "HEAD"
      case POST = "POST"
      case PUT = "PUT"
      case PATCH = "PATCH"
      case DELETE = "DELETE"
      case TRACE = "TRACE"
      case CONNECT = "CONNECT"
      }

      这些值可以作为 Alamofire.request 请求的第一个参数。

      Alamofire.request(.POST, "https://httpbin.org/post")

      Alamofire.request(.PUT, "https://httpbin.org/put")

      Alamofire.request(.DELETE, "https://httpbin.org/delete")
    • 请求参数编码方式设置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      Alamofire 使用 Alamofire.ParameterEncoding 可以支持 URL query/URI form,JSONPropertyList 方式编码参数。

      enum ParameterEncoding {

      case URL
      case URLEncodedInURL
      case JSON
      case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions)
      case Custom((URLRequestConvertible, [String : AnyObject]?) -> (NSMutableURLRequest, NSError?))

      public func encode(URLRequest: URLRequestConvertible, parameters: [String : AnyObject]?) -> (NSMutableURLRequest, NSError?)
      public func queryComponents(key: String, _ value: AnyObject) -> [(String, String)]
      public func escape(string: String) -> String
      }

      // URL 形式参数编码

      // 发送以下 HttpBody 内容: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3

      let urlStr:URLStringConvertible = "https://httpbin.org/post"
      let parameters:[String: AnyObject]? = ["foo":"bar", "baz":["a", 1], "qux":["x":1, "y":2, "z":3]]

      Alamofire.request(.POST, urlStr, parameters: parameters) // 默认编码方式是 URL

      Alamofire.request(.POST, urlStr, parameters: parameters, encoding: .URL)

      // JSON 形式参数编码

      // 发送以下 HttpBody 内容: {"foo":"bar", "baz":["a", 1], "qux":{"x":1, "y":2, "z":3}}

      let urlStr:URLStringConvertible = "https://httpbin.org/post"
      let parameters:[String: AnyObject]? = ["foo":"bar", "baz":["a", 1], "qux":["x":1, "y":2, "z":3]]

      Alamofire.request(.POST, urlStr, parameters: parameters, encoding:.JSON)

      // URLRequest 请求编码

      let url = NSURL(string: "https://httpbin.org/get")!
      var urlRequest = NSMutableURLRequest(URL: url)

      let param = ["foo": "bar"]
      let encoding = Alamofire.ParameterEncoding.URL

      (urlRequest, _) = encoding.encode(urlRequest, parameters: param)
    • 请求头设置

      1
      2
      3
      4
      5
      6
      let headers = ["User-Agent":"iPhone 6s Plus"]

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON", headers: headers)

      // 不设置时为默认值
      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")
    • 请求数据响应格式设置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      Built-in Response Methods

      response()
      responseData()
      responseString(encoding:NSStringEncoding)
      responseJSON(options:NSJSONReadingOptions)
      responsePropertyList(options:NSPropertyListReadOptions)

      可以同时响应多种格式数据。

      // 响应 NSData 格式数据

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。
      */
      }

      // 响应 String 格式数据

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      .responseString { (response:Response<String, NSError>) in

      /*
      网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 String 格式。
      或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。
      */

      response.request // original URL request
      response.response // URL response
      response.data // server data
      response.result // result of response serialization

      // 获取并判断结果值

      if let string = response.result.value {

      } else {

      }

      // 判断结果值

      if response.result.error == nil {

      } else {

      }

      // 判断结果值

      switch response.result {

      case.Success(let value):
      case.Failure(let error):
      }
      }

      // 响应 JSON 格式数据

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      .responseJSON { (response:Response<AnyObject, NSError>) in

      /*
      网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 JSON 格式。
      或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。
      */
      }

      // 响应 PList 格式数据

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      .responsePropertyList { (response:Response<AnyObject, NSError>) in

      /*
      网络请求结束,成功时 response.result.error == nil。请求返回的数据存在 response.result.value 中,为 PList 格式。
      或者成功时 response.result 的值为 .Success,失败时 response.result 的值为 .Failure。
      */
      }

      // 响应 多种格式 数据

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      // 参数不使用时可以省略
      .response { (_, _, responseData:NSData?, error:NSError?) in

      }

      .responseJSON { (response:Response<AnyObject, NSError>) in

      }
    • Request 请求创建方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      // Manager 方式

      // 必须设置为全局的
      var alamofireManager: Manager!

      self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

      self.alamofireManager.request(.GET, "http://120.25.226.186:32812/video?type=JSON")

      // Alamofire 方式,接收返回值

      let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      // 不接收返回值,request 不带参数

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

      // request 带参数

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"])

      // request 带参数及参数编码方式

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"], encoding: .URL)

      // request 带参数及请求头

      Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"],
      encoding: .URL,
      headers: ["User-Agent":"iPhone 6s"])
    • 请求任务创建方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      // 数据请求 request (GET/POST)

      // Alamofire GET 方式

      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML"

      Alamofire.request(.GET, urlStr)

      // Alamofire POST 方式

      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video"

      Alamofire.request(.GET, urlStr, parameters: ["type": "XML"])

      // Manager GET 方式

      // 必须设置为全局的
      var alamofireManager: Manager!

      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML"

      self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

      self.alamofireManager.request(.GET, urlStr)

      // Manager POST 方式

      // 必须设置为全局的
      var alamofireManager: Manager!

      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video"

      self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

      self.alamofireManager.request(.GET, urlStr, parameters: ["type": "XML"])

      // 文件下载 download

      // 指定文件路径方式

      Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4")
      { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in

      /*
      设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename
      为服务器端文件名。此 block 在子线程中执行。
      */

      return documentsDirUrl
      }

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      /*
      监听文件下载进度,此 block 在子线程中执行。
      */
      }

      .response { (_, _, _, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。
      */
      }

      // 使用默认提供的下载路径方式

      Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4", destination: destination)

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      /*
      监听文件下载进度,此 block 在子线程中执行。
      */
      }

      .response { (_, _, _, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。
      */
      }

      // 断点续传下载方式

      let downloadRequest:Request = Alamofire.download(resumeData: resumeData,
      destination: { (temporaryURL:NSURL,
      response:NSHTTPURLResponse) -> NSURL in

      /*
      设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename
      为服务器端文件名。此 block 在子线程中执行。
      */

      return documentsDirUrl
      })

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      /*
      监听文件下载进度,此 block 在子线程中执行。
      */
      }

      .response { (_, _, data:NSData?, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。
      */
      }

      // 文件上传 upload

      // Data 形式上传

      Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", headers: headers, data: formBody)

      .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in

      /*
      监听文件上传进度,此 block 在子线程中执行。
      */
      }

      .response { (_, _, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束。
      */
      }

      // MultipartFormData 形式上传

      Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload",
      multipartFormData: { (formData:MultipartFormData) in

      /*
      添加参数。第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。
      设置上传的文件。第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。
      */

      }) { (encodingResult:Manager.MultipartFormDataEncodingResult) in

      /*
      数据编码完成。
      */

      switch encodingResult {

      // 编码成功
      case .Success(let uploadRequest, _, _):

      uploadRequest

      .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in

      /*
      监听文件上传进度,此 block 在子线程中执行。
      */
      }

      .response { (_, _, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束。
      */
      }

      // 编码失败
      case .Failure(let error):

      print(error)
      }
      }
    • 请求任务设置

      1
      2
      3
      4
      5
      6
      7
      8
      // 继续请求任务
      downloadRequest.resume()

      // 暂停请求任务
      downloadRequest.suspend()

      // 取消请求任务
      downloadRequest.cancel()
    • 文件下载设置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 设置文件下载路径

      let documentsDirUrl:NSURL = NSFileManager.defaultManager()
      .URLsForDirectory( .DocumentDirectory, inDomains: .UserDomainMask)[0]
      .URLByAppendingPathComponent(response.suggestedFilename!)

      if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) {

      // 移除已经存在的文件,在 Swift 中文件已经存在时,再次相同路径写入会失败
      try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl)
      }

      // 设置文件默认下载路径

      let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)

      // 监听文件下载进度

      bytesWrite // 本次写入的大小
      totalBytesWrite // 已经写入的大小
      totalBytesExpectedToWrite // 总大小

      // 设置下载进度条
      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true)
    • 文件上传设置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      // Data 形式上传

      let boundary = "myBoundary"

      // 设置请求头
      /*
      upload task 不会在请求头里添加 content-type (上传数据类型)字段,@"myBoundary" 为请求体边界,参数可以随便设置,但需一致
      */
      let headers = ["Content-Type":"multipart/form-data; charset=utf-8; boundary=\(boundary)"]

      // 设置请求文件参数

      let formBody = NSMutableData()

      // 参数开始分割线
      /*
      每个参数开始前都需要加
      */
      formBody.appendData("--\(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 参数
      formBody.appendData("Content-Disposition: form-data; name=\"\("username")\"\r\n\r\n\("jhq")"
      .dataUsingEncoding(NSUTF8StringEncoding)!)

      // username 是后台规定的参数名,jhq 是需要添加的参数内容值
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件开始分割线
      /*
      每个文件开始前都需要加
      */
      formBody.appendData("--\(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件参数名
      formBody.appendData("Content-Disposition: form-data; name=\"\("file")\"; filename=\"\("test.mp4")\""
      .dataUsingEncoding(NSUTF8StringEncoding)!)

      // file 是后台规定的参数名,test.mp4 为上传后服务器端文件名称
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件的类型
      formBody.appendData("Content-Type: mp4".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 待上传文件数据
      /*
      本地待上传的文件路径
      */
      formBody.appendData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 结束分割线标记
      formBody.appendData("--\(boundary)--".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 指定文件路径形式上传
      /*
      public func appendBodyPart(fileURL fileURL: NSURL, name: String, fileName: String, mimeType: String);

      第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。
      */
      let fileUrl = NSBundle.mainBundle().URLForResource("HQ_0005", withExtension: "jpg")!
      formData.appendBodyPart(fileURL: fileUrl, name: "file", fileName: "test.png", mimeType: "image/jpeg")

      // 指定文件数据形式上传
      /*
      public func appendBodyPart(data data: NSData, name: String, fileName: String, mimeType: String);

      第一个参数是需要上传的文件数据,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。
      */
      let fileData = NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)
      formData.appendBodyPart(data: fileData!, name: "file", fileName: "test.mp4", mimeType: "mp4")

      // 添加参数
      /*
      public func appendBodyPart(data data: NSData, name: String);

      第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。
      */
      formData.appendBodyPart(data: "jhq".dataUsingEncoding(NSUTF8StringEncoding)!, name: "username")

      // 监听文件上传进度

      bytesLoad // 本次写入的大小
      totalBytesLoad // 已经写入的大小
      totalBytesExpectedToLoad // 总大小

      let progressNum1:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum1, waitUntilDone: true)

4、Alamofire HTTP 认证

  • 支持以下几种认证:

    • HTTP Basic
    • HTTP Digest
    • Kerberos
    • NTLM
  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // Http basic 方式认证

    let user = "user"
    let password = "password"

    Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")

    .authenticate(user: user, password: password)

    // NSURLCredential 方式认证

    let user = "user"
    let password = "password"

    let credential = NSURLCredential(user: user, password: password, persistence: .ForSession)

    Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")

    .authenticate(usingCredential: credential)

    // headers 方式认证

    let user = "user"
    let password = "password"

    let credentialData = "\(user):\(password)".dataUsingEncoding(NSUTF8StringEncoding)!
    let base64Credentials = credentialData.base64EncodedStringWithOptions([])

    let headers = ["Authorization": "Basic \(base64Credentials)"]

    Alamofire.request(.GET, "https://httpbin.org/basic-auth/user/password", headers: headers)

5、Alamofire HTTP 响应状态信息识别

  • Swift

    • 手动识别

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      /*
      Alamofire 还提供了 HTTP 响应状态的判断识别,通过 validate 方法,对于在我们期望之外的 HTTP 响应状态信息,
      Alamofire 会提供报错信息:
      */

      Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])

      .validate(statusCode: 200..<300)

      .validate(contentType: ["application/json"])
    • 自动识别

      1
      2
      3
      4
      5
      // validate 方法还提供自动识别机制,我们调用 validate 方法时不传入任何参数,则会自动认为 200…299 的状态吗为正常:

      Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])

      .validate()

6、Alamofire Timeline

  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /*
    Alamofire collects timings throughout the lifecycle of a Request and creates a Timeline object
    exposed as a property on a Response.
    */

    Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video", parameters: ["type": "JSON"])

    .validate()

    .responseJSON { response in

    print(response.timeline)
    }

    The above reports the following Timeline info:

    Latency: 0.428 seconds
    Request Duration: 0.428 seconds
    Serialization Duration: 0.001 seconds
    Total Duration: 0.429 seconds

7、Alamofire 调试打印

  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // GET print

    let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

    print(request)

    // 打印输出 GET http://192.168.88.200:8080/MJServer/video?type=JSON

    // POST print

    let request = Alamofire.request(.POST, "http://192.168.88.200:8080/MJServer/video", parameters: ["type":"JSON"])

    print(request)

    // 打印输出 POST http://192.168.88.200:8080/MJServer/video

    // GET debugprint

    let request = Alamofire.request(.GET, "http://192.168.88.200:8080/MJServer/video?type=JSON")

    debugPrint(request)

    // 打印输出 curl 信息

    $ curl -i \
    -H "User-Agent: SwiftAlamofire/com.qianqianstudio.SwiftAlamofire (1; OS Version 9.3 (Build 13E230))" \
    -H "Accept-Language: zh-Hans-US;q=1.0, en-US;q=0.9" \
    -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
    "http://192.168.88.200:8080/MJServer/video?type=JSON"

8、Alamofire 网络连接状态检查

  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    网络连接状态:

    public enum NetworkReachabilityStatus {
    case Unknown 网络状态未知
    case NotReachable 无网络连接
    case Reachable(Alamofire.NetworkReachabilityManager.ConnectionType)
    }

    public enum ConnectionType {
    case EthernetOrWiFi WiFi 网络
    case WWAN 无线网络(蜂窝移动网络)
    }

    let manager = NetworkReachabilityManager()

    // 监听网络状态闭包
    manager?.listener = { status in

    /*
    开启网络状态监听后,只要网络状态发生改变就会调用该闭包代码段。
    */

    print("Network Status Changed: \(status)")
    }

    // 开启监听网络状态
    manager?.startListening()

    // 关闭网络状态监听
    manager?.stopListening()

    // 获取网络连接状态
    let status = manager?.networkReachabilityStatus

    // 判断网络是否连接
    let isReachable:Bool? = manager?.isReachable

    // 判断 WiFi 是否连接
    let isReachableOnEthernetOrWiFi:Bool? = manager?.isReachableOnEthernetOrWiFi

    // 判断 无线网络 是否连接
    let isReachableOnWWAN:Bool? = manager?.isReachableOnWWAN

9、Alamofire 异步 GET 数据请求

  • Swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // Alamofire 方式

    let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML"

    Alamofire.request(.GET, urlStr)

    .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in

    /*
    网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。
    */
    }

    // Manager 方式

    // 必须设置为全局的
    var alamofireManager: Manager!

    let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video?type=XML"

    self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

    self.alamofireManager.request(.GET, urlStr)

    .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in

    /*
    网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。
    */
    }

10、Alamofire 文件下载

  • 支持的类型:

    • Request
    • Resume Data
  • 默认支持后台方式下载

  • Swift

    • 指定文件路径方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      // 目标路径闭包展开

      Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4")
      { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in

      /*
      设置文件下载路径,temporaryURL 是沙盒下的文件临时存储路径,下载完成后会被自动删除。response.suggestedFilename
      为服务器端文件名。此 block 在子线程中执行。
      */

      let documentsDirUrl:NSURL = NSFileManager.defaultManager()
      .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
      .URLByAppendingPathComponent(response.suggestedFilename!)

      if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) {

      // 移除已经存在的文件,在 Swift 中文件已经存在时,再次相同路径写入会失败
      try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl)
      }

      return documentsDirUrl
      }

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      /*
      监听文件下载进度,此 block 在子线程中执行。
      */

      // 设置下载进度条
      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true)
      }

      .response { (_, _, _, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。
      */
      }
    • 使用默认提供的下载路径方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      // 目标路径闭包

      // 设置文件的默认下载路径
      let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)

      Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4", destination: destination)

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      /*
      监听文件下载进度,此 block 在子线程中执行。
      */

      // 设置下载进度条
      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)),
      withObject: progressNum,
      waitUntilDone: true)
      }

      .response { (_, _, _, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。在 Swift 中文件已经存在时,再次相同路径写入会失败。
      */
      }
    • 断点续传下载方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      // 使用断点下载需要之前下载的临时文件存在,才能继续下载。

      var downloadRequest:Request!
      var resumeData:NSData!

      // 开始下载

      let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] + "/resumeData.tmp"

      // 判断断点保存的文件是否存在
      if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) {

      // 断点开始下载

      // 读取断点保存的数据
      self.resumeData = NSData(contentsOfFile: resumeTmpPath)

      self.downloadRequest = Alamofire.download(resumeData: self.resumeData, destination:
      { (temporaryURL:NSURL, response:NSHTTPURLResponse) -> NSURL in

      let documentsDirUrl:NSURL = NSFileManager.defaultManager()
      .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
      .URLByAppendingPathComponent(response.suggestedFilename!)
      if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) {

      try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl)
      }

      return documentsDirUrl
      })

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)),
      withObject: progressNum,
      waitUntilDone: true)
      }

      .response { (_, _, data:NSData?, error:NSError?) in

      if error == nil {

      // 删除断点下载缓存文件

      let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory,
      .UserDomainMask,
      true)[0]
      + "/resumeData.tmp"

      if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) {
      try! NSFileManager.defaultManager().removeItemAtPath(resumeTmpPath)
      }

      } else {

      // 下载的临时文件不存在处理

      if error?.localizedFailureReason == "No such file or directory" {

      let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory,
      .UserDomainMask,
      true)[0]
      + "/resumeData.tmp"

      if NSFileManager.defaultManager().fileExistsAtPath(resumeTmpPath) {

      // 删除断点下载缓存文件,否则继续断点下载会报错
      try! NSFileManager.defaultManager().removeItemAtPath(resumeTmpPath)
      }
      }
      }
      }
      } else {

      // 重新开始下载

      self.resumeData = NSData()

      self.downloadRequest = Alamofire.download(.GET, "http://120.25.226.186:32812/resources/videos/minion_01.mp4")
      { (temporaryURL:NSURL, response: NSHTTPURLResponse) -> NSURL in

      let documentsDirUrl:NSURL = NSFileManager.defaultManager()
      .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
      .URLByAppendingPathComponent(response.suggestedFilename!)

      if NSFileManager.defaultManager().fileExistsAtPath(documentsDirUrl.path!) {

      try! NSFileManager.defaultManager().removeItemAtURL(documentsDirUrl)
      }

      return documentsDirUrl
      }

      .progress { (bytesWrite:Int64, totalBytesWrite:Int64, totalBytesExpectedToWrite:Int64) in

      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesWrite)/Double(totalBytesExpectedToWrite)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)),
      withObject: progressNum,
      waitUntilDone: true)
      }

      .response { (_, _, data:NSData?, error:NSError?) in

      if error == nil {

      } else {

      // 停止下载处理

      if error!.code == NSURLErrorCancelled {

      if data != nil {

      // 意外终止的话,把已下载的数据储存起来
      self.resumeData = data

      let resumeTmpPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory,
      .UserDomainMask,
      true)[0]
      + "/resumeData.tmp"

      self.resumeData.writeToFile(resumeTmpPath, atomically: true)
      }

      } else {

      }
      }
      }
      }

      // 暂停下载

      self.downloadRequest.suspend()

      // 继续下载

      self.downloadRequest.resume()

      // 停止下载

      self.downloadRequest.cancel()

11、Alamofire 异步 POST 数据请求

  • Swift

    • Alamofire 方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video"
      let parameters:[String: AnyObject]? = ["type":"XML"]

      Alamofire.request(.POST, urlStr, parameters: parameters)

      .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。
      */
      }
    • Manager 方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // 必须设置为全局的
      var alamofireManager: Manager!

      let urlStr:URLStringConvertible = "http://192.168.88.200:8080/MJServer/video"
      let parameters:[String: AnyObject]? = ["type":"XML"]

      self.alamofireManager = Manager(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

      self.alamofireManager.request(.POST, urlStr, parameters: parameters)

      .response { (request:NSURLRequest?, response:NSHTTPURLResponse?, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束,成功时 error == nil。请求返回的数据存在 responseData 中,为 NSData 格式。
      */
      }

12、Alamofire 文件上传

  • 支持的类型:

    • File
    • Data
    • Stream
    • MultipartFormData
  • Swift

    • Data 形式上传

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      let boundary = "myBoundary"

      // 设置请求头
      /*
      upload task 不会在请求头里添加 content-type (上传数据类型)字段,@"myBoundary" 为请求体边界,参数可以随便设置,但需一致
      */
      let headers = ["Content-Type":"multipart/form-data; charset=utf-8; boundary=\(boundary)"]

      // 设置请求文件参数

      let formBody = NSMutableData()

      // 参数开始分割线
      /*
      每个参数开始前都需要加
      */
      formBody.appendData("--\(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 参数
      /*
      username 是后台规定的参数名,jhq 是需要添加的参数内容值
      */
      formBody.appendData("Content-Disposition: form-data; name=\"\("username")\"\r\n\r\n\("jhq")"
      .dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件开始分割线
      /*
      每个文件开始前都需要加
      */
      formBody.appendData("--\(boundary)".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件参数名
      /*
      file 是后台规定的参数名,test.mp4 为上传后服务器端文件名称
      */
      formBody.appendData("Content-Disposition: form-data; name=\"\("file")\"; filename=\"\("test.mp4")\""
      .dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 文件的类型
      formBody.appendData("Content-Type: mp4".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 待上传文件数据
      /*
      本地待上传的文件路径
      */
      formBody.appendData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      // 结束分割线标记
      formBody.appendData("--\(boundary)--".dataUsingEncoding(NSUTF8StringEncoding)!)
      formBody.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

      Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", headers: headers, data: formBody)

      .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in

      /*
      监听文件上传进度,此 block 在子线程中执行。
      */

      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)), withObject: progressNum, waitUntilDone: true)
      }

      .response { (_, _, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束。
      */
      }
    • MultipartFormData 形式上传

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      Alamofire.upload(.POST, "http://192.168.88.200:8080/MJServer/upload", 
      multipartFormData: { (formData:MultipartFormData) in

      /*
      添加参数。第一个参数是需要添加的参数内容值,第二个是后台规定的参数名。

      设置上传的文件。第一个参数是需要上传的文件路径,第二个是后台规定的参数名,第三个是上传后服务器端文件名称,第四个是文件类型。
      */

      // 添加参数

      formData.appendBodyPart(data: "jhq".dataUsingEncoding(NSUTF8StringEncoding)!, name: "username")

      // 指定文件路径形式上传

      let fileUrl = NSBundle.mainBundle().URLForResource("HQ_0005", withExtension: "jpg")!

      formData.appendBodyPart(fileURL: fileUrl, name: "file", fileName: "test.png", mimeType: "image/jpeg")

      // 指定文件数据形式上传

      let fileData = NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("minion", ofType: "mp4")!)

      formData.appendBodyPart(data: fileData!, name: "file", fileName: "test.mp4", mimeType: "mp4")

      }) { (encodingResult:Manager.MultipartFormDataEncodingResult) in

      /*
      数据编码完成。
      */

      switch encodingResult {

      // 编码成功
      case .Success(let uploadRequest, _, _):

      uploadRequest

      .progress { (bytesLoad:Int64, totalBytesLoad:Int64, totalBytesExpectedToLoad:Int64) in

      /*
      监听文件上传进度,此 block 在子线程中执行。
      */

      let progressNum:NSNumber = NSNumber(float: Float(Double(totalBytesLoad)/Double(totalBytesExpectedToLoad)))
      self.performSelectorOnMainThread(#selector(ViewController.refreshProgress(_:)),
      withObject: progressNum,
      waitUntilDone: true)
      }

      .response { (_, _, responseData:NSData?, error:NSError?) in

      /*
      网络请求结束。
      */
      }

      // 编码失败
      case .Failure(let error):

      print(error)
      }
      }
文章目录
  1. 1. 前言
  2. 2. 1、Alamofire
  3. 3. 2、Alamofire 的添加
  4. 4. 3、Alamofire 的设置
  5. 5. 4、Alamofire HTTP 认证
  6. 6. 5、Alamofire HTTP 响应状态信息识别
  7. 7. 6、Alamofire Timeline
  8. 8. 7、Alamofire 调试打印
  9. 9. 8、Alamofire 网络连接状态检查
  10. 10. 9、Alamofire 异步 GET 数据请求
  11. 11. 10、Alamofire 文件下载
  12. 12. 11、Alamofire 异步 POST 数据请求
  13. 13. 12、Alamofire 文件上传
隐藏目录