Search code examples
swiftprotocolsextension-methodsprotocol-extensionprotocol-oriented

Swift Extension computed variable not read correctly until declared in protocol


I have a protocol extension which declares and assigns a static computed variable:

protocol DataType {
    
}
extension DataType {
    static var mocks: [Self] { [] }
}

Then I have another protocol named Provider which has an associatedtype requirement of the DataType protocol and an extension:

protocol Provider {
    associatedtype Data: DataType
}

extension Provider {
    static var mock: [Data] {
        Data.mocks
    }
}

I then create the following types that conform to DataType and Provider:

struct Car: DataType {
    var name: String
    
    static var mocks: [Car] {
        [.init(name: "Nissan"), .init(name: "Toyota")]
    }
}

struct CarProvider: Provider {
    typealias Data = Car
}

print(CarProvider.mock)

When I print this out (an empty array []), the CarProvider static variable mock prints out the default value of the mocks variable of DataType - even when Car has an assigned array value for mocks inside its struct definition

However, as soon as I declare the mocks property inside the DataType protocol as a requirement, then the mocks value of Car is correctly read (printing the correct values: [__lldb_expr_93.Car(name: "Nissan"), __lldb_expr_93.Car(name: "Toyota")]):

protocol DataType {
    static var mocks: [Self] { get }
}

Why is the property definition required in the Protocol definition in the first place? Shouldn't the extension value be sufficient? And since the Car struct is assigning its own value to the mocks variable, shouldn't that be read instead of the default extension value?


Solution

  • Why is the property definition required in the Protocol definition in the first place? Shouldn't the extension value be sufficient?

    No. An implementation in a protocol extension can only be considered a true default implementation, if there is some way for it to be overridden in scope. Without having the requirement in the protocol, Swift has no reason or mechanism to look beyond an extension, because there's nothing else that will match, semantically.

    protocol DataType { }
    
    extension DataType {
      static var mocks: [Self] { [] }
    }
    
    func mocks<Data: DataType>(_: Data.Type) -> [Data] {
      Data.mocks // This *is* the extension. That is the only truth.
    }
    
    protocol DataType {
      static var mocks: [Self] { get }
    }
    
    extension DataType {
      static var mocks: [Self] { [] }
    }
    
    func mocks<Data: DataType>(_: Data.Type) -> [Data] {
      Data.mocks // Now, we can dispatch to a concrete `Data` type!
    }