This is the 11th day of my participation in the Challenge. For details, see:More article challenges
preface
I’ve been thinking about how to explain enumerations in Swift, which are so familiar that you can’t help but follow programming experience and just list a few states. It’s so strange, and once you understand its wide range of uses, you have to be impressed by how well Apple designed the Enum.
After much thinking, I started with the two most characteristic system enumerations in Swift: Optional and Result. If selected from the famous third library, the detailed AFError in Alamofire is also a good example to fully understand the enum in Swift.
AFError
Since the AFError file in Alamofire is close to 900 lines of comments and code, I’m going to cut down a lot of the examples to give you a good idea of the enum features in Swift.
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 reason.localizedDescription case let .parameterEncoderFailed(reason): return reason.localizedDescription case let .multipartEncodingFailed(reason): return reason.localizedDescription case let .requestAdaptationFailed(error): return "Request adaption failed with error: \(error.localizedDescription)" case let .responseValidationFailed(reason): return reason.localizedDescription case let .responseSerializationFailed(reason): return reason.localizedDescription case let .requestRetryFailed(retryError, originalError): return """ Request retry failed with retry error: \(retryError.localizedDescription), \ original error: \(originalError.localizedDescription) """ 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: \(reason.localizedDescription)" #endif case let .urlRequestValidationFailed(reason): return "URLRequest validation failed due to reason: \(reason.localizedDescription)" case let .createUploadableFailed(error): return "Uploadable creation failed with error: \(error.localizedDescription)" case let .createURLRequestFailed(error): return "URLRequest creation failed with error: \(error.localizedDescription)" case let .downloadedFileMoveFailed(error, source, destination): return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" case let .sessionTaskFailed(error): return "URLSessionTask failed with error: \(error.localizedDescription)" } } } extension AFError.URLRequestValidationFailureReason { var localizedDescription: String { switch self { case let .bodyDataInGETRequest(data): return """ Invalid URLRequest: Requests with GET method cannot have body data: \(String(decoding: data, as: UTF8.self)) """ } } }Copy the code
Let’s start with a bit of analysis:
-
An enum is a special struct!
-
Public enum AFError: Error; public enum AFError: Error; public enum AFError: Error;
-
Enum can bring parameters, we look at this example case urlRequestValidationFailed (reason: URLRequestValidationFailureReason).
The urlRequestValidationFailed took a tiny parameter, this parameter is of type URLRequestValidationFailureReason, carefully watching URLRequestValidationFailureReason, It is also an enumeration and takes arguments:
public enum URLRequestValidationFailureReason {
/// URLRequest with GET method had body data.
case bodyDataInGETRequest(Data)
}
Copy the code
How’s it going? A little dizzy? Is that sour enough? That’s not enough. Here we go:
Enum can not only take parameters, abide by protocols, but also write categories, and extend read-only computing properties and functions. In LocalizedError, we just follow the LocalizedError protocol and implement the var errorDescription: String? .
Let’s look at one of the implementations of this read-only computation property:
case let .urlRequestValidationFailed(reason):
return "URLRequest validation failed due to reason: \(reason.localizedDescription)"
Copy the code
This case let means: if it is urlRequestValidationFailed this kind of situation, for the reason of enumeration parameter, and the string expression, the expression in addition to the above, in the writing of often there are the following:
/ / / will let in the enumeration value, personally, I prefer this kind of writing case, urlRequestValidationFailed (let reason) : / / / don't care about the enumeration belt, replace case with _. UrlRequestValidationFailed (_) : / / / show only the enumeration of state directly, omit parameter display case. UrlRequestValidationFailed:Copy the code
Last the extension AFError. URLRequestValidationFailureReason shows embedded in AFError URLRequestValidationFailureReason type classification should be how to write.
How’s that? Is there a lot to be gained from just looking at Alamofire’s AFError?
The enum in Swift is by far the most powerful enum in any programming language I’ve studied.
Result
The following is the official Result source code, I only write the trunk function, you see, think enum and support what function?
@frozen public enum Result<Success, Failure> where Failure : Error { /// A success, storing a `Success` value. case success(Success) /// A failure, Case Failure (Failure) /// 保 存 的 信 息Copy the code
Gitignore was created six years ago, so we can see that the “Result” type appears not by accident, but more because of functional demands.
There are two common situations in asynchronous callbacks today: callback success or callback failure. In the early days of Swift callbacks, we often saw this:
typealias Callback<Success, SomeError: Error> = (Success? , SomeError?) -> VoidCopy the code
Since I know whether the callback was successful or not, both Success and SomeError are optional types. Using the Result type, we can write:
typealias ResultCallback<Success, SomeError: Error> = (Result<Success, SomeError>) -> Void
Copy the code
The results of callbacks, which are true in two cases — case success and case Failure — directly reduce the use of optional types, thus simplifying the null-finding logic.
The Result enumeration shows that the enum in Swift supports generics.
If you’ve been using Alamofire and Kingfisher, you’ll notice that the callback transitions in both of them have evolved around the closure examples above.
Finally, a look at the optional types in Swift.
Optional
The following is the official source code of Optional, I only write the trunk 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)
}
Copy the code
And the String that I see a lot? This is just the sugar syntax for Optional
.
Finally, a typical example of enums
enum NumberType: String {
case one, two, three, four, five
}
let s: NumberType = .one
print(s.rawValue)
Copy the code
S.raven value is of type String, and the String “one” is printed.
Enum NumberType: String essentially tells the compiler that the rawValue in the enumeration is of type String.
Consider the following situation:
enum NumberValue: Int {
case one = 3, two, three, four, five
}
Copy the code
Numbervalue.two. What is rawValue?
conclusion
The enum in Swift is not just a simple state type as it used to be. It has the following properties:
-
Support agreement compliance
-
Support generics
-
Support for writing extensions
-
Support with parameters
-
Supports the inheritance of basic data types and represents the type of the enumerated rawValue
There are other interesting features that may not be fully covered yet, but what I would like to say is that the design of enUms is disruptive, and using them flexibly is the most difficult.
Tomorrow to continue
It is the Dragon Boat Festival holiday, and the rest day is my most difficult moment, the reason why I will first explain enum because it and we need to explain the network request encapsulation library Moya is very close.
Come on, everybody!