Last active
August 2, 2022 15:00
-
-
Save LeeKahSeng/a347da1778c71f55537eda991bd56c64 to your computer and use it in GitHub Desktop.
Full sample code for article "How to Achieve Dynamic Dispatch Using Generic Protocols in Swift 5.7" (https://swiftsenpai.com/uncategorized/dynamic-dispatch-with-generic-protocols)
This file contains 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
protocol Vehicle { | |
associatedtype FuelType: Fuel | |
var name: String { get } | |
func startEngin() | |
func fillGasTank(with fuel: FuelType) | |
} | |
struct Car: Vehicle { | |
let name: String | |
func startEngin() { | |
print("\(name) enjin started!") | |
} | |
func fillGasTank(with fuel: Gasoline) { | |
print("Fill \(name) with \(fuel.name).") | |
} | |
} | |
struct Bus: Vehicle { | |
let name: String | |
func startEngin() { | |
print("\(name) enjin started!") | |
} | |
func fillGasTank(with fuel: Diesel) { | |
print("Fill \(name) with \(fuel.name).") | |
} | |
} | |
protocol Fuel { | |
associatedtype FuelType where FuelType == Self | |
static func purchase() -> FuelType | |
} | |
struct Gasoline: Fuel { | |
let name = "gasoline" | |
static func purchase() -> Gasoline { | |
print("Purchase gasoline from gas station.") | |
return Gasoline() | |
} | |
} | |
struct Diesel: Fuel { | |
let name = "diesel" | |
static func purchase() -> Diesel { | |
print("Purchase diesel from gas station.") | |
return Diesel() | |
} | |
} | |
// MARK: - Functions | |
func startAllEngin(for vehicles: [any Vehicle]) { | |
for vehicle in vehicles { | |
vehicle.startEngin() | |
} | |
} | |
func fillAllGasTank(for vehicles: [any Vehicle]) { | |
for vehicle in vehicles { | |
fillGasTank(for: vehicle) | |
} | |
} | |
func fillGasTank(for vehicle: some Vehicle) { | |
let fuel = type(of: vehicle).FuelType.purchase() | |
vehicle.fillGasTank(with: fuel) | |
} | |
// MARK: - Execution | |
let vehicles: [any Vehicle] = [ | |
Car(name: "Car_1"), | |
Car(name: "Car_2"), | |
Bus(name: "Bus_1"), | |
Car(name: "Car_3"), | |
] | |
startAllEngin(for: vehicles) | |
fillAllGasTank(for: vehicles) |
As of now (Swift 5.7), there is only 1 way to convert opaque type to existential type and vice versa, which is by using a function parameter. Thus using as
will not work. Furthermore, in order for the conversion to work, the function parameter must be of type any
or some
, using [any]
or [some]
will not work.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Came across your blog a short while ago and I like the style! Great article, easy to understand with examples. Quite new to generics (and maybe Swift) so thanks a lot for sharing! I wanted something like this last year but couldn't figure out how, and I read online about type erasure to make it work (AnyMyBaseClassObject). Do you have an article on type erasure?
As for this, is it possible to do something like
as some Vehicle
or some kind of explicit cast without creating a new function? And what about changingany
tosome
forfunc fillAllGasTank(for vehicles: [any Vehicle])
, will that work?