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?
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.
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.