Home > Blockchain >  Is it possible to implement a protocol type variable with a concrete class in Swift?
Is it possible to implement a protocol type variable with a concrete class in Swift?

Time:09-06

Is it possible to implement a protocol type var with a concrete class which implements this protocol?

Does that sound confusing? Here is an example :-)

// A protocol requires some var. And an implementation of this class
protocol SomethingProtocol {
    var something: String { get }
}

class SomethingConcrete: SomethingProtocol {
    var something: String { "something" }
    var someOther: String { "something else" }
}


// A protocol which requires someValue of protocol type SomethingProtocol
protocol EntityProtocol {
    var someValue: SomethingProtocol { get }
}


// Implementation of EntityProtocol
class ConcreteEntiy: EntityProtocol {
    // DOES NOT WORK:
    // Implementing the var someValue of required typ SomethingProtocol
    // using its implementation SomethingConcret 
    //       
    // Error: Type 'ConcretEntiy' does not conform to protocol 'EntityProtocol'
    var someValue: SomethingConcrete = SomethingConcrete()



    // DOES WORK:
    // Implementation as SomethingProtocol as required by EntityProtocol
    // but no access to others values of SomethingConcret
    var someValue: SomethingProtocol = SomethingConcrete()
    
    
    // Error: Value of type 'SomethingProtocol' has no member 'somethingElse'
    let otherValue = someValue.somethingElse
}
  • EntityProtocol requires a var someValue of protocol type SomethingProtocol
  • SomethingConcrete implements SomethingProtocol and thus is of type SomethingProtocol
  • ConcreteClass implements EntityProtocol and thus needs a var someValue of protocol type SomethingProtocol

Why is it not possible to full fill the requirement of EntityProtocol by providing a var someValue of type SomethingConcrete?

CodePudding user response:

You're requiring the class conforming to EntityProtocol to have a property holding SomethingProtocol, & not any object conforming to that protocol.

That's why generics exist, when using associatedtype you're first implementation works because you're setting the type of someValue to be whatever you say it is that conforms to SomethingProtocol(i.e: SomethingConcret). Hence it's not set like your attempt, it is inferred.

// A protocol requires some var. And an implementation of this class
protocol SomethingProtocol {
    var something: String { get }
}

class SomethingConcret: SomethingProtocol {
    var something: String { "something" }
    var someOther: String { "something else" }
}


// A protocol which requires someValue conforming to the type SomethingProtocol
protocol EntityProtocol {
    associatedtype SomeValue: SomethingProtocol
    var someValue: SomeValue { get }
}


// Implementation of EntityProtocol
class ConcretEntiy: EntityProtocol {
    // WORKS:
    // Implementing the var someValue of required type SomethingProtocol
    // using its implementation SomethingConcret
   // has access to others values of SomethingConcret
    var someValue: SomethingConcret = SomethingConcret()



    // ALSO WORKS:
    // Implementation as SomethingProtocol as required by EntityProtocol
    // but no access to others values of SomethingConcret
    var someValue: SomethingProtocol = SomethingConcret()
}

CodePudding user response:

  1. Syntax error where you wrote extra =:
var someOther: String { "something else" }
  1. You can assign a conformed concrete to an abstracted var:
var someValue: SomethingProtocol = SomethingConcret()
  1. You can not access concrete's property from an abstracted var. You may want to cast it first:
var otherValue: String? { (someValue as? SomethingConcret)?.someOther 

Full working demo code (typos and syntax errors fixed)

protocol SomethingProtocol {
    var something: String { get }
}

class SomethingConcrete: SomethingProtocol {
    var something: String { "something" }
    var someOther: String { "something else" }
}

protocol EntityProtocol {
    var someValue: SomethingProtocol { get }
}

class ConcreteEntity: EntityProtocol {
    var someValue: SomethingProtocol = SomethingConcrete()
    var otherValue: String? { (someValue as? SomethingConcrete)?.someOther }
}
  • Related