Skip to content

Instantly share code, notes, and snippets.

@LeeKahSeng
Last active August 2, 2022 15:00
Show Gist options
  • Save LeeKahSeng/a347da1778c71f55537eda991bd56c64 to your computer and use it in GitHub Desktop.
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)
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)
@CyberMew
Copy link

CyberMew commented Aug 2, 2022

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 changing any to some for func fillAllGasTank(for vehicles: [any Vehicle]), will that work?

@LeeKahSeng
Copy link
Author

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