Swift 网络请求 : Moya使用理解

Github : Moya

Moya版本 : 11.0.2
Swift版本 : 4.1

Moya入口

/// MoyaProvider类的对象遵循MoyaProviderType协议类型
open class MoyaProvider<Target: TargetType>: MoyaProviderType {

    /// 自定义的block
    public typealias EndpointClosure = (Target) -> Endpoint
    public typealias RequestResultClosure = (Result<URLRequest, MoyaError>) -> Void
    public typealias RequestClosure = (Endpoint, @escaping RequestResultClosure) -> Void
    public typealias StubClosure = (Target) -> Moya.StubBehavior

    /// 定义闭包对象
    open let endpointClosure: EndpointClosure
    open let requestClosure: RequestClosure
    open let stubClosure: StubClosure

    /// 定义会话对象
    open let manager: Manager

    /// 传播到Alamofire作为回调队列。 如果为nil - 将使用Alamofire默认(从2017年的API - 主队列)。
    let callbackQueue: DispatchQueue?

    /// 初始化 provider.
    public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
                requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
                stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
                callbackQueue: DispatchQueue? = nil,
                manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(),
                plugins: [PluginType] = [],
                trackInflights: Bool = false) {

        self.endpointClosure = endpointClosure
        self.requestClosure = requestClosure
        self.stubClosure = stubClosure
        self.manager = manager
        self.plugins = plugins
        self.trackInflights = trackInflights
        self.callbackQueue = callbackQueue
    }

这个是Moya的provider初始化的定义.对应传入的参数,基本上都是包含了默认参数.所以一般我们创建只需要是

let provider = MoyaProvider< Target >()

看上面的定义 : open class MoyaProvider<Target: TargetType>: MoyaProviderType
  • 注意两个地方 :
    ==> TargetType类型
    ==> 遵从 MoyaProviderType协议

1.TargetType
/// The protocol used to define the specifications necessary for a `MoyaProvider`.
public protocol TargetType {
    /// 基础请求地址
    var baseURL: URL { get }
    ///请求地址路径, path会拼接在baseURL后面组成一个完整的请求地址
    var path: String { get }
    /// 请求方法 POST/GET/PUT/DELETE.....
    var method: Moya.Method { get }
    /// 提供用于测试的存根数据。(可能使用到的次数不多)
    var sampleData: Data { get }
    /// 请求任务,相当于URLSessionTask
    var task: Task { get }
    /// 要对请求执行的验证类型。 默认为`.none`。(可能使用到的次数不多)
    var validationType: ValidationType { get }
    /// 要在请求中使用的标头。
    var headers: [String: String]? { get }
}
2.MoyaProviderType

看上面的定义 : open class MoyaProvider<Target: TargetType>: MoyaProviderType

///MoyaProvider接口的协议。
public protocol MoyaProviderType: AnyObject {
    //Target对象
    associatedtype Target: TargetType
    //网路请求
    func request(_ target: Target, callbackQueue: DispatchQueue?, progress: Moya.ProgressBlock?, completion: @escaping Moya.Completion) -> Cancellable
}

简单粗暴的理解就是,我们要创建一个TargetType类型的对象,然后实现MoyaProviderType协议方法.




第一步 : 创建一个TargetType类型的对象

//请求方法
enum MoyaApi {
    case login(account: String, password : String)
    case getNews(pageNum : Int , pageSize : Int)
}
//通过扩展遵循协议 : 当一个类型已经符合了某个协议中的所有要求,却还没有声明遵循该协议时,可以通过空扩展体的扩展来遵循该协议:
//从现在起,targetTypeDemo的实例可以作为 TargetType 类型使用:
extension MoyaApi : TargetType {
    var baseURL: URL {
        return URL.init(string: "http://api-gateway.100bue.com")!
    }

    var path: String {
        switch self {
        case .login:
            return "/usercenter/i/signin"
        case .getNews:
            return "/product-center/v1/product/app/list"
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .login:
            return .post
        case .getNews:
            return .get
        }
    }
        
    var sampleData: Data {return Data(base64Encoded: "just for test")!}
    var task: Task {
        switch self {
        case let .login(account, password):
            return .requestData(jsonToData(jsonDic: ["userName":account  ,"passWord":password])!) //参数放在HttpBody中
            
        case let .getNews(pageNum, pageSize):
            return .requestParameters(parameters: ["pageNum":pageNum,"pageSize":pageSize], encoding: URLEncoding.default)   //参数放在请求的url中
        }
    } // 请求任务

    var headers: [String : String]?{ return ["Content-type" : "application/json"] }
}

第二步 : 实现MoyaProviderType协议方法

let provider = MoyaProvider<MoyaApi>()
        provider.request(MoyaApi.login(account: "15811112222", password: "a123456"), callbackQueue: nil, progress: { (response) in
            print("response = \(response)")
        }) { (result) in
            switch result {
            case let .success(result):
                do {
                    try print("result.mapJSON() = \(result.mapJSON())")
                } catch {
                    print("MoyaError.jsonMapping(result) = \(MoyaError.jsonMapping(result))")
                }
            default:
                break
            }
            print("result = \(result.description)")
        }

打印结果 :

Json Str:{"userName":"15811112222","passWord":"a123456"}
response = ProgressResponse(response: nil, progressObject: Optional(<NSProgress: 0x1c0130360> : Parent: 0x0 / Fraction completed: 0.0000 / Completed: 189 of -1  ))
response = ProgressResponse(response: Optional(Status Code: 200, Data Length: 189), progressObject: Optional(<NSProgress: 0x1c0130360> : Parent: 0x0 / Fraction completed: 0.0000 / Completed: 189 of -1  ))
result.mapJSON() = {
    code = 0;
    data = "eyJhbGciOiJIUzI1NiIsInxxxxxRdszB624sV0cj4PxxxxSlF05CwAXjBYE";
    msg = success;
}

PS :

  • 所有的请求设置在extension MoyaApi : TargetType{}中完成
  • 对应的所有POST,GET,PUT请求都放在 var task: Task 中设置

重要 :

  • 我这里面的测试代码是只是一个POST请求,对应的还有GET请求,都通过设置task就好了.这里的demo是简单的版本,因为最开始的时候需要通过设置httpbody来请求.
  • 参考了别人iOS 使用Moya网络请求的设置头部信息的方法.都是以 [String: Any]的字典格式,不满足自己的需要.还是需要认真研究下Moya的源文档,再百度.
  • Demo中的GET请求只是一个简单的样式.请求是不成功的,拿不到数据的.换成自己项目上的试试就好

关于var task: Task

/// Represents an HTTP task.
public enum Task {
    /// 没有其他数据的请求。
    case requestPlain
    ///设置httpBody数据。
    case requestData(Data)
    /// 请求体设置为`Encodable`类型
    case requestJSONEncodable(Encodable)
    /// 请求体设置为`Encodable`类型和自定义编码器
    case requestCustomJSONEncodable(Encodable, encoder: JSONEncoder)
    /// 请求具有编码参数的主体集。
    case requestParameters(parameters: [String: Any], encoding: ParameterEncoding)
    /// 请求主体设置数据,并结合url参数。
    case requestCompositeData(bodyData: Data, urlParameters: [String: Any])
    /// 请求使用编码参数和url参数组合的主体集。
    case requestCompositeParameters(bodyParameters: [String: Any], bodyEncoding: ParameterEncoding, urlParameters: [String: Any])
    ///文件上载任务。
    case uploadFile(URL)
    /// A "multipart/form-data" upload task.
    case uploadMultipart([MultipartFormData])
    /// A "multipart/form-data" upload task  结合url参数。
    case uploadCompositeMultipart([MultipartFormData], urlParameters: [String: Any])
    /// 到目的地的文件下载任务。
    case downloadDestination(DownloadDestination)
    /// 使用给定编码的具有额外参数的目标的文件下载任务。
    case downloadParameters(parameters: [String: Any], encoding: ParameterEncoding, destination: DownloadDestination)
}

公共方法 :

//------------------------
//字典转Data
private func jsonToData(jsonDic:Dictionary<String, Any>) -> Data? {
    if (!JSONSerialization.isValidJSONObject(jsonDic)) {
        print("is not a valid json object")
        return nil
    }
    //利用自带的json库转换成Data
    //如果设置options为JSONSerialization.WritingOptions.prettyPrinted,则打印格式更好阅读
    let data = try? JSONSerialization.data(withJSONObject: jsonDic, options: [])
    //Data转换成String打印输出
    let str = String(data:data!, encoding: String.Encoding.utf8)
    //输出json字符串
    print("Json Str:\(str!)")
    return data
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 文章摘自Moya官方文档 Targets Moya的使用始于定义一个target——典型的是定义一个符合Targe...
    Jt_Self阅读 16,111评论 0 27
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,400评论 1 45
  • 那是两年前,我还在念大三的时候,一堂叫做“英汉翻译”的课的期末考试,内容是现场翻译一篇学术论文。虽然有电子词典,但...
    李子李子短信阅读 1,209评论 0 17