Swift Error重构优化详解
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>背景现状</li><li>问题分析</li><ul class="second_class_ul"><li>期望结果</li><li>技术选型</li></ul><li>优化解决</li><ul class="second_class_ul"><li>Error模型</li><li>优化前</li><li>优化后</li></ul><li>基类Request</li><ul class="second_class_ul"><li>优化前</li><li>优化后</li></ul><li>模块调用</li><ul class="second_class_ul"><li>优化前</li><li>优化后</li></ul><li>总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>背景现状</h2><p>项目每积累到一定程度,代码的重构优化是必经之路。</p>
<div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t5992/50/1038616804/55219/4e703907/592e8ae1N4e21f573.jpg" data-name="移动开发丛书 Swift 3核心技术与开发实践:Swift从入门到精通" data-owner="京东自营" data-price="53.7" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&p=JF8BAMkJK1olXwUCVFxaDE4XBV8IGFoTWgACVG4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYBVVhaDksXHDZNRwYlLWdeJDhUDRZ3ZWxTHz1pAV5wP1gieEcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD0wfBWkIH18UXQEKZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGWwUAUwkPZhZEACdPEx0WMwAGV19bCEsRM20JGlkXbTY"></div></div>
<p>试卷项目初期,整体错误Code较少,直接使用更便于处理错误状态,因此便全部归整到一个单独的 <code>NetWorkError.ResponseCodeType</code> 中,但是随着项目功能的丰富,各个功能模块越来越多,模块错误的处理也各不相同,每个模块都关联了所有的错误Code,后续还会持续增长,导致越来越难以维护。</p>
<div class="jb51code"><pre class="brush:cpp;">enum ResponseCodeType: Int {
case success = 0
case tokenExpire = 11001
case overVerifyCode = 11011
case verifyCodeExpire = 11002
case verifyCodeIncorrect = 11003
case autoLoginFailed = 11004
case appidLoginFailed = 11005
case phoneIsRegisted = 11006
case phoneHasBinded = 11010
case joinedBeePlan = 11100002
case uploadRepeate = 11020005
case wechatHasBinded = 11010017
case phoneHasBindedOtherWeChat = 11010022
case todayIsSignIned = 11140003
case subjectCountLimit = 11150004
case invalidTagName = 11160002
case alreadyExistsTagName = 11160003
case outOfMaxTagsCount = 11160004
case notRegisterHomework = 11010033
case notSupportNumber = 11010028
case wrongTeamCode = 11210005
case classNotFound = 11210006
case nicknameExists = 11210007
case joinClassThreeTimes = 11210008
case identityNickNameExists = 11210014
case checkClassCodeMax = 11210016
case createClassMAx = 11210015
case joinTeamMax = 11210017
case studentCountMax = 11210018
case other = -99999
}
</pre></div>
<p class="maodian"></p><h2>问题分析</h2>
<p>提前分析、明确目标。</p>
<p class="maodian"></p><h3>期望结果</h3>
<ul><li>错误处理分为两部分:通用、自定义模块,二者各自处理</li><li>拓展性强,各个模块可自定义并处理错误,基类代码保持稳定不变</li><li>支持点语法、穷举校验,使用清晰便捷</li></ul>
<p class="maodian"></p><h3>技术选型</h3>
<p>根据期望结果,可以大致选定技术方向</p>
<ul><li>拓展性:泛型、协议</li><li>类型穷举:枚举</li></ul>
<p class="maodian"></p><h2>优化解决</h2>
<p>前后对比,不断调优。</p>
<p class="maodian"></p><h3>Error模型</h3>
<ul><li>区分通用和自定义模块</li><li>将 ResponseCodeType 降为通用Code类型,可以将其类型固定</li><li>替换 NetWorkError,使用 ModuleRespError 作为基类Error,通过泛型为外部模块提供自定义能力</li></ul>
<p class="maodian"></p><p class="maodian"></p><p class="maodian"></p><h3>优化前</h3>
<div class="jb51code"><pre class="brush:cpp;">struct NetWorkError: Error {
var code: ResponseCodeType = .other
var msg: String { code.errorString }
}
</pre></div>
<p class="maodian"></p><p class="maodian"></p><p class="maodian"></p><h3>优化后</h3>
<div class="jb51code"><pre class="brush:cpp;">/// 错误类型描述
public protocol ISErrorProtocol {
var errorString: String { get }
}
public enum ModuleRespError<T: ISErrorProtocol>: Error {
/// 对应模块自定义类型code
case type(_ value: T)
/// 基类请求code
case baseType(_ value: ResponseCodeType)
/// 错误提示归整
public var mapErrorString: String {
switch self {
case .type(let value):
return value.errorString
case .baseType(let value):
return value.errorString
}
}
}
</pre></div>
<p class="maodian"></p><h2>基类Request</h2>
<p>使用协议的类型占位符 associatedtype,便于后续进行 rawValue 的枚举映射</p>
<ul><li>分层处理错误类型,基类错误放到基类请求的回调中处理,抛出模块的错误code</li></ul>
<p>在ISTargetType协议中关联错误码类型 <code>associatedtype ErrorCodeType: RawRepresentable</code></p>
<div class="jb51code"><pre class="brush:cpp;">public protocol ISTargetType {
/// 错误码类型,由各模块自定义
associatedtype ErrorCodeType: RawRepresentable
}
</pre></div>
<h3>优化前</h3>
<div class="jb51code"><pre class="brush:cpp;">/// 根据 ISTargetType 枚举类型调用接口,返回 model
static func requestISType<T: ISTargetType>(_ server: T,
completion: @escaping (_ model: NetworkModelResponse?, _ code: ResponseCodeType) -> Void) {
// ...
Network.IS.fetchDataDic(server) { dataDic in
guard let dataDic = dataDic,
let model: NetWorkResponseModel = NetWorkResponseModel.deserialize(from: dataDic) else {
completion(nil, .other)
return
}
// 判断code 是否为token过期
let codeValue = model.ret ?? ResponseCodeType.other.rawValue
// errorType
let codeType = ResponseCodeType(rawValue: codeValue) ?? .other
// 基类Code处理,token过期
NetWorkRequest.checkTokenDidExpire(codeType)
// 抛出的code:基类、模块混在一起
completion(model, codeType)
}
}
</pre></div>
<h3>优化后</h3>
<div class="jb51code"><pre class="brush:cpp;">/// T.ErrorCodeType: 遵循 RawRepresentable 协议的泛型
/// Result<Success, Failure> 拆分成功、失败逻辑
static func requestISResultType<T: ISTargetType>(_ server: T,
result: @escaping ((Result<NetWorkResponseModel, ModuleRespError<T.ErrorCodeType>>) -> Void)) {
// ...
Network.IS.fetchDataDic(server) { dataDic in
// 接口数据处理
guard let dataDic = dataDic,
let model: NetWorkResponseModel = NetWorkResponseModel.deserialize(from: dataDic),
let retCode = model.ret else {
// 接口错误,默认基类错误
let error: ModuleRespError<T.ErrorCodeType> = .baseType(.other)
result(.failure(error))
return
}
if retCode == 0 {
// 成功返回
result(.success(model))
return
}
// 请求失败
if let baseType = ResponseCodeType(rawValue: retCode) {
result(.failure(.baseType(baseType)))
// 优先处理基类错误code,例如 token失效
NetWorkRequest.checkTokenDidExpire(baseType)
} else if let retValue = retCode as? T.ErrorCodeType.RawValue,
let moduleType = T.ErrorCodeType(rawValue: retValue) {
// 解析并返回模块错误码
result(.failure(.type(moduleType)))
}
}
}
</pre></div>
<p class="maodian"></p><h2>模块调用</h2>
<ul><li>各模块自定义ErrorCode,互不干涉</li><li>通过泛型参数定义ErrorCode类型</li><li>使用Result<Success, Failure>,消除结果可选值,成功失败二选一,区分处理</li><li>限制失败Error类型,仅需处理当前模块和基础错误,无需关注其他类型错误</li></ul>
<h3>优化前</h3>
<div class="jb51code"><pre class="brush:cpp;">public func queryDemo(with params: , completionHandler: @escaping (_ model: DemoModel?, _ code: ResponseCodeType) -> Void) {
NetWorkRequest.requestISType(GroupQueryServer.createGroup(params)) { modelin
// ...
let code = model.ret ?? -1
let type = ResponseCodeType(rawValue: code) ?? .other
guard type == .success,
let result = DemoModel.deserialize(from: model.data) else {
completionHandler(nil, type)
return
}
completionHandler(.success(resultModel))
}
}
</pre></div>
<div class="jb51code"><pre class="brush:cpp;">logic.queryDemo(with: params) { model, code in
// 只能通过解包model来判断接口的成功或失败
guard let model = model else {
// 失败处理
handleFail(code: code)
return
}
// 成功处理
hanldeSuccess()
}
private func handleFail(code: ResponseCodeType) {
// ...
// 当前模块错误处理
let showWarning = code == .wrongTeamCode || code == .classNotFound
// UI处理
warningLabel.isHidden = !showWarning
// 提示
CEProgressHUD.showTextHUD(code.errorString)
}
</pre></div>
<h3>优化后</h3>
<div class="jb51code"><pre class="brush:cpp;">public enum StudyGroupRespCode: Int, ISErrorProtocol {
case wrongTeamCode = 11210005
case classNotFound = 11210006
case nicknameExists = 11210007
case joinClassThreeTimes = 11210008
case identityNickNameExists = 11210014
case checkClassCodeMax = 11210016
case createClassMAx = 11210015
case joinTeamMax = 11210017
case studentCountMax = 11210018
case folderLevelLimit = 11210027
case curIdentifierError = 11210011
case clockFrequencyInvalid = 11210036
case other
}
</pre></div>
<div class="jb51code"><pre class="brush:cpp;">public func queryDemo(with params: , completionHandler: @escaping ((Result<ClassItemModel, ModuleRespError<StudyGroupRespCode>>) -> Void)) {
// 基类请求
NetWorkRequest.requestISResultType(GroupQueryServer.createGroup(params)) { result in
switch result {
case .success(let success):
// 结果处理que
if let resultModel = ClassItemModel.deserialize(from: success.data) {
// 转换模块模型model
completionHandler(.success(resultModel))
} else {
// 转化失败,默认other
completionHandler(.failure(.type(.other)))
}
case .failure(let error):
// 抛出的模块错误
completionHandler(.failure(error))
}
}
</pre></div>
<div class="jb51code"><pre class="brush:cpp;">logic.queryDemo(with: params) { result in
// 通过 Result 划分结果状态
switch result {
case .success(let model):
// 成功处理
hanldeSuccess()
case .failure(let error):
// 失败处理
handleError(error)
}
}
// 示例为简单处理,若需精细化处理错误,拆分优化后的代码,逻辑明显更加清晰
private func handleError(_ error: ModuleRespError<StudyGroupRespCode>) {
switch error {
case .type(let code):
// ...
// 当前模块错误处理
let showWarning = code == .wrongTeamCode || code == .classNotFound
// UI处理
warningLabel.isHidden = !showWarning
// 提示
CEProgressHUD.showTextHUD(code.errorString)
case .baseType(let error):
// 基类错误处理
CEProgressHUD.showTextHUD(error.errorString)
}
}
</pre></div>
<p class="maodian"></p><h2>总结</h2>
<p>至此,我们已经了解了有关ErrorCode的重构优化的大体逻辑,从后续的开发流程结果可以看出,确实对项目的Code混乱增长有了良好的控制,各模块只需要关注处理自己的异常code,降低了维护代码难度,后续也会持续关注和优化。</p>
<p>参考资料</p>
<ul><li>Result 还是 Result<T, E: Error></li></ul>
<p>以上就是Swift Error重构优化详解的详细内容,更多关于Swift Error重构优化的资料请关注琼殿技术社区其它相关文章!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>swift中可选值?和!使用的方法示例</li><li>Swift中非可选的可选值类型处理方法详解</li><li>Swift使用SnapKit模仿Kingfisher第三方扩展优化</li><li>SwiftUI List在MacOS中的性能优化示例</li><li>Swift中图片资源使用流程的优化方法详解</li><li>Swift可选值优化示例详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]