cnotethegr8
cnotethegr8

Reputation: 7510

Why do I get a Swift error with nested associated types

I would like to create a couple protocols to have a more type-safe friendly Notification interface.

The idea is to have a single type which from it, can be retrieved the notifications name, user info type (which will be the only entry of the dict), and a delegate for simplifying the observed response with the user info as a parameter.

Here are two protocols to help with building this interface.

protocol StructuredNotificationIdentifier {
    associatedtype Delegate: StructuredNotificationUserInfoType
    static var notification: Notification.Name { get }
}

protocol StructuredNotificationUserInfoType {
    associatedtype UserInfoType
}

Here is an implementation of a notification with its name, user info type, and protocol.

protocol SomethingDidHappenDelegate: StructuredNotificationUserInfoType where UserInfoType == String {
    func somethingDidHappen(_ value: UserInfoType)
}

enum SomethingDidHappenNotification: StructuredNotificationIdentifier {
    typealias Delegate = SomethingDidHappenDelegate
    static var notification: Notification.Name { Notification.Name("SomethingDidHappenNotification") }
}

And here is one more example of how the interface becomes simplified.

extension NotificationCenter {
    func post<T: StructuredNotificationIdentifier>(identifier: T, userInfo: T.Delegate.UserInfoType) {
        post(name: type(of: identifier).notification, object: nil, userInfo: ["info": userInfo])
    }
}

Now, my error happens on the enum declaration.

Type 'SomethingDidHappenNotification' does not conform to protocol 'StructuredNotificationIdentifier'

The problem as I understand it is coming from the StructuredNotificationIdentifier.Delegate requiring conformance of StructuredNotificationUserInfoType which also needs a defined associatedtype. The part that I'm confused about is why this is not resolved with the SomethingDidHappenDelegate declaration?

I understand I can set the user info type in multiple places, however since this is a pattern I'll be using often, I wanted the least amount of redundant code needed for the setup.

Why am I getting this error and is there an elegant way to resolve it?

Upvotes: 1

Views: 214

Answers (2)

Asperi
Asperi

Reputation: 257749

The following makes your code compilable

enum SomethingDidHappenNotification<D: SomethingDidHappenDelegate>: StructuredNotificationIdentifier {
    typealias Delegate = D
    static var notification: Notification.Name { Notification.Name("SomethingDidHappenNotification") }
}

Upvotes: 1

Sweeper
Sweeper

Reputation: 271735

Protocols don't conform to protocols, so although it seems like

typealias Delegate = SomethingDidHappenDelegate

satisfies the Delegate associated type requirement of StructuredNotificationIdentifier, it doesn't, because Delegate must conform to StructuredNotificationUserInfoType. And SomethingDidHappenDelegate, being a protocol, does not conform to any protocols.

I tried to come up with a workaround, and this is what I've got:

protocol StructuredNotificationIdentifier {
    associatedtype Delegate
    associatedtype UserInfoType
    static var notification: Notification.Name { get }
}

protocol SomethingDidHappenDelegate {
    func somethingDidHappen(_ value: String)
}

enum SomethingDidHappenNotification: StructuredNotificationIdentifier {
    typealias Delegate = SomethingDidHappenDelegate
    typealias UserInfoType = String
    static var notification: Notification.Name { Notification.Name("SomethingDidHappenNotification") }
}

extension NotificationCenter {
    func post<T: StructuredNotificationIdentifier>(identifier: T, userInfo: T.UserInfoType) {
        post(name: type(of: identifier).notification, object: nil, userInfo: ["info": userInfo])
    }
}

I moved UserInfoType into StructuredNotificationIdentifier as well and deleted the constraint on Delegate, because I don't think it is possible to access T.Delegate.UserInfoType when Delegate is a protocol. The error message I got when I tried to do that was pretty clear:

Associated type 'UserInfoType' can only be used with a concrete type or generic parameter base

Upvotes: 1

Related Questions