iOS模块化架构实践(三)依赖关系&Core Service

依赖关系

如上图所示,Appkit 为底层核心模块 包含 Foundation ,Service , Widgets ,Navigation ,等

上层Module 依赖核心模块

这样做的好处:

  • 易于扩展新业务。
  • 高度复用。
  • 人力资源能够调配简单。

CoreFramework - Service

Service 网络层。

主要功能:

  • 统一化网络请求接口。
  • 统一解析接口数据。
  • 处理网络错误。
  • 处理token 等

具体实现方案:

推荐使用 Moya

核心code

ServiceProvider 通过继承MoyaProvider 来实现 Moya open func request(_ target: Target, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, completion: @escaping Completion) -> Cancellable的方法,这样我们可以在 completion 中来处理网络请求结果,对请求结果统一处理,

import Moya

import SwiftyJSON

/// Closure to be executed when a request has completed.

public typealias OnSuccess<T: Mappable> = (_ result: ResponseMapping<T>) -> Void

public typealias OnError = (_ error:ErrorCode) -> Void

/// Base Moay Service Provider

public class ServiceProvider<Target: TargetType> : MoyaProvider<Target> {

public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,

stubClosure: @escaping StubClosure = MoyaProvider.neverStub) {

let networkActivityPlugin = NetworkActivityPlugin { (change, type) in

switch change {

case .began:

UIApplication.shared.isNetworkActivityIndicatorVisible = true

case .ended:

UIApplication.shared.isNetworkActivityIndicatorVisible = false

}

}

var plugins:[PluginType] = [networkActivityPlugin]

#if DEBUG

plugins.append(ConsoleLoggerPlugin())

#endif

super.init(endpointClosure: endpointClosure, stubClosure: stubClosure,plugins:plugins)

}

public func request<T:Mappable>(_ target: Target, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, onSuccess: OnSuccess<T>? = nil, onError: OnError? = nil) -> Cancellable {

let cancellable = self.request(target, callbackQueue: callbackQueue, progress: progress) { (result) in

switch result {

caselet .success(response):

do {

let json = try response.serializeAsJson()

let mapping = ResponseMapping<T>(json)

onSuccess?(mapping)

} catch let error {

iflet errorCode = error as? ErrorCode {

onError?(errorCode)

} else {

onError?(ErrorCode.unknown)

}

}

caselet .failure(error):

var resError = ErrorCode.unknown

switch error {

case .underlying(let netError, let response):

if netError._code == -1009 {

resError = ErrorCode.networkError

}

iflet response = response, response.statusCode == 401 {

resError = ErrorCode.userSessionExpired

}

fallthrough

default:

resError = ErrorCode.remoteServerError

}

onError?(resError)

}

}

return cancellable

}

}

AppTargetType 为继承 Moya TargetType 来统一接口的request 简化 TargetType

import Foundation

import Moya

public protocol AppTargetType:TargetType {

var parameters: [String: Any] { get }

}

public extension AppTargetType {

var baseURL: URL {

return URL(string: ServiceUrlConfiguration.baseUrl)!

}

var sampleData: Data {

return"".data(using: .utf8)!

}

var validationType: ValidationType {

return .successCodes

}

var task: Task {

switch self.method {

case .get:

return .requestParameters(parameters: parameters, encoding: URLEncoding.default)

default:

return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)

}

}

var headers: [String : String]? {

return [:]

// return ["customHeaderKey":"customHeaderValue"]

}

}

json 解析

SwiftyJson 是一个很不错的json解析的库

Mapplable 项目中model 的父类 通过继承 Mapplable 来实现自动解析

import Foundation

import SwiftyJSON

public protocol Mappable {

init(_ json:JSON)

}

统一解析类

import SwiftyJSON

let codeKey = "code"

let messageKey = "msg"

let dataKey = "data"

/// A wrapper classs which object-mapping with response

public class ResponseMapping<T> : Mappable {

public let code:Int

public let message:String?

fileprivate let json:JSON

fileprivate var _body: T?

public required init(_ json:JSON) {

code = json[codeKey].intValue

message = json[messageKey].stringValue

self.json = json

}

}

public extension ResponseMapping where T : Mappable {

var body : T? {

get {

if(_body != nil) {

return _body!

}

_body = T(json[dataKey])

return _body

}

}

}

public extension ResponseMapping where T : Sequence, T.Element : Mappable {

var body : T? {

get {

if(_body != nil) {

return _body

}

iflet array = json[dataKey].array {

let result = array.map({ element -> T.Element in

return T.Element(element)

})

_body = result as? T

}

return _body

}

}

}

Module 中具体使用方法

API 为该模块的所有api 接口

import Foundation

import ModuleServices

import Moya

enum APITarget {

case Login(username:String,pwd:String)

case MobileLogin(phone:String,code:String)

}

extension APITarget:AppTargetType {

var parameters: [String : Any] {

switch self {

case .Login(let username,letpwd):

return ["username":username,"password":pwd]

case .MobileLogin(let phone,let code):

return ["phone":phone,"code":code]

}

}

var path: String {

switch self {

case .Login:

return"login/path"

case .MobileLogin:

return"mobile/login/path"

}

}

var method: Moya.Method {

return .post

}

}

typealias APIRequest = ServiceProvider<APITarget>

DataProvider 提供api 中所有接口的网络请求部分。

import ModuleServices

import SwiftyJSON

protocol DataProvider {

func login(username:String,password:String,successHandle:@escaping (LoginModel)->Void? , failHandle:@escaping (ErrorCode)->Void? )

}

struct LoginModel : Mappable {

init(_ json: JSON) {

}

}

class RemoteDataProvider:DataProvider {

var provider = APIRequest()

func login(username: String, password: String, successHandle:@escaping (LoginModel)->Void? ,failHandle:@escaping (ErrorCode)->Void?) {

_ = provider.request(.Login(username: username, pwd: password),onSuccess: { (response:ResponseMapping<LoginModel>) in

iflet loginModel = response.body {

successHandle(loginModel)

}

},onError:{ code in

failHandle(code)

})

}

}

注:这里为什么要用protocol 先生命请求方法。当写mock 数据或者unit test 时 我们可以通过继承 dataprovider的方式来直接handle 网络请求,而不用改变 任何代码 这部分会放在 接下来的章节 去详细介绍。

后记

大概网络层以及 业务模块依赖网络层的部分就这么多。

这样写的好处是,逻辑清楚,编写业务模块的人员不会动到核心业务代码,流水线工作模式,只需要了解怎么使用 怎么组装,不需要考虑详细的逻辑处理。 可以快速的进行业务的扩展。 团队规模可以随业务多少进行缩放。

以上是 iOS模块化架构实践(三)依赖关系&amp;Core Service 的全部内容, 来源链接: utcz.com/a/30206.html

回到顶部