“Unfortunately, at this point, Swift’s do-try-catch system isn’t type-specific. There’s no way to tell the compiler that it should only expect PugBotErrors. To the compiler, that isn’t exhaustive, because it doesn’t handle each and every possible error that it knows about, so you still need a default case”
A type can conform to the Error protocol to work with Swift’s error-handling system.
Any function that can throw an error, or call a function that can throw an error, has to be marked with throws or rethrows.
When calling an error-throwing function, you must embed the function call in a do block. Within that block, you try the function, and if it fails, you catch the error.
let value = Int("3")
// 透過Failable initializers來回傳error情況的case
let failedValue = Int("nope") // nil
enum PetFood: String {
case kibble, canned
}
let morning = PetFood(rawValue: "kibble")
// 透過Failable initializers來回傳error情況的case
let snack = PetFood(rawValue: "fuuud!") // nil
struct PetHouse {
let squareFeet: Int
// 如何自己寫一個Failable initializers
init?(squareFeet: Int) {
if squareFeet < 1 {
return nil
}
self.squareFeet = squareFeet
}
}
let tooSmall = PetHouse(squareFeet: 0)
let house = PetHouse(squareFeet: 1)
class Toy {
enum Kind {
case ball
case zombie
case bone
case mouse
}
enum Sound {
case squeak
case bell
}
let kind: Kind
let color: String
var sound: Sound?
init(kind: Kind, color: String, sound: Sound? = nil) {
self.kind = kind
self.color = color
self.sound = sound
}
}
class Pet {
enum Kind {
case dog
case cat
case guineaPig
}
let name: String
let kind: Kind
let favoriteToy: Toy?
init(name: String, kind: Kind, favoriteToy: Toy? = nil) {
self.name = name
self.kind = kind
self.favoriteToy = favoriteToy
}
}
class Person {
let pet: Pet?
init(pet: Pet? = nil) {
self.pet = pet
}
}
// 複雜的資料結構
let janie = Person(pet: Pet(name: "Delia", kind: .dog, favoriteToy: Toy(kind: .ball, color: "Purple", sound: .bell)))
let tammy = Person(pet: Pet(name: "Evil Cat Overlord", kind: .cat, favoriteToy: Toy(kind: .mouse, color: "Orange")))
let felipe = Person()
// 透過Optional chaining快速判斷某個值是否nil
if let sound = janie.pet?.favoriteToy?.sound {
print("Sound \(sound)")
} else {
print("No sound.")
}
if let sound = tammy.pet?.favoriteToy?.sound {
print("Sound \(sound)")
} else {
print("No sound.")
}
if let sound = felipe.pet?.favoriteToy?.sound {
print("Sound \(sound)")
} else {
print("No sound.")
}
let team = [janie, tammy, felipe]
let petNames = team.map { $0.pet?.name }
for pet in petNames {
// compiler warns you about conversion from Optional to Any
print(pet as Any) // cast to Any to shut the warning off
// Optional("Delia")
// Optional("Evil Cat Overlord")
// nil
}
// compactMap, 具有map的功能,另外還處理array萬一有nil的情況
let betterPetNames = team.compactMap { $0.pet?.name }
for pet in betterPetNames {
print(pet)
// Delia
// Evil Cat Overlord
}
class Pastry {
let flavor: String
var numberOnHand: Int
init(flavor: String, numberOnHand: Int) {
self.flavor = flavor
self.numberOnHand = numberOnHand
}
}
// 實作error protocol
enum BakeryError: Error {
case tooFew(numberOnHand: Int)
case doNotSell
case wrongFlavor
}
let bakery = Bakery()
do {
// 此種try需要根據不同的error進行對應的處理
try bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")
} catch BakeryError.doNotSell {
print("Sorry, but we don't sell this item")
} catch BakeryError.wrongFlavor {
print("Sorry, but we don't carry this flavor")
} catch BakeryError.tooFew {
print("Sorry, we don't have enough items to fulfill your order")
}
let remaining = try? bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")