Using a protocol with an associated type that conforms to View as a type for a property on a struct

Hi Swift crew!

I've just done some reading up on generics and protocols and I'm trying to implement an example. I'm clearly missing something.

For the code below, I've created a protocol called Themeable that I would like to use as a type to switch between themes. This will be done on a property called currentTheme on the struct SomeViewModel.

However, I'm running into an error:

Protocol 'Themeable' can only be used as a generic constraint because it has Self or associated type requirements.

Does that means it can only form part of a where clause and not be a type like I had hoped? If you could point me onto what I'm missing in the docs, that'd be ace!

protocol Themeable {
    
    associatedtype Content: View
    
    @ViewBuilder
    func contentFor(_ somethingIrrelevant:String) -> Content
    
}

struct Theme: Themeable {
    
    @ViewBuilder
    public func contentFor(_ somethingIrrelevant:String) -> some View {
        Text(somethingIrrelevant)
    }
    
}

struct SomeViewModel {
    
    private var currentTheme:Themeable // error
    
}

I don't understand what you mean by this sentence. Is this what you're hoping for, or not hoping for? :thinking:

// Replace `YourModule` with the actual name of your module.
struct SomeViewModel<Themeable: YourModule.Themeable> {

Hi Jessy,

In short, I was hoping to be able to use the Themeable protocol as a type for a property.

That sentence is more a wondering on why I cannot use a protocol as a type, as I thought that you can constrain a generic with a where clause. But as you can probably tell, I'm a bit confused myself.

See the educational note for this diagnostic:

1 Like

You may be able to make SomeViewModel generic over a type that conforms to Themeable rather than requiring a protocol type here. But if not, allowing protocols like Themeable to be used as types was the subject of SE-0309. That change is not yet available in an official Xcode release, but if you're willing to wait, you can try a weekly snapshot toolchain.

Hi John,

Thanks for posting your reply.

I was able to change the property's type to be a generic as suggested and constrain that generic to the Themeable protocol.

struct SomeViewModel<T:Themeable> {
    private var currentTheme:T // compiler is happy
}

Thanks to @xwu for the note above that explains why this is the case.

Why is T a better name than Themeable?

There are deployment constraints at play as well. IIRC the new and exciting language features will need OS support.

SE-0309 does not require runtime support, no.

1 Like

I don't think it is. I was just following what I thought was convention.

Theme:Themeable is maybe more descriptive.

The whole idea was to switch out themes that have the same functionality that I could call. But if I have to define the type when I create SomeViewModel then I'm constraining myself to one type of theme.

Looks like it's back to the design drawing board for me. Using generics inside protocols is tricky.

Thanks for the information, and sorry for the rushed answer! The next Swift release will be great, with many life-enhancing features, and it's still difficult to keep in mind which ones will be available right away, and which ones will need runtime support :sweat_smile:

2 Likes