Swift: Typecasing

Enums …as data models?

Andyy Hope
Aug 17, 2016 · 6 min read

The crisis

"characters" : [
{
type: "hero",
name: "Jake",
power: "Shapeshift"
},
{
type: "hero",
name: "Finn",
power: "Grass sword"
},
{
type: "princess",
name: "Lumpy Space Princess",
kingdom: "Lumpy Space"
},
{
type: "civilian",
name: "BMO"
},
{
type: "princess",
name: "Princess Bubblegum",
kingdom: "Candy"
}
]

Classes and inheritance

class Character {
type: String
name: String
}
class Hero: Character {
power: String
}
class Princess: Character {
kingdom: String
}
class Civilian: Character {
}
...struct Model {
characters: [Character]
}
// Type checkingif model.characters[indexPath.row] is Hero {
print(model.characters[indexPath.row].name)
}
// Type checking and Typecastingif let hero = model.characters[indexPath.row] as? Hero {
print(hero.power)
}

Structs and protocols

protocol Character {
var type: String { get set }
var name: String { get set }
}
struct Hero: Character {
power: String
}
struct Princess: Character {
kingdom: String
}
struct Civilian: Character {
}
...struct Model {
characters: [Character]
}
// Type checkingif model.characters[indexPath.row] is Hero {
print(model.characters[indexPath.row].name)
}
// Type checking and Typecastingif let hero = model.characters[indexPath.row] as? Hero {
print(hero.power)
}

Typecasting

{
type: "king"
name: "Ice King"
power: "Frost"
}

The opportunity

Enums

enum Character {
case hero, princess, civilian
}

Associated values

enum Character {
case hero(Hero)
case princess(Princess)
case civilian(Civilian)
}
...switch characters[indexPath.row] {
case .hero(let hero):
print(hero.power)
case .princess(let princess):
print(princess.kingdom)
case .civilian(let civilian):
print(civilian.name)
}

Raw Value

enum Character: String { // Error: ❌
case hero(Hero)
case princess(Princess)
case civilian(Civilian)
}

Initialising

init

enum Type

enum Character {
private enum Type: String {
case hero, princess, civilian
static let key = "type"
}
}

Failable initializers

// enum Characterinit?(json: [String : AnyObject]) {
guard
let string = json[Type.key] as? String,
let type = Type(rawValue: string)
else { return nil }
switch type {
case .hero:
guard let hero = Hero(json: json)
else { return nil }

self = .hero(hero)

case .princess:
guard let princess = Princess(json: json)
else { return nil }
self = .princess(princess) case .civilian:
guard let civilian = Civilian(json: json)
else { return nil }
self = .civilian(civilian)
}
}

Parsing

// Model initialisationif let characters = json["characters"] as? [[String : AnyObject]] {
self.characters = characters.flatMap { Character(json: $0) }
}

Typecasing

switch model.characters[indexPath.row] {
case .hero(let hero):
print(hero.power)

case .princess(let princess):
print(princess.kingdom)

case .civilian(let civilian):
print(civilian.name)
}

BONUS: Typecasing with Pattern Matching

if case
func printPower(character: Character) {
switch character {
case .hero(let hero):
print(hero.power)
default:
break
}
func printPower(character: Character) {
if case .hero(let hero) = character {
print(hero.power)
}
}

Swift Programming

The Swift Programming Language

Thanks to Benjamin J. Dietzkis and Tony Arnold.

Andyy Hope

Written by

Australian iOS Engineer living in SF, Blogger, Speaker, Organiser of Playgrounds Conference, Founder of Wu-Tang Clang

Swift Programming

The Swift Programming Language