iOS - 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 网址:https://github.com/Alamofire/Alamofire
  • Alamofire 使用 ARC
  • Swift
        // 将第三方库文件复制到工程目录下
        Alamofire       
    
        // 将第三方库文件中的 xcodeproj 添加到工程中
        Alamofire.xcodeproj
    
        // 在 TARGETS -> General -> Embedded Binaries 下添加静态库文件(添加上边的)
        Alamofire.framework
    
        // 添加头文件
        import Alamofire

3、Alamofire 的设置

  • Swift

    • 请求超时时间设置

          // 必须设置为全局的
          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)
          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")
    • 请求参数编码方式设置
          Alamofire 使用 Alamofire.ParameterEncoding 可以支持 URL query/URI form,JSON,PropertyList 方式编码参数。
      
          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)
    • 请求头设置
          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")
    • 请求数据响应格式设置
          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 请求创建方式
          // 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"])
    • 请求任务创建方式
          // 数据请求 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)
                      }
                  }
    • 请求任务设置
          // 继续请求任务
          downloadRequest.resume()
      
          // 暂停请求任务
          downloadRequest.suspend()
      
          // 取消请求任务
          downloadRequest.cancel()
    • 文件下载设置
          // 设置文件下载路径
      
              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)
    • 文件上传设置
          // 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
        // 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

    • 手动识别

          /*
                  Alamofire 还提供了 HTTP 响应状态的判断识别,通过 validate 方法,对于在我们期望之外的 HTTP 响应状态信息,
              Alamofire 会提供报错信息:
          */
      
          Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
      
              .validate(statusCode: 200..<300)
      
              .validate(contentType: ["application/json"])
    • 自动识别
          // validate 方法还提供自动识别机制,我们调用 validate 方法时不传入任何参数,则会自动认为 200…299 的状态吗为正常:
      
          Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
      
              .validate()

6、Alamofire Timeline

  • Swift

        /*
                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

        // 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

        网络连接状态:
    
            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

        // 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
    • 指定文件路径方式

          // 目标路径闭包展开
      
          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 中文件已经存在时,再次相同路径写入会失败。
                  */
              }
    • 使用默认提供的下载路径方式
          // 目标路径闭包
      
              // 设置文件的默认下载路径
              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 中文件已经存在时,再次相同路径写入会失败。
                      */
                  }
    • 断点续传下载方式
          // 使用断点下载需要之前下载的临时文件存在,才能继续下载。
      
          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 方式

          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 方式
          // 必须设置为全局的
          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 形式上传

          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 形式上传
          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)
              }
          }
时间: 2024-10-01 21:12:45

iOS - Alamofire 网络请求的相关文章

iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别?

问题描述 iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别? iOS 异步网络请求 和 把 同步网络请求放在子线程有什么区别? 解决方案 那就是异步和同步的问题咯,,异步的话是不会等待请求完成能继续执行下面的程序,,而同步会等待请求的完成,在继续执行下面的 解决方案二: 同步放子线程效果跟异步达到的类似.只是一个是API原生支持,一个是你自己代码来实现 解决方案三: 同步放子线程效果跟异步达到的类似.只是一个是API原生支持,一个是你自己代码来实现 解决方案四: 同步放子线程效果跟

iOS判断网络请求超时的方法_IOS

 本文介绍了iOS判断网络请求超时的方法,代码具体如下: + (AFHTTPRequestOperation *)requestOperationWithUrl:(NSString *)url requetMethod:(NSString *)method paramData:(NSDictionary *)aParamData constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block succes

iOS - ASIHTTPRequest 网络请求

前言 使用 iOS SDK 中的 HTTP 网络请求 API,相当的复杂,调用很繁琐,ASIHTTPRequest 就是一个对 CFNetwork API 进行了封装,并且使用起来非常简单的一套 API,外号 "HTTP终结者",用 Objective-C 编写,运行效率很高,可以很好的应用在 Mac OS X 系统和 iOS 平台的应用程序中,ASIHTTPRequest 适用于基本的 HTTP 请求,和基于 REST 的服务之间的交互.可惜作者早已停止更新,有一些潜在的 BUG 无

iOS - AFNetworking 网络请求

前言 在 iOS 开发中,一般情况下,简单的向某个 Web 站点简单的页面提交请求并获取服务器的响应,用 Xcode 自带的 NSURLConnection 是能胜任的.但是,在绝大部分下我们所需要访问的 Web 页面则是属于那种受到权限保护的页面,并不是有一个简单的 URL 可以访问的.这就涉及到了 Session 和 Cookie 的处理了,在此时使用 NSURLConnection 也是能够达到要求的,只是其中处理起来的复杂度和难度就提升了.为了更好的处理向 Web 站点的请求,包括处理

iOS - NSURLSession 网络请求

前言 NS_CLASS_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0) @interface NSURLSession : NSObject @available(iOS 7.0, *) public class NSURLSession : NSObject 1.NSURLSession 在 iOS9.0 之后,以前使用的 NSURLConnection 过期,苹果推荐使用 NSURLSession 来替换 NSURLConnection 完成网路请求相关操作.

iOS - NSURLConnection 网络请求

前言 @interface NSURLConnection : NSObject class NSURLConnection : NSObject DEPRECATED: The NSURLConnection class should no longer be used. NSURLSession is the replacement for NSURLConnection 从 iOS 9 开始 NSURLConnection 的大部分方法被废弃. 1.NSURLConnection NSUR

iOS开发中不合法的网络请求地址如何解决_IOS

NSString *const kWebsite = @http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fr=&sf=1&fmq=1459502303089_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&

afnetworki-ios AFNetworking网络请求出现415错误

问题描述 ios AFNetworking网络请求出现415错误 POST请求,参数上传是json.没有参数的情况下,后台能接受到请求,加上参数之后就出现415错误,很纠结,我传给后台的也是json数据,但是报这样的错误让我不知道如何更改.求大神帮忙,谢谢了! 解决方案 415,错误码已经很明确告诉你了 你还是要检测一下你post的json数据格式等 比如你可以用浏览器插件postman来手动测试一下, 看是否你post的格式服务器接受 解决方案二: 估计是你的服务器不支持post json的格

同步网络请求的意义何在?

问题描述 同步网络请求的意义何在? 我们说在发出网络请求时要使用异步请求,那么我就在想同步请求存在的意义是啥? 大神们,求解答 解决方案 网络同步请求网络请求 同步请求IOS 同步 网络请求 解决方案二: 先去百度同步跟异步的概念吧,然后你就知道了 解决方案三: 异步请求用于不需要立即回复的场合,而同步请求则是需要等待请求回复后才能进行其他操作的情况. 同步和异步,参考:http://baike.baidu.com/link?url=x-qOUNt4kzizZrF2omHjgN0wfhcCFi5