SoFunction
Updated on 2025-04-09

Detailed explanation of the use of Swift enum enum type

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 withpublic 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 examplecase 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: LocalizedErrorIt 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: StringWriting 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!