0

In short I am struggling with an error "ObjectWithGeneric requires that MyProtocol be a class type". I created a small example that (hopefully) makes sense, to simulate the issue.

I created an array-like container that can hold weak references to generic types:

class Container<Type> where Type: AnyObject {

    private class WeakWrapper {
        weak var listener: Type?
        init(listener: Type) { self.listener = listener }
    }

    private var wrappers: [WeakWrapper] = [WeakWrapper]()

    func append(_ listener: Type) {
        wrappers = wrappers.filter { $0.listener != nil && $0.listener !== listener } + [WeakWrapper(listener: listener)]
    }

    func remove(_ listener: Type) {
        wrappers = wrappers.filter { $0.listener != nil && $0.listener !== listener }
    }

    func forEach(_ execution: ((_ listener: Type) -> Void)) {
        wrappers.forEach { wrapper in
            guard let listener = wrapper.listener else { return }
            execution(listener)
        }
    }

}

All good so far. A small functionality to add and remove objects and a bit of a cleanup for objects that are deallocated.

Then I created a manager with a singleton that should hold such an array as "delegates". So instead of one weak delegate we would have an array of them. So a simple protocol:

protocol ExampleProtocol: class {
    func showMessage(_ message: String)
}

And a simple manager

class ExampleManager {

    static let shared: ExampleManager = ExampleManager()

    var delegates: Container<ExampleProtocol> = Container<ExampleProtocol>()

    private func sendMessageToAll() {
        delegates.forEach { $0.showMessage("Ping") }
    }

}

Some examples of usage:

class ExampleClass: ExampleProtocol {

    init() {
        ExampleManager.shared.delegates.append(self)
    }

    func showMessage(_ message: String) { print(message) }

}

class ExampleViewController: UIViewController, ExampleProtocol {

    override func viewDidLoad() {
        super.viewDidLoad()
        ExampleManager.shared.delegates.append(self)
    }

    func showMessage(_ message: String) { print("A controller says: \(message)") }

}

The compile problem is on the line var delegates: Container<ExampleProtocol> = Container<ExampleProtocol>(). It says that "'Container' requires that 'ExampleProtocol' be a class type.".

This kind of does and doesn't make sense to me. The protocol is defined as class but I guess the protocol itself is not a concrete class. So perhaps there should be way to define it as AnyObject that corresponds to ExampleProtocol. Like Container<AnyObject : ExampleProtocol>. But so far I have no luck achieving this.

Any clue appreciated. Thank You!

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
  • The error is because `where Type: AnyObject`, not because `: class`. – Sweeper Jun 15 '20 at 07:31
  • A _very_ similar situation here: https://stackoverflow.com/questions/51541353/generic-class-with-class-bound-constraint-cannot-be-parametrized-by-class-bound Possible duplicate. – Sweeper Jun 15 '20 at 07:35
  • @Sweeper the type needs to be declared as a class of some form in order to use it as a weak property. There are a few ways to do this but it seems that all eventually lead to same error. – Matic Oblak Jun 15 '20 at 07:51
  • Yes, I saw that. The OP of the linked post seems to be facing the same issue. Do you agree it's a duplicate? – Sweeper Jun 15 '20 at 07:51
  • @Sweeper no, not a duplicate. The linked question seems to be implying he is trying to use a none-class object where he explicitly defined a requirement to use a class object. It seems that his case is not solvable. I feel that this case here should be solvable. – Matic Oblak Jun 15 '20 at 07:53
  • You are (implicitly) required to use a class object as well, because of `weak`. And yes, it is not solvable, see matt's comment on their own answer. This is another case of the "protocol doesn't conform to itself" problem. – Sweeper Jun 15 '20 at 07:59

0 Answers0