Dynamic cast to protocol conformance across module (not retroactive conformance)

I've been surprised by the change of behavior of moving a class into its module and observing a behavior change in this container behavior.

The container is this one

public struct WeakObserverOrderedSet<T> {

    public struct Weak {
// using dynamic cast because i couldn't find a way to code a generic Weak container 
// over a protocol
        weak var value: AnyObject?
        init(_ v: AnyObject) {
            value = v
        }
    }

    var elements: [Weak] = []

    public mutating func add(value: T) {
        let anyValue = value as AnyObject
        guard indexOf(value: anyValue) == nil else { return }
        elements.append(Weak(anyValue))
    }
// …
    public func invoke(_ f: ((T) throws -> Void)) rethrows {
      for e in elements {
          guard let val = e.value else { continue }
          if let eVal = e.value as? T {
        try f(eVal)
          } else {
              print("could not cast \(e.value) as \(T.self)")
          }
      }
  }

It's usually used with protocol type such as :

protocol MyClassObserver: AnyObject { func onThis() }
class MyClass {
 var observers:  WeakObserverOrderedSet<MyClassObserver>
}

and its goal is to implement some kind of multicast delegate over any protocol.

The code was working fine, until i started using it across modules. In some configuration, a component that was defined in module A could register itself as an observer to a component defined in module B, using the "componentB.observers.add(self)", but the "invoke" method failed at the dynamic casting.

I've read about the issue with retroactive protocol conformance, however this isn't the case here. The component in module A is defined publicly as conforming to the B protocol. It's just that module B doesn't know about module A or its types.

The two questions are : why isn't this working, and how to make this work ?

So, just for information, i simply created a peer macro (to be used over the protocol) that redefined the matching weak & orderedset types, avoiding dynamic casting altogether.

Still, i'm curious to know if there's another solution that doesn't rely on macro codegen.

For information, it seems to occur only in unit tests.