美文网首页Swift学习
RxAlamofire的使用

RxAlamofire的使用

作者: FallPine | 来源:发表于2018-09-12 10:19 被阅读167次

    需要导入RxAlamofire

    pod 'RxAlamofire'

    request(<#T##method: HTTPMethod##HTTPMethod#>, <#T##url: URLConvertible##URLConvertible#>, parameters: <#T##[String : Any]?#>, encoding: <#T##ParameterEncoding#>, headers: <#T##[String : String]?#>)
    
    • 加载网络请求的几个方法:request、requestData、requestJSON、requestString

    //创建URL对象
    let urlString = "https://www.douban.com/j/app/radio/channels"
    let url = URL(string:urlString)!
    
    request(.get, url)
        .data()
        .subscribe(onNext: { (data) in
            print(data)
        }).disposed(by: disposeBag)
    
    requestData(.post, url)
        .subscribe(onNext: { (response, data) in
            print(response)
            print(data)
        }).disposed(by: disposeBag)
    
    requestJSON(.get, url)
        .subscribe(onNext: { (response, json) in
            print(json)
        }).disposed(by: disposeBag)
    
    requestString(.get, url)
        .subscribe(onNext: { (response, str) in
            print(response.allHeaderFields)
            print(str)
        }).disposed(by: disposeBag)
    
    • 搭配HandyJSON,处理数据、模型转换

      • 扩展HandyJSON,支持rx
    import HandyJSON
    import RxSwift
    
    //数据映射错误
    public enum RxMapModelError: Error {
        case parsingError
    }
    
    //扩展Observable:增加模型映射方法
    public extension Observable where Element:Any {
        
        //将JSON数据转成对象
        public func mapModel<T>(type:T.Type) -> Observable<T> where T:HandyJSON {
            return self.map { (element) -> T in
                guard let parsedElement = T.deserialize(from: element as? Dictionary) else {
                    throw RxMapModelError.parsingError
                }
                
                return parsedElement
            }
        }
        
        //将JSON数据转成数组
        public func mapModels<T>(type:T.Type) -> Observable<[T]> where T:HandyJSON {
            return self.map { (element) -> [T] in
                guard let parsedArray = [T].deserialize(from: element as? [Any]) else {
                    throw RxMapModelError.parsingError
                }
                
                return parsedArray as! [T]
            }
        }
    }
    
    • 事例代码
    requestJSON(.get, url)
         .map({ (response, json) -> Any in
             print(json)
             return json
         })
         .mapModel(type: MusicModel.self)
         .subscribe(onNext: { (model) in
             print(model.channels?.count ?? 1)
         }).disposed(by: disposeBag)
    
    • 文件上传:upload

      • 使用文件流的形式上传文件
      //需要上传的文件路径
      let fileURL = Bundle.main.url(forResource: "hangge", withExtension: "zip")
      //服务器路径
      let uploadURL = URL(string: "http://www.hangge.com/upload.php")!
      
      //将文件上传到服务器
      upload(fileURL!, urlRequest: try! urlRequest(.post, uploadURL))
          .subscribe(onCompleted: {
              print("上传完毕!")
          })
          .disposed(by: disposeBag)
      

    如何在上传时附带上文件名?
    有时我们在文件上传的同时还会想要附带一些其它参数,比如文件名。这样服务端接收到文件后,就可以根据我们传过来的文件名来保存。实现这个其实很简单,客户端和服务端分别做如下修改。
    客户端:将文件名以参数的形式跟在链接后面。比如:http://hangge.com/upload.php?fileName=image1.png
    服务端:通过 $_GET["fileName"] 得到这个参数,并用其作为文件名保存。

    • 获得上传进度
    upload(fileURL!, urlRequest: try! urlRequest(.post, uploadURL))
        .subscribe(onNext: { element in
            print("--- 开始上传 ---")
            element.uploadProgress(closure: { (progress) in
                print("当前进度:\(progress.fractionCompleted)")
                print("  已上传载:\(progress.completedUnitCount/1024)KB")
                print("  总大小:\(progress.totalUnitCount/1024)KB")
            })
        }, onError: { error in
            print("上传失败! 失败原因:\(error)")
        }, onCompleted: {
            print("上传完毕!")
        })
        .disposed(by: disposeBag)
    
    
    upload(fileURL!, urlRequest: try! urlRequest(.post, uploadURL))
     .map{request in
         //返回一个关于进度的可观察序列
         Observable<Float>.create{observer in
             request.uploadProgress(closure: { (progress) in
                 observer.onNext(Float(progress.fractionCompleted))
                 if progress.isFinished{
                     observer.onCompleted()
                 }
             })
             return Disposables.create()
         }
     }
     .flatMap{$0}
     .bind(to: progressView.rx.progress) //将进度绑定UIProgressView上
     .disposed(by: disposeBag)
    
    • 上传 MultipartFormData 类型的文件数据(类似于网页上 Form 表单里的文件提交)
    //需要上传的文件
    let fileURL1 = Bundle.main.url(forResource: "0", withExtension: "png")
    let fileURL2 = Bundle.main.url(forResource: "1", withExtension: "png")
    
    //服务器路径
    let uploadURL = URL(string: "http://www.hangge.com/upload2.php")!
    
    //将文件上传到服务器
    upload(
        multipartFormData: { multipartFormData in
            multipartFormData.append(fileURL1!, withName: "file1")
            multipartFormData.append(fileURL2!, withName: "file2")
    },
        to: uploadURL,
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .success(let upload, _, _):
                upload.responseJSON { response in
                    debugPrint(response)
                }
            case .failure(let encodingError):
                print(encodingError)
            }
    })
    
    • 文本参数与文件一起提交(文件除了可以使用 fileURL,还可以上传 Data 类型的文件数据)
    //字符串
    let strData = "hangge.com".data(using: String.Encoding.utf8)
    //数字
    let intData = String(10).data(using: String.Encoding.utf8)
    //文件1
    let path = Bundle.main.url(forResource: "0", withExtension: "png")!
    let file1Data = try! Data(contentsOf: path)
    //文件2
    let file2URL = Bundle.main.url(forResource: "1", withExtension: "png")
    
    //服务器路径
    let uploadURL = URL(string: "http://www.hangge.com/upload2.php")!
    
    //将文件上传到服务器
    upload(
        multipartFormData: { multipartFormData in
            multipartFormData.append(strData!, withName: "value1")
            multipartFormData.append(intData!, withName: "value2")
            multipartFormData.append(file1Data, withName: "file1",
                                     fileName: "php.png", mimeType: "image/png")
            multipartFormData.append(file2URL!, withName: "file2")
    },
        to: uploadURL,
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .success(let upload, _, _):
                // 上传进度
                upload.uploadProgress(closure: { (progress) in
                            debugPrint(progress.fractionCompleted, progress.completedUnitCount / 1024, progress.totalUnitCount / 1024)
                        })
    
                upload.responseJSON { response in
                    debugPrint(response)
                }
            case .failure(let encodingError):
                print(encodingError)
            }
    })
    
    • 文件下载

    // 下载路径
    let destination: DownloadRequest.DownloadFileDestination = { _, response in
        let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        // 文件名不变
        // let fileURL = documentsURL.appendingPathComponent(response.suggestedFilename!)
        // 自定义文件名
        let fileURL = documentsURL.appendingPathComponent("file1/myLogo.png")
        //两个参数表示如果有同名文件则会覆盖,如果路径中文件夹不存在则会自动创建
        return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
    }
    
    // 需要下载的文件
    let fileURL = URL(string: "http://www.hangge.com/blog/images/logo.png")!
    
    // 开始下载
    download(URLRequest(url: fileURL), to: destination)
        .subscribe(onNext: { element in
            // 下载进度
            element.downloadProgress(closure: { (progress) in
                    debugPrint(progress.fractionCompleted, progress.completedUnitCount / 1024, progress.totalUnitCount / 1024)
                })
    
            print("开始下载。")
        }, onError: { error in
            print("下载失败! 失败原因:\(error)")
        }, onCompleted: {
            print("下载完毕!")
        })
        .disposed(by: disposeBag)
    
    • 使用默认提供的下载路径

    Alamofire 内置的许多常用的下载路径方便我们使用,简化代码。注意的是,使用这种方式如果下载路径下有同名文件,不会覆盖原来的文件

    let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
    
    • 下载进度
    download(URLRequest(url: fileURL), to: destination)
        .subscribe(onNext: { element in
            print("开始下载。")
            element.downloadProgress(closure: { progress in
                print("当前进度: \(progress.fractionCompleted)")
                print("  已下载:\(progress.completedUnitCount/1024)KB")
                print("  总大小:\(progress.totalUnitCount/1024)KB")
            })
        }, onError: { error in
            print("下载失败! 失败原因:\(error)")
        }, onCompleted: {
            print("下载完毕!")
        }).disposed(by: disposeBag)
    
    
    //开始下载
    download(URLRequest(url: fileURL), to: destination)
        .map{request in
            //返回一个关于进度的可观察序列
            Observable<Float>.create{observer in
                request.downloadProgress(closure: { (progress) in
                    observer.onNext(Float(progress.fractionCompleted))
                    if progress.isFinished{
                        observer.onCompleted()
                    }
                })
                return Disposables.create()
            }
        }
        .flatMap{$0}
        .bind(to: progressView.rx.progress) //将进度绑定UIProgressView上
        .disposed(by: disposeBag)
    

    参考文章:Swift - RxSwift的使用详解45(结合RxAlamofire使用1:数据请求)
    Swift - RxSwift的使用详解46(结合RxAlamofire使用2:结果处理、模型转换)
    Swift - RxSwift的使用详解47(结合RxAlamofire使用3:文件上传)
    Swift - RxSwift的使用详解48(结合RxAlamofire使用4:文件下载)

    相关文章

      网友评论

        本文标题:RxAlamofire的使用

        本文链接:https://www.haomeiwen.com/subject/mtuugftx.html