Protocol-Oriented Programming Tutorial in Swift 5.1: Getting Started | raywenderlich.com

In this protocol-oriented programming tutorial, you’ll learn about extensions, default implementations and other techniques to add abstraction to your code.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/6742901-protocol-oriented-programming-tutorial-in-swift-5-1-getting-started

Thank you for the great tutorial! I was both very clear and enjoyable.

1 Like

Good info here. It would’ve been cool to see something about associated types on protocols too.

I had a problem recently where I had two structs…

struct A {
  var response: Int
}

struct B {
  var response: SpecialEnum
}

protocol SameThing {
  var response: Int  // or :SpecialEnum?
}

I wanted to use them in the same places throughout my app. But I couldn’t conform them to the protocol because they had different types for the response property. I tried creating an associated type…

protocol SameThing {
  associatedType Response
  var response: Response
}

But the compiler complained that my “protocol can only be used as a generic constraint because it has Self or associated type requirements.” So, I decided to use an empty protocol instead.

protocol SameThing { }
extension A: SameThing { }
extension B: SameThing { }

I just downcast them to the explicit types as needed.
Let me know if anyone has a better solution. Thanks!

I wonder why, in FlappyBird’s definition of var airspeedVelocity, there’s no return.

struct FlappyBird: Bird, Flyable {
let name: String
let flappyAmplitude: Double
let flappyFrequency: Double
var airspeedVelocity: Double {
3 * flappyFrequency * flappyAmplitude // no return here??
}
}

It’s an implicit return, new to swift 5.1. Single expressions don’t need return

1 Like

“airspeedVelocity”

I think airSpeed would be sufficient as an identifier. Here you are just doubling up on names.
Also velocity implies direction and the type of airspeedVelocity is double - much more appropriate for a scalar value like speed.

1 Like

Really good article here, thanks.

As an engineer one think irked me though - adding power to speedFactor in boost() is like adding String to Double

You’ve probably forgotten about the medieval science of calculating the airspeed velocity of swallows. It was pretty helpful in determining the difference between African and European swallows carrying coconuts. :]

I have never seen the “is” word used in Swift before.
It seems very clean in it’s use, but I was not able to find any reference to it’s use.
Would you happen to have more information on the use of “is”?

var canFly: Bool { self is Flyable }

Thanks for the great lesson.

1 Like

@ron_bowser Thanks very much for your question!

The “is” keyword in Swift is actually similar to the “isKindOfClass()” function. The difference is that while “isKindOfClass()” only works with subclasses of NSObject or classes that implement NSObjectProtocol, “is” works with any, and all classes, and non-class types. Which means “is” can also be used to check against protocols. :slight_smile:

I hope this helps!

All the best.

Great tutorial. Thanks for providing numerous examples of extending, mutating, and conforming to standard library protocols. This should be recommended reading after one completes Apple’s intermediate Swift course.

1 Like

I think a better way for Score and RacingScore to conform to the Comparable protocol, would be by extending Score rather than RacingScore, since there is no special/additional logic for comparing RacingScore instances, and future types that conform to Score won’t need to be updated again to conform to Comparable. I tried the following and it worked well:

extension Score {
    static func <(lhs: Self, rhs: Self) -> Bool {
      lhs.value < rhs.value
    }
}

Try making the response property a protocol instead of a type and go from there:

protocol SameThing {
    var response: ResponseThing { get set }
}

protocol ResponseThing {
    // functionality you want the response to have... for example...
    var message: String { get }
}

Make sure the types you are using for the response property conform to ResponseThing:

extension Int: ResponseThing {
    var message: String {
        return "\(self)"
    }
}

extension SpecialEnum: ResponseThing {
    var message: String {
        switch self {
        // return different string message depending on case
        case .specialCase:
            return "SpecialCaseEnum"
        }
    }
}

And finally…

struct A: SameThing {
  var response: Int
}

struct B: SameThing {
  var response: SpecialEnum
}

I hope that helps.

Thanks @lchwe. I didn’t think of using two protocols but it looks like that would work. It would probably make sense to call it WrappedResponse instead of ResponseThing, but I get your point. Then, I would access the property of conforming types (e.g. A or B) with thing.response and for protocol instances with thing.response.message or more generally, thing.response.wrappedValue.

I also learned from the RW fundamental design patterns course that this is called the Strategy pattern.

How’d you think of this? Did you need something like it in your app too?

No problem! I thought you had a legitimate question and I just wanted to take a shot at finding a solution. I’m sure I needed to do this in some form or another in an app, but I also was curious of how to solve your problem in a clean way using Protocols. I’m glad my reply didn’t go unnoticed and hopefully you found it useful! And yeah, for ResponseThing, I was just following the SameThing naming pattern without giving it too much thought lolll.

Definitely man. Thanks again!

@lchwe Thank you for sharing your solution - much appreciated!

@xtiandiaz @akirna @simonmoss49 @drunneriv Really glad you like it! Cheers! :]

This tutorial is more than six months old so questions are no longer supported at the moment for it. Thank you!