Swift?Error重構(gòu)優(yōu)化詳解
背景現(xiàn)狀
項目每積累到一定程度,代碼的重構(gòu)優(yōu)化是必經(jīng)之路。
試卷項目初期,整體錯誤Code較少,直接使用更便于處理錯誤狀態(tài),因此便全部歸整到一個單獨的 NetWorkError.ResponseCodeType
中,但是隨著項目功能的豐富,各個功能模塊越來越多,模塊錯誤的處理也各不相同,每個模塊都關聯(lián)了所有的錯誤Code,后續(xù)還會持續(xù)增長,導致越來越難以維護。
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 }
問題分析
提前分析、明確目標。
期望結(jié)果
- 錯誤處理分為兩部分:通用、自定義模塊,二者各自處理
- 拓展性強,各個模塊可自定義并處理錯誤,基類代碼保持穩(wěn)定不變
- 支持點語法、窮舉校驗,使用清晰便捷
技術選型
根據(jù)期望結(jié)果,可以大致選定技術方向
- 拓展性:泛型、協(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 { /// 對應模塊自定義類型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ù)進行 rawValue 的枚舉映射
- 分層處理錯誤類型,基類錯誤放到基類請求的回調(diào)中處理,拋出模塊的錯誤code
在ISTargetType協(xié)議中關聯(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 { // 接口錯誤,默認基類錯誤 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類型,僅需處理當前模塊和基礎錯誤,無需關注其他類型錯誤
優(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) { // ... // 當前模塊錯誤處理 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)化失敗,默認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) } } // 示例為簡單處理,若需精細化處理錯誤,拆分優(yōu)化后的代碼,邏輯明顯更加清晰 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) } }
總結(jié)
至此,我們已經(jīng)了解了有關ErrorCode的重構(gòu)優(yōu)化的大體邏輯,從后續(xù)的開發(fā)流程結(jié)果可以看出,確實對項目的Code混亂增長有了良好的控制,各模塊只需要關注處理自己的異常code,降低了維護代碼難度,后續(xù)也會持續(xù)關注和優(yōu)化。
參考資料
以上就是Swift Error重構(gòu)優(yōu)化詳解的詳細內(nèi)容,更多關于Swift Error重構(gòu)優(yōu)化的資料請關注腳本之家其它相關文章!
相關文章
switch實現(xiàn)一個兩數(shù)的運算代碼示例
這篇文章主要介紹了switch實現(xiàn)一個兩數(shù)的運算代碼示例,需要的朋友可以參考下2017-06-06在Swift中使用KVO的細節(jié)以及內(nèi)部實現(xiàn)解析(推薦)
這篇文章主要介紹了在Swift中使用KVO的細節(jié)以及內(nèi)部實現(xiàn)解析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07Ubuntu 16.04上安裝 Swift 3.0及問題解答
本文給大家分享的是在Ubuntu系統(tǒng)中安裝 Swift 3.0的方法和步驟,以及安裝過程中有可能遇到的問題的解答,這里推薦給小伙伴們,希望大家能夠喜歡2016-07-07