Swift - Type Casting
Published: 2022-04-05 09:38:16

Type casting is a way to treat instance of a type as a different superclass or subclass and to check the type of an instance, implemented with the **as** and **is** operators.

It can be also used to check whether a type conforms to a protocol.

### Checking Type
- **is** operator is used to check whether an instance is of a certain type or it's subclass type.

example :

``` swift
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
```

``` swift
class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}
```

``` swift
let library = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]

var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}
```

---

### Downcasting
A constant or variable of a certain class type may actually refer to an instance of a subclass using type cast operator (as? or as!).

1. as?, returns an optional value
2. as!, attempts the downcast and force-unwraps the result

Use as! when you are sure that downcast will succeed and as? when you are not sure.

example : 

``` swift
for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}
```
---

### Type Casting for Any and AnyObject
Swift provides two special types for working with nonspecific types:

1. **Any** can represent an instance of any type at all, including function types.
2. **AnyObject** can represent an instance of any class type.

``` swift
var things: [Any] = []

things.append(0)
things.append(0.0)
things.append(3.14159)
things.append("hello")
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
```

- The Any type represents values of any type, including optional types. Swift gives you a warning if you use an optional value where a value of type Any is expected. If you really do need to use an optional value as an Any value, you can use the as operator to explicitly cast the optional to Any.

example :

``` swift
let optionalNumber: Int? = 3
things.append(optionalNumber)        // Warning
things.append(optionalNumber as Any) // No warning
```

---

To discover the specific type of a constant or variable that’s known only to be of type Any or AnyObject, you can use an is or as pattern in a switch statement’s cases.

example :

``` swift
for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}
```