- Hands-On Design Patterns with Swift
- Florent Vilmart Giordano Scalzo Sergio De Simone
- 501字
- 2021-07-02 14:45:09
Throwing and catching errors
Swift has an error handling mechanism that is based on throwing and catching errors, very similar to JavaScript or Java. In Swift, Error is simply a protocol. Any of your custom types conforming to the Error protocol can be used, and can also be thrown. This provides us with the ability to be as expressive as possible with the underlying types that we'll be using.
Unlike in Java, for example, it is not possible to specialize error catching, but you can always leverage pattern matching to determine the kind of error that's been thrown. You may want to define functions that throw, in order to indicate that an abnormal operation has run, and therefore, the execution of the program should properly handle these abnormal operations.
Let's look at our credit card example and suppose that we can create charges on a prepaid card:
class CreditCard {
private(set) var balance: Int // balance is in cents
init(balance: Int) {
self.balance = balance
}
func charge(amount: Int) {
balance -= amount
}
}
let card = CreditCard(balance: 10000)
card.charge(amount: 2000)
card.balance == 8000
This implementation is now completely unsafe, due to the following:
- A developer can increment the balance by providing a negative amount
- The balance can become negative, and we may want to include restrictions
We could use UInt instead of Int, but, for the sake of the example, we'll write our own error handling logic.
Let's rewrite our example, as follows:
class CreditCard {
Let's define ChargeError as an enum, which encompasses the two expected errors. Using an enum lets you safely define a finite possibility of values, while ensuring that the consumer will always implement all of the cases, and therefore, handle all of the error types:
enum ChargeError: Error {
case invalidAmount
case insufficientFunds
}
/* unchanged implementation of init / balance */
We can now mark our charge method with the throws keyword, to indicate to anyone using this method that it can fail:
func charge(amount: Int) throws {
guard amount >= 0 else { throw ChargeError.invalidAmount }
guard balance >= amount else { throw ChargeError.insufficientFunds }
balance -= amount
}
}
Let's take a look at how to use this API now:
The first way is to use the do...catch pattern and cast the error as the one thrown, as follows:
let card = CreditCard(balance: 10000)
do {
try card.charge(amount: 2000)
} catch CreditCard.ChargeError.invalidAmount {
/* handle invalidAmount */
} catch CreditCard.ChargeError.insufficientFunds {
/* handle insufficientFunds */
} catch {}
If you're not interested in catching errors, you can also use try?:
try? card.charge(amount: -1000) // this will fail safely and nicely
Error handling is a fundamental feature in Swift, when you need to interact with failable code. Throughout the course of this book, you'll see other patterns that encapsulate error management differently, and which many Swift users prefer over the default do...catch...throw pattern. However, for now let's continue our exploration of the Standard Library with the different container types.
- Libgdx Cross/platform Game Development Cookbook
- Spark核心技術與高級應用
- 數亦有道:Python數據科學指南
- Spark大數據編程實用教程
- INSTANT Apple iBooks How-to
- 淘寶、天貓電商數據分析與挖掘實戰(第2版)
- Hadoop集群與安全
- 大數據技術原理與應用:概念、存儲、處理、分析與應用
- 中國云存儲發展報告
- Unity Game Development Blueprints
- 數字化轉型實踐:構建云原生大數據平臺
- Delphi High Performance
- 工業大數據融合體系結構與關鍵技術
- Discovering Business Intelligence Using MicroStrategy 9
- 數據庫高效優化:架構、規范與SQL技巧