The problem, which David has already alluded to, is that once you constrain a protocol's associatedtype
to a specific (non @objc
) protocol, you have to use a concrete type to satisfy that requirement.
This is because protocols don't conform to themselves – therefore meaning that you cannot use Address
to satisfy the protocol's associated type requirement of a type that conforms to Validator
, as Address
is not a type that conforms to Validator
.
As I demonstrate in my answer here, consider the counter-example of:
protocol Validator {
init()
}
protocol Address : Validator {}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
}
extension FormRepresentable {
static func foo() {
// if ValueWrapper were allowed to be an Address or Validator,
// what instance should we be constructing here?
// we cannot create an instance of a protocol.
print(ValueWrapper.init())
}
}
// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
typealias ValueWrapper = Address
}
The simplest solution would be to ditch the Validator
protocol constraint on your ValueWrapper
associated type, allowing you to use an abstract type in the method argument.
protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}
If you need the associated type constraint, and each AddressFrom
instance only expects a single concrete implementation of Address
as an input – you could use generics in order for your AddressFrom
to be initialised with a given concrete type of address to be used in your method.
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom<T : Address> : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: T) -> String {
// ...
}
}
// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom<ShippingAddress>.Address1
However, if you require both the associated type constraint and each AddressFrom
instance must be able to handle an input of any type of Address
– you'll have use a type erasure in order to wrap an arbitrary Address
in a concrete type.
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
struct AnyAddress : Address {
private var _base: Address
var addressLine1: String {
get {return _base.addressLine1}
set {_base.addressLine1 = newValue}
}
var country: String {
get {return _base.country}
set {_base.country = newValue}
}
var city: String {
get {return _base.city}
set {_base.city = newValue}
}
init(_ base: Address) {
_base = base
}
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: AnyAddress) -> String {
// ...
}
}
let addressFrom = AddressFrom.Address1
let address = ShippingAddress(addressLine1: "", city: "", country: "")
addressFrom.valueForDetail(AnyAddress(address))