Created
October 8, 2024 19:43
-
-
Save DandyLyons/ab101ecaa4d8a73c4202ed2cc8a0a12d to your computer and use it in GitHub Desktop.
Typed throws in Swift (multiple types)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| struct IceCreamShop { | |
| enum Error: Swift.Error { | |
| case notEnoughMoney | |
| case flavorNotSoldHere | |
| case flavorError(IceCreamFlavor.Error) | |
| } | |
| private(set) var availableFlavors: [String: IceCreamFlavor] | |
| private(set) var cashOnHand: Int | |
| private(set) var isFreezerOn = true | |
| private(set) var billLastPaidOn: Date | |
| mutating func sellIceCream(flavorName: String) throws(Self.Error) { | |
| guard isFlavorSoldHere(flavorName) else { throw .flavorNotSoldHere } | |
| do throws(IceCreamFlavor.Error) { | |
| try availableFlavors[flavorName]?.scoop() | |
| } catch let error { | |
| throw Self.Error.flavorError(error) | |
| } | |
| cashOnHand += 1 | |
| try payBillIfNecessary() | |
| } | |
| mutating func restockIceCream(flavorName: String, amount: Int) throws(Self.Error) { | |
| guard cashOnHand > amount else { throw .notEnoughMoney } | |
| guard isFlavorSoldHere(flavorName) else { throw .flavorNotSoldHere } | |
| availableFlavors[flavorName]?.refillIceCream(amount: amount) | |
| try payBillIfNecessary() | |
| } | |
| func isFlavorSoldHere(_ flavorName: String) -> Bool { | |
| availableFlavors.keys.contains(flavorName) | |
| } | |
| mutating func payBillIfNecessary() throws(Self.Error) { | |
| // pay bills if it has been more than a month since you last paid bills | |
| if Calendar.current.dateComponents([.month], from: billLastPaidOn, to: Date()).month! > 1 { | |
| try payBills() | |
| } | |
| } | |
| mutating func payBills() throws(Self.Error) { | |
| guard cashOnHand > 500 else { | |
| isFreezerOn = false | |
| throw .notEnoughMoney | |
| } | |
| billLastPaidOn = Date() | |
| isFreezerOn = true | |
| cashOnHand -= 500 | |
| } | |
| } | |
| struct IceCreamFlavor: Hashable { | |
| enum Error: Swift.Error { | |
| case flavorOutOfStock | |
| case iceCreamMelted | |
| } | |
| private(set) var scoopsLeft: Int | |
| var isMelted = false | |
| mutating func scoop() throws(Self.Error) { | |
| guard !isMelted else { throw .iceCreamMelted } | |
| guard scoopsLeft > 0 else { throw .flavorOutOfStock } | |
| scoopsLeft -= 1 | |
| } | |
| mutating func refillIceCream(amount: Int) { | |
| scoopsLeft += amount | |
| } | |
| } | |
| /// an example of what the call site could look like | |
| func timeline() { | |
| var iceCreamShop = IceCreamShop( | |
| availableFlavors: [ | |
| "Strawberry": IceCreamFlavor(scoopsLeft: 200), | |
| "Chocolate": IceCreamFlavor(scoopsLeft: 1) | |
| ], | |
| cashOnHand: 20, | |
| billLastPaidOn: Date(timeIntervalSince1970: 0) | |
| ) | |
| // ๐๐ผ This exhaustively handles every possible error, even across multiple types. | |
| do throws(IceCreamShop.Error) { | |
| try iceCreamShop.sellIceCream(flavorName: "Strawberry") // throws IceCreamShop.Error | |
| } catch { | |
| switch error { | |
| case .flavorNotSoldHere: | |
| print("Sir, this is a Baskin Robbins.") | |
| case let .flavorError(flavorError): | |
| switch flavorError { | |
| case .flavorOutOfStock: | |
| print("Shucks! We're out of that flavor.") | |
| case .iceCreamMelted: | |
| print("...would you like a milk drink instead? ๐ ") | |
| } | |
| case .notEnoughMoney: | |
| print("Time for us to go out of business...") | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment