Alamofire 對錯(cuò)誤的處理考慮使用枚舉,只要結(jié)果可能是有限的集合的情況下,其實(shí)枚舉本身還是數(shù)據(jù)的一種載體海蔽,swift中党窜,枚舉有著很豐富的使用方法.
先總結(jié)一下swfit中enum中的用法:
1.正常用法
enum Movement {
case Left
case Right
case Top
case Bottom
}
let aMovement = Movement.Left
switch aMovement {
case .Left:
print("left")
default:
print("Unknow")
}
if case .Left = aMovement {
print("Left")
}
if .Left == aMovement {
print("Left")
}
2.聲明為整型
enum Season: Int {
case Spring = 0
case Summer = 1
case Autumn = 2
case Winter = 3
}
.3.聲明為字符串類型
enum House: String {
case ZhangSan = "I am zhangsan"
case LiSi = "I am lisi"
}
let zs = House.ZhangSan
print(zs.rawValue)
enum CompassPoint: String {
case North, South, East, West
}
let n = CompassPoint.North
4.聲明為浮點(diǎn)類型
enum Constants: Double {
case π = 3.14159
case e = 2.71828
case φ = 1.61803398874
case λ = 1.30357
}
let pai = Constants.π
print(pai.rawValue)
5.其他類型
enum VNodeFlags : UInt32 {
case Delete = 0x00000001
case Write = 0x00000002
case Extended = 0x00000004
case Attrib = 0x00000008
case Link = 0x00000010
case Rename = 0x00000020
case Revoke = 0x00000040
case None = 0x00000080
}
6.enum包含enum
enum Character {
enum Weapon {
case Bow
case Sword
case Lance
case Dagger
}
enum Helmet {
case Wooden
case Iron
case Diamond
}
case Thief
case Warrior
case Knight
}
7.結(jié)構(gòu)體和枚舉
struct Scharacter {
enum CharacterType {
case Thief
case Warrior
case Knight
}
enum Weapon {
case Bow
case Sword
case Lance
case Dagger
}
let type: CharacterType
let weapon: Weapon
}
let sc = Scharacter(type: .Thief, weapon: .Bow)
print(sc.type)
8.值關(guān)聯(lián)
enum Trade {
case Buy(stock: String, amount: Int)
case Sell(stock: String, amount: Int)
}
let trade = Trade.Buy(stock: "Car", amount: 100)
if case let Trade.Buy(stock, amount) = trade {
print("buy \(amount) of \(stock)")
}
enum Trade0 {
case Buy(String, Int)
case Sell(String, Int)
}
let trade0 = Trade0.Buy("Car0", 100)
if case let Trade0.Buy(stock, amount) = trade0 {
print("buy \(amount) of \(stock)")
}
9.枚舉中的函數(shù)
enum Wearable {
enum Weight: Int {
case Light = 2
}
enum Armor: Int {
case Light = 2
}
case Helmet(weight: Weight, armor: Armor)
func attributes() -> (weight: Int, armor: Int) {
switch self {
case .Helmet(let w, let a):
return (weight: w.rawValue * 2, armor: a.rawValue * 4)
}
}
}
let test = Wearable.Helmet(weight: .Light, armor: .Light).attributes()
print(test)
enum Device {
case iPad, iPhone, AppleTV, AppleWatch
func introduced() -> String {
switch self {
case .AppleTV: return "\(self) was introduced 2006"
case .iPhone: return "\(self) was introduced 2007"
case .iPad: return "\(self) was introduced 2010"
case .AppleWatch: return "\(self) was introduced 2014"
}
}
}
print (Device.iPhone.introduced())
10.枚舉中的屬性
enum Device1 {
case iPad, iPhone
var year: Int {
switch self {
case .iPad:
return 2010
case .iPhone:
return 2007
}
}
}
let iPhone = Device1.iPhone
print(iPhone.year)
ParameterEncodingFailureReason
通過ParameterEncodingFailureReason我們能夠很清楚的看出來這是一個(gè)參數(shù)編碼的錯(cuò)誤原因壤玫。大家注意reason這個(gè)詞,在命名中择镇,有或者沒有這個(gè)詞腻豌,表達(dá)的意境完全不同吝梅,因此惹骂,Alamofire牛逼就體現(xiàn)在這些細(xì)節(jié)之中对粪。
public enum AFError: Error {
/// The underlying reason the parameter encoding error occurred.
///
/// - missingURL: The URL request did not have a URL to encode.
/// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the
/// encoding process.
/// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during
/// encoding process.
public enum ParameterEncodingFailureReason {
case missingURL
case jsonEncodingFailed(error: Error)
case propertyListEncodingFailed(error: Error)
}
}
ParameterEncodingFailureReason本身是一個(gè)enum,同時(shí)牍帚,它又被包含在AFError之中乳蛾,這說明枚舉之中可以有另一個(gè)枚舉肃叶。那么像這種情況我們怎么使用呢因惭?看下邊的代碼:
let parameterErrorReason = AFError.ParameterEncodingFailureReason.missingURL
枚舉的訪問是一級一級進(jìn)行的。我們再看這行代碼:case jsonEncodingFailed(error: Error)浸锨。jsonEncodingFailed(error: Error)并不是函數(shù)柱搜,就是枚舉的一個(gè)普通的子選項(xiàng)剥险。(error: Error)是它的一個(gè)關(guān)聯(lián)值表制,相對于任何一個(gè)子選項(xiàng)么介,我們都可以關(guān)聯(lián)任何值,它的意義就在于设拟,把這些值與子選項(xiàng)進(jìn)行綁定纳胧,方便在需要的時(shí)候調(diào)用帘撰。我們會在下邊講解如何獲取關(guān)聯(lián)值摧找。
參數(shù)編碼有一下幾種方式:
把參數(shù)編碼到URL中
把參數(shù)編碼到httpBody中
Alamofire中是如何進(jìn)行參數(shù)編碼的,這方面的內(nèi)容會在后續(xù)的ParameterEncoding.swift這一篇文章中給出詳細(xì)的解釋唤衫。那么編碼失敗的原因可能為:
missingURL 給定的urlRequest.url為nil的情況拋出錯(cuò)誤
jsonEncodingFailed(error: Error) 當(dāng)選擇把參數(shù)編碼成JSON格式的情況下绵脯,參數(shù)JSON化拋出的錯(cuò)誤
propertyListEncodingFailed(error: Error) 這個(gè)同上
綜上所述蛆挫,ParameterEncodingFailureReason封裝了參數(shù)編碼的錯(cuò)誤悴侵,可能出現(xiàn)的錯(cuò)誤類型為Error可免,說明這些所謂一般是調(diào)用系統(tǒng)Api產(chǎn)生的錯(cuò)誤做粤。
MultipartEncodingFailureReason
public enum MultipartEncodingFailureReason {
case bodyPartURLInvalid(url: URL)
case bodyPartFilenameInvalid(in: URL)
case bodyPartFileNotReachable(at: URL)
case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
case bodyPartFileIsDirectory(at: URL)
case bodyPartFileSizeNotAvailable(at: URL)
case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
case bodyPartInputStreamCreationFailed(for: URL)
case outputStreamCreationFailed(for: URL)
case outputStreamFileAlreadyExists(at: URL)
case outputStreamURLInvalid(url: URL)
case outputStreamWriteFailed(error: Error)
case inputStreamReadFailed(error: Error)
}
多部分編碼錯(cuò)誤一般發(fā)生在上傳或下載請求中對數(shù)據(jù)的處理過程中怕品,這里邊最重要的是對上傳數(shù)據(jù)的處理過程肉康,會在后續(xù)的MultipartFormData.swift這一篇文章中給出詳細(xì)的解釋吼和,我們就簡單的分析下MultipartEncodingFailureReason子選項(xiàng)錯(cuò)誤出現(xiàn)的原因:
bodyPartURLInvalid(url: URL) 上傳數(shù)據(jù)時(shí)炫乓,可以通過fileURL的方式,讀取本地文件數(shù)據(jù)光督,如果fileURL不可用结借,就會拋出這個(gè)錯(cuò)誤
bodyPartFilenameInvalid(in: URL) 如果使用fileURL的lastPathComponent或者pathExtension獲取filename為空拋出的錯(cuò)誤
bodyPartFileNotReachable(at: URL) 通過fileURL不能訪問數(shù)據(jù)船老,也就是不可達(dá)的
bodyPartFileNotReachableWithError(atURL: URL, error: Error) 這個(gè)不同于bodyPartFileNotReachable(at: URL),當(dāng)嘗試檢測fileURL是不是可達(dá)的情況下拋出的錯(cuò)誤
bodyPartFileIsDirectory(at: URL) 當(dāng)fileURL是一個(gè)文件夾時(shí)拋出錯(cuò)誤
bodyPartFileSizeNotAvailable(at: URL) 當(dāng)使用系統(tǒng)Api獲取fileURL指定文件的size出現(xiàn)錯(cuò)誤
bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) 查詢fileURL指定文件size出現(xiàn)錯(cuò)誤
bodyPartInputStreamCreationFailed(for: URL) 通過fileURL創(chuàng)建inputStream出現(xiàn)錯(cuò)誤
outputStreamCreationFailed(for: URL) 當(dāng)嘗試把編碼后的數(shù)據(jù)寫入到硬盤時(shí)柳畔,創(chuàng)建outputStream出現(xiàn)錯(cuò)誤
outputStreamFileAlreadyExists(at: URL) 數(shù)據(jù)不能被寫入薪韩,因?yàn)橹付ǖ膄ileURL已經(jīng)存在
outputStreamURLInvalid(url: URL) fileURL不是一個(gè)file URL
outputStreamWriteFailed(error: Error) 數(shù)據(jù)流寫入錯(cuò)誤
inputStreamReadFailed(error: Error) 數(shù)據(jù)流讀入錯(cuò)誤
綜上所述,這些錯(cuò)誤基本上都跟數(shù)據(jù)的操作相關(guān)罗捎,這個(gè)在后續(xù)會做出很詳細(xì)的說明桨菜。
ResponseValidationFailureReason
public enum ResponseValidationFailureReason {
case dataFileNil
case dataFileReadFailed(at: URL)
case missingContentType(acceptableContentTypes: [String])
case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
case unacceptableStatusCode(code: Int)
}
Alamofire不管請求是否成功倒得,都會返回response霞掺。它提供了驗(yàn)證ContentType和StatusCode的功能讹躯,關(guān)于驗(yàn)證蜀撑,再后續(xù)的文章中會有詳細(xì)的解答酷麦,我們先看看這些原因:
dataFileNil 保存數(shù)據(jù)的URL不存在沃饶,這種情況一般出現(xiàn)在下載任務(wù)中,指的是下載代理中的fileURL缺失
dataFileReadFailed(at: URL) 保存數(shù)據(jù)的URL無法讀取數(shù)據(jù)琴昆,同上
missingContentType(acceptableContentTypes: [String]) 服務(wù)器返回的response不包含ContentType且提供的acceptableContentTypes不包含通配符(通配符表示可以接受任何類型)
unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) ContentTypes不匹配
unacceptableStatusCode(code: Int) StatusCode不匹配
ResponseSerializationFailureReason
public enum ResponseSerializationFailureReason {
case inputDataNil
case inputDataNilOrZeroLength
case inputFileNil
case inputFileReadFailed(at: URL)
case stringSerializationFailed(encoding: String.Encoding)
case jsonSerializationFailed(error: Error)
case propertyListSerializationFailed(error: Error)
}
我們在上篇中已經(jīng)提到业舍,Alamofire支持把服務(wù)器的response序列成幾種數(shù)據(jù)格式舷暮。
response 直接返回HTTPResponse下面,未序列化
responseData 序列化為Data
responseJSON 序列化為Json
responseString 序列化為字符串
responsePropertyList 序列化為Any
那么在序列化的過程中沥割,很可能會發(fā)生下邊的錯(cuò)誤:
inputDataNil 服務(wù)器返回的response沒有數(shù)據(jù)
inputDataNilOrZeroLength 服務(wù)器返回的response沒有數(shù)據(jù)或者數(shù)據(jù)的長度是0
inputFileNil 指向數(shù)據(jù)的URL不存在
inputFileReadFailed(at: URL) 指向數(shù)據(jù)的URL無法讀取數(shù)據(jù)
AFError
上邊內(nèi)容中介紹的ParameterEncodingFailureReason MultipartEncodingFailureReason ResponseValidationFailureReason和 ResponseSerializationFailureReason机杜,他們是定義在AFError中獨(dú)立的枚舉,他們之間是包含和被包含的關(guān)系,理解這一點(diǎn)很重要陡叠,因?yàn)橛辛诉@種包含的管理枉阵,在使用中就需要通過AFError.ParameterEncodingFailureReason這種方式進(jìn)行操作兴溜。
那么最重要的問題就是耻陕,如何把上邊4個(gè)獨(dú)立的枚舉進(jìn)行串聯(lián)呢?Alamofire巧妙的地方就在這里膘怕,有4個(gè)獨(dú)立的枚舉岛心,分別代表4大錯(cuò)誤忘古。也就是說這個(gè)網(wǎng)絡(luò)框架肯定有這4大錯(cuò)誤模塊髓堪,我們只需要給AFError設(shè)計(jì)4個(gè)子選項(xiàng),每個(gè)子選項(xiàng)關(guān)聯(lián)上上邊4個(gè)獨(dú)立枚舉的值就ok了骤菠。
這個(gè)設(shè)計(jì)真的很巧妙商乎,試想鹉戚,如果把所有的錯(cuò)誤都放到AFError中专控,就顯得非常冗余伦腐。那么下邊的代碼就呼之欲出了柏蘑,大家好好體會體會在swift下這么設(shè)計(jì)的妙用:
case invalidURL(url: URLConvertible)
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
case responseValidationFailed(reason: ResponseValidationFailureReason)
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
AFError的擴(kuò)展
也許在開發(fā)中咳焚,我們完成了上邊的代碼就認(rèn)為夠用了革半,但對于一個(gè)開源框架而言,遠(yuǎn)遠(yuǎn)是不夠的延刘。我們一點(diǎn)點(diǎn)進(jìn)行剖析:
現(xiàn)在給定一條數(shù)據(jù):
func findErrorType(error: AFError) {
}
我只需要知道這個(gè)error是不是參數(shù)編碼錯(cuò)誤访娶,應(yīng)該怎么辦崖疤?因此為AFError提供5個(gè)布爾類型的屬性劫哼,專門用來獲取當(dāng)前的錯(cuò)誤是不是某個(gè)指定的類型权烧。這個(gè)功能的實(shí)現(xiàn)比較簡單般码,代碼如下:
extension AFError {
/// Returns whether the AFError is an invalid URL error.
public var isInvalidURLError: Bool {
if case .invalidURL = self { return true }
return false
}
/// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will
/// contain the associated value.
public var isParameterEncodingError: Bool {
if case .parameterEncodingFailed = self { return true }
return false
}
/// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties
/// will contain the associated values.
public var isMultipartEncodingError: Bool {
if case .multipartEncodingFailed = self { return true }
return false
}
/// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`,
/// `responseContentType`, and `responseCode` properties will contain the associated values.
public var isResponseValidationError: Bool {
if case .responseValidationFailed = self { return true }
return false
}
/// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and
/// `underlyingError` properties will contain the associated values.
public var isResponseSerializationError: Bool {
if case .responseSerializationFailed = self { return true }
return false
}
}
總而言之宫静,這些都是給AFError這個(gè)枚舉擴(kuò)展的屬性券时,還包含下邊這些屬性
urlConvertible: URLConvertible? 獲取某個(gè)屬性橘洞,這個(gè)屬性實(shí)現(xiàn)了URLConvertible協(xié)議炸枣,在AFError中只有case invalidURL(url: URLConvertible)這個(gè)選項(xiàng)符合要求
/// The `URLConvertible` associated with the error.
public var urlConvertible: URLConvertible? {
switch self {
case .invalidURL(let url):
return url
default:
return nil
}
}
url: URL? 獲取AFError中的URL适肠,當(dāng)然這個(gè)URL只跟MultipartEncodingFailureReason這個(gè)子選項(xiàng)有關(guān)
/// The `URL` associated with the error.
public var url: URL? {
switch self {
case .multipartEncodingFailed(let reason):
return reason.url
default:
return nil
}
}
underlyingError: Error? AFError中封裝的所有的可能出現(xiàn)的錯(cuò)誤中迂猴,并不是每種可能都會返回Error這個(gè)錯(cuò)誤信息沸毁,因此這個(gè)屬性是可選的
/// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`,
/// `.multipartEncodingFailed` or `.responseSerializationFailed` error.
public var underlyingError: Error? {
switch self {
case .parameterEncodingFailed(let reason):
return reason.underlyingError
case .multipartEncodingFailed(let reason):
return reason.underlyingError
case .responseSerializationFailed(let reason):
return reason.underlyingError
default:
return nil
}
}
acceptableContentTypes: [String]? 可接受的ContentType
/// The response `Content-Type` of a `.responseValidationFailed` error.
public var responseContentType: String? {
switch self {
case .responseValidationFailed(let reason):
return reason.responseContentType
default:
return nil
}
}
responseCode: Int? 響應(yīng)碼
/// The response code of a `.responseValidationFailed` error.
public var responseCode: Int? {
switch self {
case .responseValidationFailed(let reason):
return reason.responseCode
default:
return nil
}
}
failedStringEncoding: String.Encoding? 錯(cuò)誤的字符串編碼
/// The `String.Encoding` associated with a failed `.stringResponse()` call.
public var failedStringEncoding: String.Encoding? {
switch self {
case .responseSerializationFailed(let reason):
return reason.failedStringEncoding
default:
return nil
}
}
這里是一個(gè)小的分割線,在上邊屬性的獲取中搂誉,也是用到了下邊代碼中的擴(kuò)展功能:
extension AFError.ParameterEncodingFailureReason {
var underlyingError: Error? {
switch self {
case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
return error
default:
return nil
}
}
}
extension AFError.MultipartEncodingFailureReason {
var url: URL? {
switch self {
case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url),
.bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url),
.bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url),
.outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url),
.bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _):
return url
default:
return nil
}
}
var underlyingError: Error? {
switch self {
case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error),
.outputStreamWriteFailed(let error), .inputStreamReadFailed(let error):
return error
default:
return nil
}
}
}
extension AFError.ResponseValidationFailureReason {
var acceptableContentTypes: [String]? {
switch self {
case .missingContentType(let types), .unacceptableContentType(let types, _):
return types
default:
return nil
}
}
var responseContentType: String? {
switch self {
case .unacceptableContentType(_, let responseType):
return responseType
default:
return nil
}
}
var responseCode: Int? {
switch self {
case .unacceptableStatusCode(let code):
return code
default:
return nil
}
}
}
extension AFError.ResponseSerializationFailureReason {
var failedStringEncoding: String.Encoding? {
switch self {
case .stringSerializationFailed(let encoding):
return encoding
default:
return nil
}
}
var underlyingError: Error? {
switch self {
case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error):
return error
default:
return nil
}
}
}
錯(cuò)誤描述
在開發(fā)中拂檩,如果程序遇到錯(cuò)誤稻励,我們往往會給用戶展示更加直觀的信息,這就要求我們把錯(cuò)誤信息轉(zhuǎn)換成易于理解的內(nèi)容加矛。因此我們只要實(shí)現(xiàn)LocalizedError協(xié)議就好了。這里邊的內(nèi)容很簡單毁腿,在這里就直接把代碼寫上了狸棍,不做分析
extension AFError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidURL(let url):
return "URL is not valid: \(url)"
case .parameterEncodingFailed(let reason):
return reason.localizedDescription
case .multipartEncodingFailed(let reason):
return reason.localizedDescription
case .responseValidationFailed(let reason):
return reason.localizedDescription
case .responseSerializationFailed(let reason):
return reason.localizedDescription
}
}
}
extension AFError.ParameterEncodingFailureReason {
var localizedDescription: String {
switch self {
case .missingURL:
return "URL request to encode was missing a URL"
case .jsonEncodingFailed(let error):
return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
case .propertyListEncodingFailed(let error):
return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)"
}
}
}
extension AFError.MultipartEncodingFailureReason {
var localizedDescription: String {
switch self {
case .bodyPartURLInvalid(let url):
return "The URL provided is not a file URL: \(url)"
case .bodyPartFilenameInvalid(let url):
return "The URL provided does not have a valid filename: \(url)"
case .bodyPartFileNotReachable(let url):
return "The URL provided is not reachable: \(url)"
case .bodyPartFileNotReachableWithError(let url, let error):
return (
"The system returned an error while checking the provided URL for " +
"reachability.\nURL: \(url)\nError: \(error)"
)
case .bodyPartFileIsDirectory(let url):
return "The URL provided is a directory: \(url)"
case .bodyPartFileSizeNotAvailable(let url):
return "Could not fetch the file size from the provided URL: \(url)"
case .bodyPartFileSizeQueryFailedWithError(let url, let error):
return (
"The system returned an error while attempting to fetch the file size from the " +
"provided URL.\nURL: \(url)\nError: \(error)"
)
case .bodyPartInputStreamCreationFailed(let url):
return "Failed to create an InputStream for the provided URL: \(url)"
case .outputStreamCreationFailed(let url):
return "Failed to create an OutputStream for URL: \(url)"
case .outputStreamFileAlreadyExists(let url):
return "A file already exists at the provided URL: \(url)"
case .outputStreamURLInvalid(let url):
return "The provided OutputStream URL is invalid: \(url)"
case .outputStreamWriteFailed(let error):
return "OutputStream write failed with error: \(error)"
case .inputStreamReadFailed(let error):
return "InputStream read failed with error: \(error)"
}
}
}
extension AFError.ResponseSerializationFailureReason {
var localizedDescription: String {
switch self {
case .inputDataNil:
return "Response could not be serialized, input data was nil."
case .inputDataNilOrZeroLength:
return "Response could not be serialized, input data was nil or zero length."
case .inputFileNil:
return "Response could not be serialized, input file was nil."
case .inputFileReadFailed(let url):
return "Response could not be serialized, input file could not be read: \(url)."
case .stringSerializationFailed(let encoding):
return "String could not be serialized with encoding: \(encoding)."
case .jsonSerializationFailed(let error):
return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
case .propertyListSerializationFailed(let error):
return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)"
}
}
}
extension AFError.ResponseValidationFailureReason {
var localizedDescription: String {
switch self {
case .dataFileNil:
return "Response could not be validated, data file was nil."
case .dataFileReadFailed(let url):
return "Response could not be validated, data file could not be read: \(url)."
case .missingContentType(let types):
return (
"Response Content-Type was missing and acceptable content types " +
"(\(types.joined(separator: ","))) do not match \"*/*\"."
)
case .unacceptableContentType(let acceptableTypes, let responseType):
return (
"Response Content-Type \"\(responseType)\" does not match any acceptable types: " +
"\(acceptableTypes.joined(separator: ","))."
)
case .unacceptableStatusCode(let code):
return "Response status code was unacceptable: \(code)."
}
}
}
總結(jié)
通過閱讀AFError這篇代碼,給了我很大的震撼涨颜,在代碼的設(shè)計(jì)上庭瑰,可以參考這種設(shè)計(jì)方式弹灭。
由于知識水平有限穷吮,如有錯(cuò)誤,還望指出