Skip to content

Nested Types

You can define types within the scope of another type in Swift. This is useful for creating supporting types like enumerations, structures, or protocols that are closely tied to a main type, such as a class or structure.

For instance, enums often enhance a specific type's features. Utility structures or protocols can also be nested for use only within that type's context. Nested types can go as deep as needed by placing their definitions inside the outer type's braces.

Nested Types Example

Consider a structure called PlayingCard that represents a standard playing card. It includes two nested enums: Suit and Rank.

In many card games, face cards (jack, queen, king) have a value of 10, while aces can be 1 or 11. This is handled by a nested structure Values inside the Rank enum.

swift
struct PlayingCard {
    // Nested Suit enum
    enum Suit: Character {
        case clubs = "♣", diamonds = "♦", hearts = "♥", spades = "♠"
    }

    // Nested Rank enum
    enum Rank: Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
        
        struct Values {
            let primary: Int
            let alternative: Int?
        }
        
        var values: Values {
            switch self {
            case .ace:
                return Values(primary: 1, alternative: 11)
            case .jack, .queen, .king:
                return Values(primary: 10, alternative: nil)
            default:
                return Values(primary: self.rawValue, alternative: nil)
            }
        }
    }

    // PlayingCard properties and methods
    let rank: Rank
    let suit: Suit
    
    var description: String {
        var details = "Suit: \(suit.rawValue), Value: \(rank.values.primary)"
        if let alt = rank.values.alternative {
            details += " or \(alt)"
        }
        return details
    }
}

The Suit enum lists the four suits with raw Character values for their symbols.

The Rank enum covers the 13 ranks with raw Int values for numeric cards (not used for face cards or aces).

Inside Rank, the Values structure captures single or dual values: primary as Int and alternative as optional Int.

The computed property values in Rank returns a Values instance based on the rank.

PlayingCard has properties rank and suit, plus a computed description that builds a string using these values and checks for an alternative value.

Since PlayingCard lacks custom initializers, it gets a default memberwise initializer:

swift
let sampleCard = PlayingCard(rank: .ace, suit: .hearts)
print("Sample Card: \(sampleCard.description)")
// Prints "Sample Card: Suit: ♥, Value: 1 or 11"

Here, types are inferred, so enum cases like .ace and .hearts work without full qualification.

Accessing Nested Types

Outside the parent type, reference nested types by prefixing with the parent's name:

swift
let diamondSymbol = PlayingCard.Suit.diamonds.rawValue
// diamondSymbol is "♦"

This keeps nested type names short inside their context but fully qualified elsewhere.

Released under the MIT License.