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 Moyaimport 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 Foundationimport SwiftyJSON
public protocol Mappable {
init(_ json:JSON)
}
统一解析类
import SwiftyJSONlet 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 Foundationimport 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 ModuleServicesimport 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模块化架构实践(三)依赖关系&Core Service 的全部内容, 来源链接: utcz.com/a/30206.html