61

If you have protocol like so:

protocol Messaging {
    func sendMessage(message: String)
}

Is there any way to satisfy it in a class like so:

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {}
}

This would be nice to have, as the resulting signature of the protocol is satisfied by adding the defaulted parameter. Is there any way to get this to work with Swift 2?

This is a simplified example. Let's say, for the sake of argument, that the protocol is fixed. A solution can only update the Messager class. My goal is to be able to call sendMessage() like so:

let m: Messaging = Messager()
m.sendMessage("")

The only way I found to accomplish this (and satisfy the compiler) is with overloading like so:

class Messager: Messaging {
    func sendMessage(message: String) {
        self.sendMessage(message, count: 1)
    }

    func sendMessage(message: String, count: Int = 1) {}
}

The problem with this approach is that my defaults are then specified in two places and I lose the main advantage of Swift's default parameters.

Dov
  • 15,530
  • 13
  • 76
  • 177

4 Answers4

79

in Swift 3 you could use extensions to solve that, however it's a bit ugly. Hope for a better solution in next swift versions.

import UIKit

protocol TestProtocol {
    func testFunction(a: Int, b: Int?) -> String
}

extension TestProtocol
{
    func testFunction(a: Int, b: Int? = nil) -> String {
        return testFunction(a: a, b: b)
    }
}

class TestClass: TestProtocol
{
    func testFunction(a: Int, b: Int?) -> String {
        return "a: \(a), b: \(b)"
    }
}

func testit(testProtocol: TestProtocol) {
    print(testProtocol.testFunction(a: 10)) // will print a: 10, b: nil
    print(testProtocol.testFunction(a:10, b:20)) // will print a: 10, b: Optional(20)
}

let t = TestClass()
testit(testProtocol: t)

However, this would somehow lead to one issue. If some class is not conforming to the protocol, it wont result in a compile error but rather in an endless loop.

A slightly better solution (in my opinion) is to capsulate the default parameter in a second function like this:

import Foundation

protocol TestProtocol {
    func testFunction(a: Int, b: Int?) -> String
}

extension TestProtocol
{
    // omit the second parameter here
    func testFunction(a: Int) -> String {
        return testFunction(a: a, b: nil) // <-- and use the default parameter here
    }
}

class TestClass: TestProtocol
{
   func testFunction(a: Int, b: Int?) -> String 
   {
       return "testFunction(a: \(a), b: \(b))"       
   }
}

func testit(testProtocol: TestProtocol) {
    print(testProtocol.testFunction(a: 10)) // will print a: 10, b: nil
    print(testProtocol.testFunction(a: 10, b: 20)) // will print a: 10, b: Optional(20)
}
print(Date())
let t = TestClass()
testit(testProtocol: t)

In this way the compiler will notify when a class does not conform to the protocol, and also wont end up in and endless loop.

hhamm
  • 1,511
  • 15
  • 22
33

If anyone is still looking for an answer to this, this link helped me out:

https://oleb.net/blog/2016/05/default-arguments-in-protocols/

Basically the original function definition includes all the params you need:

protocol Messaging {
    func sendMessage(message: String, count: Int)
}

and your extension provides the default values, calling your original protocol function with those default values:

extension Messaging {
    func sendMessage(message: String, count: Int = 1) {
        sendMessage(message, count)
    }
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
John C.
  • 415
  • 5
  • 3
  • 16
    In my head this creates an endless loop :) – Yvo Feb 02 '17 at 21:31
  • 3
    If you make count optional in the protocol function like I did, the endless loop is not only in your head ... ;-) – TheEye Aug 22 '17 at 17:09
  • @TheEye I don't see your answer code here. Do you mean you're the author of the post?! Still, from what I understand it seems that this doesn't call the function in the extension...rather it calls the function in the original protocol. I just don't understand why/how it does that! – mfaani Sep 21 '17 at 19:18
  • 1
    @Honey "*this doesn't call the function in the extension*" – correct, well at least not *directly* (w/ static dispatch), that is. When you call a method on a protocol-typed instance (in this example, the method is being implicitly called on `self`, which is typed as `Messaging`), if that method is a protocol requirement (such as in this case), it will be dispatched dynamically via the protocol witness table for the underlying conforming type of the instance. In the case where the type provides its own implementation of the requirement; that implementation will be called. So no infinite loop. – Hamish Sep 21 '17 at 19:43
  • 4
    Although that being said, if the conforming type had *not* provided its own implementation, there *would* be an infinite loop, as the extension would then be dispatched to. This is a fairly serious issue with this solution, and was avoided in the linked blog post by using a different name for the extension method (so that it doesn't satisfy the requirement as a default implementation). – Hamish Sep 21 '17 at 19:43
  • If you don't understand what I'm babbling on about with protocol witness tables, [this WWDC talk](https://www.youtube.com/watch?v=9By9KLs51Dc) & [my answer here](https://stackoverflow.com/a/44706021/2976878) may be useful. – Hamish Sep 21 '17 at 19:44
  • @Hamish I remember seeing that video referenced in your answers before. Barely understood it. Though my knowledge has improved. Would there be something more basic that I could read/watch before that? I feel like my knowledge on Protocols/Dynamic-static dispatch/Generics/ is limited and I'm not reading/watching from suitable materials... – mfaani Sep 21 '17 at 19:46
  • @Hamish "if the conforming type had not provided its own implementation, there would be an infinite loop" you mean if we have `class XYZ : Messaging {func sendMessage(message: String, count: Int){}` or I guess you mean `class XYZ : Messaging{ //no comformance, just pure adoption which would 'attempt' to use the extension...which in this case would fail? }` – mfaani Sep 21 '17 at 19:51
  • @Honey I mean the latter; yup (try it!). – Hamish Sep 21 '17 at 19:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155038/discussion-between-honey-and-hamish). – mfaani Sep 21 '17 at 19:53
  • This little nice implementation won't help my case - because I have lots of classes implementing a (delegate) protocol, and the caller, not knowing which delegate is used - will need to ever apply both parameters. What I'd like to achieve is FORCING all implementors of the protocol to have a parameter whose value defaults to "false" and can be omitted altogether. – Motti Shneor Oct 22 '17 at 10:41
  • `Function call causes an infinite recursion` Do not do this – Arturo Aug 09 '22 at 16:14
  • **Note**: `sendMessage(message:count:)` and `sendMessage(_:_:)` are two different method signatures. This is more clearly called out in the link referenced. – bshirley Jan 28 '23 at 19:32
12

With Swift 2 you can now extend your protocol like so and give it a default implementation

protocol Messageable {
    func sendMessage(message: String)
}

extension Messageable {
    func sendMessage(message: String, count: Int = 1) {
        // Do your default implementation
    }
}

I am still learning about this so I am not 100% sure how this will work with your send message example, but I believe this is what you are looking for.

There are so many new cool things you can do with protocols in Swift 2.

Watch Apple's presentation which is very good:

https://developer.apple.com/videos/play/wwdc2015-408/

and read these:

http://matthijshollemans.com/2015/07/22/mixins-and-traits-in-swift-2/

http://code.tutsplus.com/tutorials/protocol-oriented-programming-in-swift-2--cms-24979

http://www.raywenderlich.com/109156/introducing-protocol-oriented-programming-in-swift-2

gmogames
  • 2,993
  • 1
  • 28
  • 40
crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • 1
    I don't think it addresses what I'm trying to achieve. A protocol extension would only help me if the protocol could define default arguments, which it can't. – Dov Jan 06 '16 at 16:57
  • yeah thats what I just thought in your specific example. I just updated my answer to reflect this. Not possible to adjust your protocol to make it more generic? – crashoverride777 Jan 06 '16 at 17:00
  • No, I'd like the protocol to contain the minimum number of arguments. The updated protocol extension you gave just shifts the implementation with defaults from a specific instance, to cover all instances. I'd still need to define the method twice. – Dov Jan 06 '16 at 17:03
  • you can override the extension by implementing the 2 methods in your scenes. Thats why I thought might be relevant. You have a general implementation in the extension and if you need a specific one you just define the method in the scene which will override the extension. – crashoverride777 Jan 06 '16 at 22:08
  • In my case protocol method are in objective-c (i.e. declared with @objc) so above mentioned solution dosen't work. – Jayprakash Dubey Sep 15 '20 at 08:33
6

You could add typealiases to your protocol to represent possible different argument types in your protocol-blueprinted function .sendMessage. In the example below I have explicitly specified that the 2nd argument has neither an internal nor an external name.

Since you have two typealiases, you can either implement this blueprint as one using two different types (Messenger in example below), or, simply throw away the second argument (AnotherMessenger in example) as an empty tuple type () with default value () (you can think of this as a void type with a value of void).

protocol Messaging {
    typealias T
    typealias U
    func sendMessage(message: T, _ _ : U)
}

/* Class where you make use of 2nd argument */
class Messager: Messaging {

    func sendMessage(message: String, _ count: Int) {
        print(message + "\(count)")
    }
}

/* Class where you ignore 2nd argument */
class AnotherMessager : Messaging {

    func sendMessage(message: String, _ _ : () = ()) {
        print(message)
    }
}

/* Tests */
var a = Messager()
a.sendMessage("Hello world #", 1)
// prints "Hello World #1"

var b = AnotherMessager()
b.sendMessage("Hello world")
// prints "Hello World"

This is as close as I can get you to simulate the behaviour you describe in the question and in the comments below. I'll leave the alternative solutions below in case they can help someone else with similar thought but less hard design constraints.


Another option: not exactly the behaviour your asking for, but you could use an anonymous closure as the single parameter to your protocol-blueprinted function, where this close take no arguments but returns an array of Any objects, which you can access and treat in your sendMessage function as you wish.

protocol Messaging {
    func sendMessage(@autoclosure messages: ()->[Any])
}

class Messager: Messaging {
    func sendMessage(@autoclosure messages: ()->[Any]) {
        for message in messages() {
            print(message, terminator: "")
        }
    }
}

var a = Messager()
a.sendMessage([String("Hello "), String("World "), String("Number "), Int(1)])
// prints "Hello World Number 1"

Another alternative would be to have to separate blueprints of function sendMessage(..) in your protocol Messaging, one with and one without the additional parameter count. You thereafter add default (dummy) implementations for both of these functions via extension of protocol Messaging. Your class Messager will then comply to Messaging protocol even without any implementation of sendMessage(..) in it at all; in the lack thereof, the default implementations are used. Finally make a detailed implementation only the sendMessage function you wish to use in your class.

protocol Messaging {
    func sendMessage(message: String)
    func sendMessage(message: String, count: Int)
}

/* Extend blueprints with default dummy implementations */
extension Messaging {
    func sendMessage(message: String) { }
    func sendMessage(message: String, count: Int = 1) { }
}

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {
        print(message + "\(count)")
    }
}

var a = Messager()
a.sendMessage("Hello world #")
// prints "Hello World #1"

Note however that instances of your class will list both sendMessage functions as available class methods; one of them being your function, and the other the dummy default implementation.


Old answer prior to edit regarding different type parameters (I'll leave it here as it can be a possible alternative in the case where all parameters are of same type)

Finally, you could make use of variadic parameters:

protocol Messaging {
    func sendMessage(messages: String...)
}

class Messager: Messaging {
    func sendMessage(messages: String...) {
        for message in messages {
            print(message)
        }
    }
}

var a = Messager()

a.sendMessage("Hello", "World", "!")

A variadic parameter accepts zero or more values of a specified type. You use a variadic parameter to specify that the parameter can be passed a varying number of input values when the function is called. Write variadic parameters by inserting three period characters (...) after the parameter’s type name.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • I updated my question to reflect that the example I gave is simplified. It's not just concatenating strings. There are a variety of parameter types. – Dov Jan 05 '16 at 00:03
  • @Dov Ok, then the above is, naturally, not applicable. I'll see if I can come up with alternative, or if someone else can help you first. – dfrib Jan 05 '16 at 00:04
  • Interesting approach, but then the protocol isn't really serving much of a purpose. Can you think of a method that wouldn't require the protocol to change? – Dov Jan 05 '16 at 00:15
  • @Dov Just a comment: I'd say protocol many times serves purposes just as this: you could write a generic function with protocol `Messaging` as a type constraint. You would then know that all generic types conforming to that constraint will have access to a function `sendMessage` (with the specific function signature). See e.g. http://stackoverflow.com/questions/34475597/extend-generic-arrayt-to-adopt-protocol/34475721#34475721 for an interesting example. Anyway, back to the question: I'll see if I can give this a third try, or if I'll strike out on this last one. – dfrib Jan 05 '16 at 00:19
  • @Dov: I can't come up with any way (except for the workarounds in my answer) to come around the fact that by a class conforming to a protocol, it's expected to contain implementation for all protocol blueprints, with the exception of those blueprints that have default implementations (via `extension MyProtocol ...`). In your example above, the addition of the default valued `count` parameter ascertains that class `Messenger` does no longer conform to your protocol. Are you sure this is a behaviour you're really looking for, or could you perhaps use an alternative approach? – dfrib Jan 05 '16 at 00:31
  • @Dov: Ok, I added one last approach to it (typealises), perhaps this most closely mimics the behaviour you're looking for. – dfrib Jan 05 '16 at 01:05
  • Thanks for the effort, and with each back-and-forth I'm editing my question to try to make my goal clearer. I'm not trying to accept _arbitrary_ lists of arguments, even strongly typed. I want to be able to define a single function on the class that satisfies the protocol and also adds some additional defaulted options. – Dov Jan 05 '16 at 15:18
  • @Dov I see. With your last edit: if you see it as acceptable to let your blueprinted function `.sendMessage(...)` to simple call another internal overload of itself, then you might as well let the default parameter value be the value of a private property that has a default value itself. This way, at least, you'll avoid entering the default value at two different places. (If this is possible, writing from a mobile device, so untested). – dfrib Jan 05 '16 at 15:53
  • Of course, but I'd prefer not to have the overload in the first place. – Dov Jan 05 '16 at 15:53
  • @Dov I'm sorry, then I think I'm out of suggestions. I think this is simply the way protocols work: you need to precisely match your function implementation signature with the corresponding blue-print function signature in the protocol. Again, are you sure this is the behaviour you're looking for? Maybe there's an alternative approach which does not put in in this situation, or perhaps someone else can help you out. Good luck! – dfrib Jan 05 '16 at 18:52