How to Achieve Dynamic Dispatch Using Generic Protocols in Swift 5.7

It’s by no means been simpler!

Dynamic dispatch is likely one of the most essential mechanisms in Object-Oriented Programming (OOP). It’s the core mechanism that makes run-time polymorphism attainable, enabling builders to put in writing code that decides their execution path throughout run-time moderately than compile-time.

As straightforward because it appears to attain dynamic dispatch in OOP, it’s not the case with regards to Protocol-Oriented Programming (POP). Attempting to perform dynamic dispatch utilizing protocols all the time comes with unpredicted difficulties on account of varied limitations within the Swift compiler.

With the discharge of Swift 5.7, all these have grow to be historical past! Attaining dynamic dispatch within the realm of POP has by no means been simpler. On this article, let’s discover what sort of enhancements we get from Swift 5.7 and what it takes to perform dynamic dispatch utilizing protocol with related varieties.

So with out additional ado, let’s get proper into it!

Notice:

If you happen to’re unfamiliar with the some and any key phrase in Swift, I extremely encourage you to first learn my weblog put up referred to as “Understanding the “some” and “any” keywords in Swift 5.7“.

Earlier than I can begin displaying you the enhancements in Swift 5.7, let’s outline the protocols and structs that we’d like for our pattern code all through this text.

The definitions now we have above are much like what we’re utilizing in my previous article, however with a bit little bit of a twist. Right here in our Automobile protocol, now we have 2 operate necessities, startEngin() and fillGasTank(with:). For the sake of demonstration, We are going to attempt to obtain dynamic dispatch utilizing these 2 features in each Automobile and Bus structs.

Now, let’s say we need to create a startAllEngin() operate that accepts a heterogeneous array as proven under:

You’ll discover that that is actually unattainable in Swift 5.6 as you’ll be prompted with an error saying: “Protocol ‘Automobile’ can solely be used as a generic constraint as a result of it has Self or related kind necessities”. The Swift compiler is prohibiting us to create a heterogeneous array with Automobile as its factor kind on account of the truth that Automobile has an related kind (FuelType).

Professional Tip:

If you want to study extra concerning the error, and how one can work round it previous to Swift 5.7, take a look at my article printed on Medium: “Swift: Accomplishing Dynamic Dispatch on PATs (Protocol with Associated Types)

Because of the improve Apple made to the Swift compiler, this limitation now not exists in Swift 5.7. We are able to lastly use a protocol similar to how we use a superclass in OOP. Let me present you the way.

In Swift 5.7, making a heterogeneous array is now not prohibited by the compiler. All we have to do is to make use of the any key phrase.

Through the use of the any key phrase, we’re telling the compiler that the array will include existential varieties and that their underlying concrete kind will all the time conform to the Automobile protocol.

With that, calling startAllEngin(for:) will give us the dynamic dispatch that we would like.

Now let’s check out one other extra sophisticated instance. Let’s say we need to create a operate named fillAllGasTank(for:). This operate will carry out dynamic dispatch to the car’s fillGasTank(with:) operate based mostly on the given automobilesarray.

Outline a Generic Parameter Sort

What we making an attempt to attain may appears easy at first, however after we begin coding, we’ll stumble upon our first drawback:

Since various kinds of automobiles would require completely different sorts of gasoline, we should create a generic protocol to characterize each Gasoline and Diesel. Let’s go forward and do this.

The Gas protocol is only a easy protocol consisting of an related kind named FuelType, and a static buy() operate. Discover how we constrain FuelType to all the time equal to the kind that conforms to the Gas protocol. This constraint is essential to ensure that the compiler to find out the concrete kind returned by the static buy() operate.

Subsequent up, let’s conform each Gasoline and Diesel to the Gas protocol.

On prime of that, we additionally want to make sure that the Automobile protocol’s FuelType is a kind that conforms to the Gas protocol.

“any” to “some” Conversion

With the Gas protocol and all different associated modifications in place, we will now revisit the fillAllGasTank(for:) operate and replace it accordingly.

Within the above code, discover how we leverage the car’s gasoline kind to get an occasion of the Gas concrete kind, in order that we will cross it into the fillGasTank(with:)operate.

Sadly, if we attempt to compile our code, we’ll stumble upon our 2nd drawback: Member ‘fillGasTank’ can’t be used on worth of kind ‘any Automobile’; think about using a generic constraint as a substitute”. What does that imply?

With the intention to perceive the error that we’re getting, let’s have a fast recap on what are the variations between the some and any key phrase.

Compare the differences between the some and any keyword in Swift

As illustrated within the above picture, the underlying concrete kind of an existential kind is being wrapped inside a field. Due to this fact, the compiler is prohibiting us from accessing the fillGasTank(with:) operate. To go about this, we should first convert (unbox) the existential kind to an opaque kind earlier than accessing the fillGasTank(with:) operate.

Happily, Apple has made the conversion (unboxing) course of extraordinarily straightforward in Swift 5.7. All we have to do is to cross the existential kind to a operate that accepts an opaque kind and the conversion will occur routinely.

With that, we will now compile and execute our code with none error.

Be happy to seize the complete pattern code here if you wish to strive it out your self.

More Posts