Swift?Error重構(gòu)優(yōu)化詳解
背景現(xiàn)狀
項目每積累到一定程度,代碼的重構(gòu)優(yōu)化是必經(jīng)之路。
試卷項目初期,整體錯誤Code較少,直接使用更便于處理錯誤狀態(tài),因此便全部歸整到一個單獨的 NetWorkError.ResponseCodeType 中,但是隨著項目功能的豐富,各個功能模塊越來越多,模塊錯誤的處理也各不相同,每個模塊都關(guān)聯(lián)了所有的錯誤Code,后續(xù)還會持續(xù)增長,導(dǎo)致越來越難以維護(hù)。
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
}
問題分析
提前分析、明確目標(biāo)。
期望結(jié)果
- 錯誤處理分為兩部分:通用、自定義模塊,二者各自處理
- 拓展性強(qiáng),各個模塊可自定義并處理錯誤,基類代碼保持穩(wěn)定不變
- 支持點語法、窮舉校驗,使用清晰便捷
技術(shù)選型
根據(jù)期望結(jié)果,可以大致選定技術(shù)方向
- 拓展性:泛型、協(xié)議
- 類型窮舉:枚舉
優(yōu)化解決
前后對比,不斷調(diào)優(yōu)。
Error模型
- 區(qū)分通用和自定義模塊
- 將 ResponseCodeType 降為通用Code類型,可以將其類型固定
- 替換 NetWorkError,使用 ModuleRespError 作為基類Error,通過泛型為外部模塊提供自定義能力
優(yōu)化前
struct NetWorkError: Error {
var code: ResponseCodeType = .other
var msg: String { code.errorString }
}
優(yōu)化后
/// 錯誤類型描述
public protocol ISErrorProtocol {
var errorString: String { get }
}
public enum ModuleRespError<T: ISErrorProtocol>: Error {
/// 對應(yīng)模塊自定義類型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
}
}
}
基類Request
使用協(xié)議的類型占位符 associatedtype,便于后續(xù)進(jìn)行 rawValue 的枚舉映射
- 分層處理錯誤類型,基類錯誤放到基類請求的回調(diào)中處理,拋出模塊的錯誤code
在ISTargetType協(xié)議中關(guān)聯(lián)錯誤碼類型 associatedtype ErrorCodeType: RawRepresentable
public protocol ISTargetType {
/// 錯誤碼類型,由各模塊自定義
associatedtype ErrorCodeType: RawRepresentable
}
優(yōu)化前
/// 根據(jù) ISTargetType 枚舉類型調(diào)用接口,返回 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)
}
}
優(yōu)化后
/// T.ErrorCodeType: 遵循 RawRepresentable 協(xié)議的泛型
/// Result<Success, Failure> 拆分成功、失敗邏輯
static func requestISResultType<T: ISTargetType>(_ server: T,
result: @escaping ((Result<NetWorkResponseModel, ModuleRespError<T.ErrorCodeType>>) -> Void)) {
// ...
Network.IS.fetchDataDic(server) { dataDic in
// 接口數(shù)據(jù)處理
guard let dataDic = dataDic,
let model: NetWorkResponseModel = NetWorkResponseModel.deserialize(from: dataDic),
let retCode = model.ret else {
// 接口錯誤,默認(rèn)基類錯誤
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)))
// 優(yōu)先處理基類錯誤code,例如 token失效
NetWorkRequest.checkTokenDidExpire(baseType)
} else if let retValue = retCode as? T.ErrorCodeType.RawValue,
let moduleType = T.ErrorCodeType(rawValue: retValue) {
// 解析并返回模塊錯誤碼
result(.failure(.type(moduleType)))
}
}
}
模塊調(diào)用
- 各模塊自定義ErrorCode,互不干涉
- 通過泛型參數(shù)定義ErrorCode類型
- 使用Result<Success, Failure>,消除結(jié)果可選值,成功失敗二選一,區(qū)分處理
- 限制失敗Error類型,僅需處理當(dāng)前模塊和基礎(chǔ)錯誤,無需關(guān)注其他類型錯誤
優(yōu)化前
public func queryDemo(with params: [String: String], completionHandler: @escaping (_ model: DemoModel?, _ code: ResponseCodeType) -> Void) {
NetWorkRequest.requestISType(GroupQueryServer.createGroup(params)) { model in
// ...
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))
}
}
logic.queryDemo(with: params) { model, code in
// 只能通過解包model來判斷接口的成功或失敗
guard let model = model else {
// 失敗處理
handleFail(code: code)
return
}
// 成功處理
hanldeSuccess()
}
private func handleFail(code: ResponseCodeType) {
// ...
// 當(dāng)前模塊錯誤處理
let showWarning = code == .wrongTeamCode || code == .classNotFound
// UI處理
warningLabel.isHidden = !showWarning
// 提示
CEProgressHUD.showTextHUD(code.errorString)
}
優(yōu)化后
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
}
public func queryDemo(with params: [String: String], completionHandler: @escaping ((Result<ClassItemModel, ModuleRespError<StudyGroupRespCode>>) -> Void)) {
// 基類請求
NetWorkRequest.requestISResultType(GroupQueryServer.createGroup(params)) { result in
switch result {
case .success(let success):
// 結(jié)果處理que
if let resultModel = ClassItemModel.deserialize(from: success.data) {
// 轉(zhuǎn)換模塊模型model
completionHandler(.success(resultModel))
} else {
// 轉(zhuǎn)化失敗,默認(rèn)other
completionHandler(.failure(.type(.other)))
}
case .failure(let error):
// 拋出的模塊錯誤
completionHandler(.failure(error))
}
}
logic.queryDemo(with: params) { result in
// 通過 Result 劃分結(jié)果狀態(tài)
switch result {
case .success(let model):
// 成功處理
hanldeSuccess()
case .failure(let error):
// 失敗處理
handleError(error)
}
}
// 示例為簡單處理,若需精細(xì)化處理錯誤,拆分優(yōu)化后的代碼,邏輯明顯更加清晰
private func handleError(_ error: ModuleRespError<StudyGroupRespCode>) {
switch error {
case .type(let code):
// ...
// 當(dāng)前模塊錯誤處理
let showWarning = code == .wrongTeamCode || code == .classNotFound
// UI處理
warningLabel.isHidden = !showWarning
// 提示
CEProgressHUD.showTextHUD(code.errorString)
case .baseType(let error):
// 基類錯誤處理
CEProgressHUD.showTextHUD(error.errorString)
}
}
總結(jié)
至此,我們已經(jīng)了解了有關(guān)ErrorCode的重構(gòu)優(yōu)化的大體邏輯,從后續(xù)的開發(fā)流程結(jié)果可以看出,確實對項目的Code混亂增長有了良好的控制,各模塊只需要關(guān)注處理自己的異常code,降低了維護(hù)代碼難度,后續(xù)也會持續(xù)關(guān)注和優(yōu)化。
參考資料
以上就是Swift Error重構(gòu)優(yōu)化詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift Error重構(gòu)優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
switch實現(xiàn)一個兩數(shù)的運算代碼示例
這篇文章主要介紹了switch實現(xiàn)一個兩數(shù)的運算代碼示例,需要的朋友可以參考下2017-06-06
在Swift中使用KVO的細(xì)節(jié)以及內(nèi)部實現(xiàn)解析(推薦)
這篇文章主要介紹了在Swift中使用KVO的細(xì)節(jié)以及內(nèi)部實現(xiàn)解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
Ubuntu 16.04上安裝 Swift 3.0及問題解答
本文給大家分享的是在Ubuntu系統(tǒng)中安裝 Swift 3.0的方法和步驟,以及安裝過程中有可能遇到的問題的解答,這里推薦給小伙伴們,希望大家能夠喜歡2016-07-07

