Home > database >  Implied protocol conformance of properties in Swift
Implied protocol conformance of properties in Swift

Time:11-03

I have the following

protocol FooPresentable {
   var bar: BarPresentable { get }
}

protocol BarPresentable {
}

class Foo {
  let bar: Bar 
}

extension Bar: BarPresentable {
}

extension Foo: FooPresentable {
}  

And I get the error Foo does not conform to FooPresentable. Is it possible to have Foo conform to FooPresentable by just letting the compiler know that all Bars conform to BarPresentable

CodePudding user response:

protocol FooPresentable {
   var bar: BarPresentable { get }
}

For Foo to conform to FooPresentable, the type of it’s bar property must be BarPresentable, which is not the same as having a bar property whose type conforms to BarPresentable. The type BarPresentable represents any type that conforms to BarPresentable:

struct Bar: BarPresentable {}
struct Baz: BarPresentable {}

let aBarPresentable: BarPresentable = Bar()
aBarPresentable = Baz()

What you probably want instead is an associated type:

protocol FooPresentable {
   associatedtype BarBarPresentable: BarPresentable
   var bar: BarBarPresentable { get }
}

This means that a type A conforming to FooPresentable must have a property bar which has a type (TheBarPresentable) specific to A that conforms to BarPresentable. I think this is best explained with an example:

class Foo {
    let bar: Bar
    init(bar: Bar) { self.bar = bar }
}

extension Foo: FooPresentable {
    typealias BarBarPresentable = Bar
}

In fact, you don’t need the explicit typealias declaration as Swift can infer this for you:

extension Foo: FooPresentable {}

CodePudding user response:

Not the way you're doing it, no. You've written a protocol that says: to conform to me, declare a variable bar that is actually declared as BarPresentable. Your Foo doesn't do that; it declares a variable bar that is declared as being Bar, which is not the same thing.

What you want is a generic protocol where the generic is constrained to be an adopter of BarPresentable.

Here is a complete example that compiles:

protocol FooPresentable {
    // this says: `bar` must be declared as a type that adopts BarPresentable
    associatedtype T: BarPresentable
    var bar: T { get }
}

protocol BarPresentable {
}

struct Bar {}

struct Foo {
    // behold, `bar` _is_ declared as a type that adopts BarPresentable
    let bar: Bar
}

extension Bar: BarPresentable {
}

extension Foo: FooPresentable {
}

CodePudding user response:

FooPresentable has an associated type.

protocol FooPresentable {
  associatedtype Bar: BarPresentable
  var bar: Bar { get }
}

class Foo {
  let bar: Module.Bar = .init()
}

CodePudding user response:

You have to use existential types or associatedtype

protocol FooPresentable {
    associatedtype B : BarPresentable
    var bar: B { get }
}

Or

protocol FooPresentable {
    var bar: any BarPresentable { get }
}

Which is correct depends on your actual use case.

Check out Design protocol interfaces in Swift

  • Related