Preface
I have been thinking about how to explain the enum types in Swift. It is so familiar that you can't help but follow your programming experience. If you list a few states, you will feel that everything will be fine. It is so strange that when you deeply understand its wide range of uses, you have to admire how eye-catching Apple is in designing enum.
After thinking about it, I started with the two most distinctive system enumerations in Swift: Optional and Result. If you choose from the famous third library, the detailed AFError in Alamofire is also a good example of a comprehensive understanding of enums in Swift.
AFError
Since the comments and code in the AFError file in Alamofire are nearly 900 lines, I will simplify a large part of it in my examples, and let everyone understand the characteristics of enums in Swift by throwing bricks and attracting jade.
import Foundation public enum AFError: Error { /// The underlying reason the `.urlRequestValidationFailed` public enum URLRequestValidationFailureReason { /// URLRequest with GET method had body data. case bodyDataInGETRequest(Data) } /// `URLRequest` failed validation. case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) } // MARK: - Error Descriptions extension AFError: LocalizedError { public var errorDescription: String? { switch self { case .explicitlyCancelled: return "Request explicitly cancelled." case let .invalidURL(url): return "URL is not valid: \(url)" case let .parameterEncodingFailed(reason): return case let .parameterEncoderFailed(reason): return case let .multipartEncodingFailed(reason): return case let .requestAdaptationFailed(error): return "Request adaption failed with error: \()" case let .responseValidationFailed(reason): return case let .responseSerializationFailed(reason): return case let .requestRetryFailed(retryError, originalError): return """ Request retry failed with retry error: \(), \ original error: \() """ case .sessionDeinitialized: return """ Session was invalidated without error, so it was likely deinitialized unexpectedly. \ Be sure to retain a reference to your Session for the duration of your requests. """ case let .sessionInvalidated(error): return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" #if !(os(Linux) || os(Windows)) case let .serverTrustEvaluationFailed(reason): return "Server trust evaluation failed due to reason: \()" #endif case let .urlRequestValidationFailed(reason): return "URLRequest validation failed due to reason: \()" case let .createUploadableFailed(error): return "Uploadable creation failed with error: \()" case let .createURLRequestFailed(error): return "URLRequest creation failed with error: \()" case let .downloadedFileMoveFailed(error, source, destination): return "Moving downloaded file from: \(source) to: \(destination) failed with error: \()" case let .sessionTaskFailed(error): return "URLSessionTask failed with error: \()" } } } extension { var localizedDescription: String { switch self { case let .bodyDataInGETRequest(data): return """ Invalid URLRequest: Requests with GET method cannot have body data: \(String(decoding: data, as: )) """ } } }
Let's analyze it little by little:
- Enum is a special struct!
- Enum can abide by the agreement, you can see that it starts with
public enum AFError: Error
, means that this AFError complies with the Error protocol. If you are interested, you can see what the Error is. - Enum can take parameters, let's look at this example
case urlRequestValidationFailed(reason: URLRequestValidationFailureReason)
。
This urlRequestValidationFailed brings a reason parameter, the type of this parameter is URLRequestValidationFailureReason. If you look at URLRequestValidationFailureReason carefully, it is actually an enum and has parameters:
public enum URLRequestValidationFailureReason { /// URLRequest with GET method had body data. case bodyDataInGETRequest(Data) }
How about it, are you a little dizzy? Is it so sour enough? This is not enough, let's continue to look at:
Enum can not only take parameters and comply with protocols, but also write categories and extend read-only computing properties and functions.extension AFError: LocalizedError
It is to comply with the LocalizedError protocol and implement the properties in the LocalizedError protocol.var errorDescription: String?
。
Let's take a look at one of the implementations of this read-only computed attribute:
case let .urlRequestValidationFailed(reason): return "URLRequest validation failed due to reason: \()"
The meaning of this case let is: If it is urlRequestValidationFailed, obtain the reason parameter in this enum and perform string expression. In addition to the above expression, there are several common writing methods:
/// Place lets where the enum value is taken. I personally like this writing methodcase .urlRequestValidationFailed(let reason): /// Don't care about enumeration with parameters, use _ insteadcase .urlRequestValidationFailed(_): /// Directly display only the enumeration status, omitting parameter displaycase .urlRequestValidationFailed:
The last oneextension
Shows how the URLRequestValidationFailureReason type embedded in AFError should be written in a classification.
How about it? Just looking at Alamofire's AFError will bring you a lot of gains, right?
The enum in Swift is the most powerful enum among the programming languages I have learned so far.
Result
The following is the official source code of Result. I only wrote the main function. After reading it, I think about what functions does enum support?
@frozen public enum Result<Success, Failure> where Failure : Error { /// A success, storing a `Success` value. case success(Success) /// A failure, storing a `Failure` value. case failure(Failure) /// I omitted other content}
The official Result enumeration was officially launched after Swift5, and Github is open source, we can look at this libraryResult, its .gitignore was created 6 years ago. It can be seen that the emergence of the Result type is not accidental, but it appears more because of functional appeals.
Among the current asynchronous callbacks, there are two most common cases: whether the callback is successful or the callback fails. In the early Swift callbacks, we often see this writing:
typealias Callback<Success, SomeError: Error> = (Success?, SomeError?) -> Void
Because I know whether the callback is successful, Success and SomeError are optional types. After using the Result type, we can write it like this:
typealias ResultCallback<Success, SomeError: Error> = (Result<Success, SomeError>) -> Void
There are two situations for the result of the callback, which is true and reliable - case success and case failure, which directly reduces the use of optional types and simplifies the logic of judgment.
Through the Result enum, we can see that enums in Swift support generics! ! !
If you have been using Alamofire and Kingfisher, you will find that the callback changes in them also evolved around the examples of the two closures above.
Finally, let’s introduce the optional types in Swift.
Optional
The following is the official Optional source code. I only wrote the main function. Is it very similar to Result?
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral { /// The absence of a value. /// /// In code, the absence of a value is typically written using the `nil` /// literal rather than the explicit `.none` enumeration case. case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) }
And what I see oftenString?
This is justOptional<String>
The sugar grammar is just.
Finally, give a typical example of enum
enum NumberType: String { case one, two, three, four, five } let s: NumberType = .one print()
The type is String, and the printed string "one".
Thisenum NumberType: String
Writing method, essentially tells the compiler that the rawValue value in the enumeration is String type. How about it? Is it a little dizzy?
Let’s think about the following situation:
enum NumberValue: Int { case one = 3, two, three, four, five }
What is it?
Summarize
Enum in Swift is not only a simple type that only represents state in past cognition, it has the following characteristics:
- Support compliance with the agreement
- Support generic usage
- Support writing extensions
- Support with parameters
- Support inheritance of basic data types, representing the type of rawValue of enumeration
There are other interesting features that may not be summarized in full. What I want to express is that the design of enum is subversive, and the most difficult thing is to flexibly use these enum features.
The reason I explained enum first is that it is very close to the network request encapsulation library Moya that we need to explain.
The above is the detailed explanation of the use of Swift enum enum type. For more information about Swift enum enum type, please follow my other related articles!