Compound AssociatedTypes protocols

Hi, I'm trying to create a generic solution for Validation. But stuck on a problem where I want to compound validators that validate the same type.

protocol Validator {
  associatedtype ValidationType
  func validate(_ value: ValidationType)
}

struct CompoundValidator<T, V: Validator>: Validator where V.ValidationType == T {
  typealias ValidationType = T
  private let validators: [V]

  init(validators: [V]) {
    self.validators = validators
  }

  func validate(_ value: ValidationType) {
    for validator in validators {
      validator.validate(value)
    }
  }
}

A sample how want to use

struct EmptyStringValidator: Validator {
  typealias ValidationType = String

  func validate(_ value: ValidationType) { }
}

struct LenghtValidator: Validator {
  typealias ValidationType = String

  func validate(_ value: ValidationType) { }
}


// works
let validator1= CompoundValidator(validators: [EmptyStringValidator(), EmptyStringValidator()]) 
validator1.validate("sample string")    

// doesn't work "Cannot convert value of type 'EmptyStringValidator' to expected element type '_'"
let validator2 = CompoundValidator(validators: [EmptyStringValidator(), LenghtValidator()])
validator2.validate("sample string")    

Can someone help me with it.

The way Generics work in Swift is each specialized version is, for this purpose, it's own unique Type. So, Array<SuperClass> is a different Type from Array<ChildClass> the same way Array<String> and Array<Int> are different.

So while the Type of validator1 can be CompoundValidator<EmptyStringValidator>, there's no reasonable Type for validator2 (since it has to a concrete type, i.e. not a protocol).

I believe there will be better options for this in the future, but one current workaround would be to re-organize the structure a bit.

protocol Validator {
  associatedtype ValidationType
  func validate(_ value: ValidationType)
}

struct GenericValidator<ValidationType>: Validator {

    let validation: (ValidationType) -> ()
    init(validation: @escaping (ValidationType) -> ()) {
        self.validation = validation
    }
    func validate(_ value: ValidationType) { validation(value) }
}

struct CompoundValidator<ValidationType>: Validator {

    let validators: [GenericValidator<ValidationType>]
    init(validators: [GenericValidator<ValidationType>]) {
        self.validators = validators
    }
    func validate(_ value: ValidationType) { validators.forEach{$0.validate(value)} }
}

let emptyStringValidator = GenericValidator<String>.init{ print(!$0.isEmpty) }
let lengthValidator = GenericValidator<String>.init{ print($0.count > 10) }
// works
let validator1 = CompoundValidator(validators: [emptyStringValidator, emptyStringValidator])
validator1.validate("sample string")

// doesn't work "Cannot convert value of type 'EmptyStringValidator' to expected element type '_'"
let validator2 = CompoundValidator(validators: [emptyStringValidator, lengthValidator])
validator2.validate("sample string")

Thank you @GetSwifty . I think this solution should help me.

Terms of Service

Privacy Policy

Cookie Policy