How do I conform to two protocols that each want the same function name?

protocol ScannerDelegate {
    func outputReceived(_ s: String)
}

protocol ScoreboxDelegate {
    func outputReceived(_ s: String)
}

class SessionHandler : ScannerDelegate, ScoreboxDelegate {
   let scanner = Scanner()
   let scorebox = Scorebox()

   init() {
      // scanner.delegate is of type ScannerDelegate?
      scanner.delegate = self 

     // scorebox.delegate is of type ScoreboxDelegate?
      scorebox.delegeate = self
   }

    // I need to implement two different outputReceived() functions,
    // or know whether I'm being called because I'm a scanner delegate,
    // or a scorebox delegate.

    // Now what?
}


I would like my SessionHandler class to be able to conform to both classes, set itself as a delegate of both classes, and implement two very different methods for the required outputReceived() function.

How do I do this?

Do I have to contact the author of one of the delegates and convince them to change the name of their required method?

@_implements(Protocol, requirement)

1 Like

The leading underscore would suggest this is a slightly hidden or not formally adopted construct?

Correct, it is not an official part of the language.

Thank you, @Nevin

But, what I am doing wrong here?

@main
enum Driver {
    static func main () async throws {
        do {
            let u = Fubar ()
            
            let v: any Foo = u
            v.jibber (s: "Hello")
            
            let w: any Bar = u
            w.jibber (s: "Hello")
        }
    }
}

protocol Foo {
    func jibber (s: String)
}

protocol Bar {
    func jibber (s: String)
}

// [https://forums.swift.org/t/how-do-i-conform-to-two-protocols-that-each-want-the-same-function-name/82588/2]

struct Fubar: Foo, Bar {
    @_implements(Foo, jibber)
    func jibber (s: String) {
        print (#function, "Foo.jibber")
    }
    
    @_implements(Bar, jibber)
    func jibber2 (s: String) {
        print (#function, "Bar.jibber")
    }
}

Actual output:

jibber(s:) Foo.jibber
jibber(s:) Foo.jibber

Expected output:

jibber(s:) Foo.jibber
jibber(s:) Bar.jibber

You can't. (Please don't use @_implements, it's broken in so many ways.)

I would suggest declaring two distinct concrete types, perhaps wrapping the same value (think delegate pattern).

3 Likes

FWIW, I believe the issue you're running into is the very reason that most delegate methods provided by Apple include the sender's type as part of the method name (so that a single type can witness multiple delegates without conflicting). The other reason being that it provides a place for the sender to be passed in (so that, similarly, an object can witness the same delegate for multiple instances of the sender's type).

The collision you're hitting is why it's a good idea for folks designing their own APIs to follow these well-established design patterns.

3 Likes

If you really just need to disambiguate one method, you can make like you're using sink.

class SessionHandler {
  let scanner = Scanner()
  let scorebox = Scorebox()

  private var delegates: [AnyObject] = []

  init() {
    scanner.delegate = ClosureObject { [unowned self] output in
      print("\(scanner) says: \(output)")
    }&.apply { delegates.append($0) }

    scorebox.delegate = ClosureObject { [unowned self] output in
      print("\(scorebox) says: \(output)")
    }&.apply { delegates.append($0) }
  }
}

This would be more versatile if constraints on parameter packs were supported:

final class ClosureObject<Input, Error: Swift.Error, Output> {
  typealias Closure = (Input) throws(Error) -> Void
  init(_ closure: @escaping Closure) { self.closure = closure }
  let closure: Closure
}

extension ClosureObject<String, Never, Void>: ScannerDelegate & ScoreboxDelegate {
  func outputReceived(_ s: String) { closure(s) }
}
&.apply
public struct And<Value> {
  @usableFromInline let value: Value
  @inlinable init(_ value: Value) { self.value = value }
}

postfix operator &
@inlinable public postfix func &<Value>(value: Value) -> And<Value> {
  .init(value)
}

public extension And {
  @inlinable func apply<Error>(
    _ mutate: (inout Value) throws(Error) -> Void
  ) throws(Error) -> Value {
    var value = value
    try mutate(&value)
    return value
  }
}

You can also use the same generic type for everything, whether or not you need to support more than one protocol requirement:

init() {
  scanner.delegate = DelegateDisambiguator(self)&.apply { delegates.append($0) }
  scorebox.delegate = DelegateDisambiguator(self)&.apply { delegates.append($0) }
}
extension DelegateDisambiguator<SessionHandler, any ScannerDelegate>: ScannerDelegate {
  func outputReceived(_ s: String) {
    print("\(delegate.scanner) says: \(s)")
  }
}

extension DelegateDisambiguator<SessionHandler, any ScoreboxDelegate>: ScoreboxDelegate {
  func outputReceived(_ s: String) {
    print("\(delegate.scorebox) says: \(s)")
  }
}


final class DelegateDisambiguator<Delegate: AnyObject, Protocol> {
  init(_ delegate: Delegate) { self.delegate = delegate }
  unowned let delegate: Delegate
}
1 Like