I added a question mark to the init
keyword in the Monster
class to specify a failable initializer. I also added a guard statement to prevent monsters from having an empty string for a name.
//
// Monster.swift
// MonsterTown
//
import Foundation
class Monster {
static let isTerrifying = true
class var spookyNoise: String {
return "Grrr"
}
var town: Town?
var name: String
var victimPool: Int {
get {
return town?.population ?? 0
}
set(newVictimPool) {
town?.population = newVictimPool
}
}
required init?(town: Town?, monsterName: String) {
guard monsterName != "" else {
return nil
}
self.town = town
name = monsterName
}
func terrorizeTown() {
if town != nil {
print("\(name) is terrorizing a town!")
} else {
print("\(name) hasn't found a town to terrorize yet...")
}
}
}
The change to the Monster
class necessitated changes to the Zombie
class: I made all of the initializers failable by adding question marks to them.
//
// Zombie.swift
// MonsterTown
//
import Foundation
class Zombie: Monster {
class override var spookyNoise: String {
return "Brains..."
}
var walksWithLimp: Bool
private(set) var isFallingApart: Bool
init?(limp: Bool,
fallingApart: Bool,
town: Town?,
monsterName: String) {
walksWithLimp = limp
isFallingApart = fallingApart
super.init(town: town, monsterName: monsterName)
}
convenience init?(limp: Bool, fallingApart: Bool) {
self.init(limp: limp,
fallingApart: fallingApart,
town: nil,
monsterName: "Fred")
if walksWithLimp {
print("This zombie has a bad knee.")
}
}
convenience required init?(town: Town?, monsterName: String) {
self.init(limp: false,
fallingApart: false,
town: town,
monsterName: monsterName)
}
deinit {
print("Zombie \(name) is no longer with us.")
}
func regenerate() {
walksWithLimp = false
}
override func terrorizeTown() {
if !isFallingApart {
town?.changePopulation(by: -10)
}
super.terrorizeTown()
regenerate()
}
}
I added a proof of concept to main.swift
where I attempt to create a new zombie instance with an empty string.
//
// main.swift
// MonsterTown
//
import Foundation
var myTown = Town(population: 0, stoplights: 6)
myTown?.printDescription()
let myTownSize = myTown?.townSize
print(String(describing: myTownSize))
myTown?.changePopulation(by: 1_000_000)
print("Size: \(String(describing: myTown?.townSize)); population: "
+ "\(String(describing: myTown?.population))")
var fredTheZombie: Zombie? = Zombie(limp: false,
fallingApart: false,
town: myTown,
monsterName: "Fred")
fredTheZombie?.terrorizeTown()
fredTheZombie?.town?.printDescription()
var convenientZombie = Zombie(limp: true, fallingApart: false)
// Silver challenge
var convenientZombieTwo = Zombie(town: myTown, monsterName: "Barney")
convenientZombieTwo?.terrorizeTown()
// Gold challenge
var failableZombie = Zombie(town: myTown, monsterName: "")
failableZombie?.terrorizeTown() // This shouldn't unwrap and terrorize
// because it is nil
print("Victim pool: \(String(describing: fredTheZombie?.victimPool))")
fredTheZombie?.victimPool = 500
print("Victim pool: \(String(describing: fredTheZombie?.victimPool))")
print(Zombie.spookyNoise)
if Zombie.isTerrifying {
print("Run away!")
}
fredTheZombie = nil