iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🕊

[Swift] Why You Must Manually Define allCases for Enums with Associated Values When Conforming to CaseIterable

に公開

What this article covers

  • When conforming to the CaseIterable protocol for enums with associated values, you must manually define allCases.

Concrete Examples

Example 1: Standard usage of CaseIterable

enum Color: CaseIterable {
    case red, green, blue
}

Color.allCases.forEach {
    print($0)
}

// Output
//
// red
// green
// blue

Example 2: Using CaseIterable with enums with associated values

For an enum with associated values like the one below, adding CaseIterable will trigger the compiler error: Type 'Trade' does not conform to protocol 'CaseIterable'.

enum Trade: CaseIterable { // Type 'Trade' does not conform to protocol 'CaseIterable'
    case buy(stock: String, amount: Int)
    case sell(stock: String, amount: Int)
}

Therefore, you must define allCases yourself and provide specific values as follows:

enum Trade: CaseIterable {
    // Must define allCases manually
    static var allCases: [Trade] {
        return [
            .buy(stock: "", amount: 0),
            .sell(stock: "", amount: 0)
        ]
    }
    
    case buy(stock: String, amount: Int)
    case sell(stock: String, amount: Int)
}

Trade.allCases.forEach {
    print($0)
}

// Output
//
// buy(stock: "", amount: 0)
// sell(stock: "", amount: 0)

Example 3: When the "associated value" is itself an enum that conforms to CaseIterable

So, what happens if the associated value of an enum is another enum that conforms to CaseIterable? You might hope the compiler would be smart enough to handle this automatically.

After testing this, it turns out the compiler still complains, and you are required to define allCases yourself (sadly).

Here is how you would have to write it out manually:

enum FinancialProduct: CaseIterable {
    case stock
    case gold
    case crypto
}

enum Trade: CaseIterable {
    static var allCases: [Trade] {
        return [
            FinancialProduct.allCases.map { .buy(financialProduct: $0) },
            FinancialProduct.allCases.map { .sell(financialProduct: $0) },
        ].flatMap { $0 }
    }
        
    case buy(financialProduct: FinancialProduct)
    case sell(financialProduct: FinancialProduct)
}

Trade.allCases.forEach { trade -> () in
    switch trade {
    case let .buy(financialProduct):
        print("buy: \(financialProduct)")
    case let .sell(financialProduct):
        print("sell: \(financialProduct)")
    }
}

// Output
//
// buy: stock
// buy: gold
// buy: crypto
// sell: stock
// sell: gold
// sell: crypto

Conclusion

  • Be careful when using CaseIterable with enum types that have associated values.
GitHubで編集を提案

Discussion