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.
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:
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:
let diamondSymbol = PlayingCard.Suit.diamonds.rawValue
// diamondSymbol is "♦"This keeps nested type names short inside their context but fully qualified elsewhere.