Generic Parameter ‘’ Could Not Be Inferred in Cast to ‘’

I’m encountering an issue where I am unable to properly infer the generic parameter EventType when trying to cast a Notification‘s userInfo dictionary to a EventNotification. Here’s the relevant code:

func handle(_ notification: Notification) {
    Log.info("[Event]\(notification.name.rawValue)")
    
    if let userInfo = notification.userInfo {
        if let eventNotification = userInfo["event"] as? EventNotification<any Event> {
            // Trying to use eventNotification here
        }
    }
}

In this code, I’m trying to extract an EventNotification from the userInfo dictionary of a Notification. However, I get the following error:

Type 'any Event' cannot conform to 'Event'

The EventNotification struct is a generic type, and I’m unsure how to explicitly specify or infer the generic EventType.
Here’s the definition of the EventNotification struct:

public struct EventNotification<EventType: Event> {
    let event: EventType
    let eventWho: any EventWho
    let eventWhere: EventWhere
    let eventTime: EventTime
    let eventResult: EventResult
}
public protocol Event: Sendable{}

Could anyone provide guidance on how to handle this situation? How should I explicitly specify the EventType, or is there another approach I should take?

Thank for danny
Obviously my code is minimized like this



protocol Event {
    
}

enum EventA: Event {
    case a1
}

enum EventB: Event {
    case b1
}

enum EventC: Event {
    case c1
}

struct EventWrapper<EventType: Event> {
    let event: EventType
}

let userInfo: [String: Any] = [
    "event": EventWrapper.init(event: EventA.a1)
]

if let wrapper = userInfo["event"] as? EventWrapper<any Event> {
    handleEvent(wrapper)
}
else if let wrapper = userInfo["event"] as? EventWrapper<?> {
    handleEvent(wrapper)
}
else if let wrapper = userInfo["event"] as? EventWrapper<*> {
    handleEvent(wrapper)
}
else if let wrapper = userInfo["event"] as? EventWrapper {
    handleEvent(wrapper)
}

func handleEvent(_ wrapper: EventWrapper<any Event>) {
    if let eventA = wrapper.event as? EventA {
        // dosomething
        return
    }
    // other
}

@Danny can you help me

The simplest way to do type erasure these days is to use a protocol like so:

protocol EventWrapperProtocol {
  associatedtype EventType
  var event: EventType { get }
}

extension EventWrapper: EventWrapperProtocol { }
if case let wrapper as any EventWrapperProtocol = userInfo["event"] {
  handleEvent(wrapper)
}
func handleEvent(_ wrapper: some EventWrapperProtocol) {

That said, I would avoid Notifications altogether.

1 Like

Thanks danny
Your code works.
In my subjective opinion this is a bit twisted and ugly.
Actually I don't think this has much to do with type erasure. This can also support similar writing in other languages ​​that implement type erasure.
This abstraction is intuitive. Can you tell me why swift doesn't support it?

The underlying issue here is that EventNotification<any Event> is 1) a completely unrelated type to any concrete type like EventNotification<Int> and 2) not a valid type because any Event does not conform to the Event protocol.

One way to deal with this is by creating a protocol that EventNotification conforms to:

private protocol AnyEventNotification {
    var eventType: any Event.Type { get }
}
extension EventNotification: AnyEventNotification {
    var eventType: any Event.Type { EventType.self }
}

func handle(_ notification: Notification) {
    Log.info("[Event]\(notification.name.rawValue)")
    
    if let userInfo = notification.userInfo {
        if let eventNotification = userInfo["event"] as? AnyEventNotification {
            func extract<EventType: Event>(as type: EventType.Type) {
                handle(eventNotification as! EventNotification<EventType>)
            }
            extract(as: eventNotification.eventType)
        }
    }
}

func handle(_ eventNotification: EventNotification<some Event>) {
    // ...
}

Unfortunately, the compiler doesn’t support performing this conversion without the nested extract function at present.

1 Like

This is good, but you never need metatype properties anymore.

private protocol AnyEventNotification<EventType> {
  associatedtype EventType: Event
  var event: EventType { get }
}

extension EventNotification: AnyEventNotification { }
func handle(_ notification: Notification) {
  if let eventNotification = notification.userInfo?["event"] as? any AnyEventNotification {
    func _handle<Event>(_: some AnyEventNotification<Event>) {
      handle(eventNotification as! EventNotification<Event>)
    }
    _handle(eventNotification)
  }
}
1 Like

Small style nit… I believe the convention is that AnyFoo is generally meant for a concrete type that erases a Foo… not for an abstract protocol interface that Foo would conform to.

I believe EventNotificationProtocol or EventNotificationType would follow the convention I have seen here so far.

3 Likes