Type Casting, Nested Types and Extensions

18.1 Type Casting

class MediaItem { let name: String; init(name: String) { self.name = name } }
class Movie: MediaItem { let director = "Unknown" }
class Song: MediaItem { let artist = "Unknown" }

let library: [MediaItem] = [Movie(name: "Inception"), Song(name: "Imagine")]
for item in library {
    if let movie = item as? Movie {           // conditional downcast
        print("Movie by \(movie.director)")
    } else if item is Song {                  // type check
        print("Song")
    }
}
let forced = library[0] as! Movie             // forced downcast (crashes if wrong)

var things: [Any] = [0, 3.14, "hello", (1, 2), { (s: String) in s }]
for thing in things {
    switch thing {
    case let i as Int: print("Int \(i)")
    case let s as String: print("String \(s)")
    case let (x, y) as (Int, Int): print("tuple \(x), \(y)")
    default: print("other")
    }
}
let anyObject: AnyObject = Movie(name: "X")   // any class instance

18.2 Nested Types

struct BlackjackCard {
    enum Suit: Character { case spades = "♠", hearts = "♡" }
    enum Rank: Int {
        case two = 2, three, ace = 14
        struct Values { let first: Int, second: Int? }
        var values: Values { self == .ace ? Values(first: 1, second: 11) : Values(first: rawValue, second: nil) }
    }
    let rank: Rank, suit: Suit
}
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue

18.3 Extensions

Extensions can add computed properties, methods, initializers, subscripts, nested types, and protocol conformances, but not stored properties or overrides of existing functionality.

extension Double {
    var km: Double { self * 1_000 }
    var m: Double { self }
}
let marathon = 42.195.km

extension Int {
    func repetitions(_ task: () -> Void) { for _ in 0..<self { task() } }
    mutating func square() { self = self * self }
    subscript(digitIndex: Int) -> Int {
        var base = 1
        for _ in 0..<digitIndex { base *= 10 }
        return (self / base) % 10
    }
}
3.repetitions { print("Hi") }
746381295[0]    // 5

extension Point: CustomStringConvertible {          // add protocol conformance
    var description: String { "(\(x), \(y))" }
}

extension Rectangle {
    init(square side: Double) { self.init(width: side, height: side) }   // keeps the memberwise init
}