Getting around "Protocol X can only be used as a generic constraint" - swift

I'm working on a form-input library. My goal is to have a re-usable set of validators which can be applied to a set of form fields. I'm running into difficulty specialising my generic protocol. The full error from the code below is protocol 'FieldValidator' can only be used as a generic constraint because it has Self or associated type requirements.
Complete playground-ready code:
import Foundation
protocol FieldValidator {
associatedtype InputType: Any
func validate(input value: InputType)
}
struct EmailValidator: FieldValidator {
func validate(input value: String) {}
}
enum Field {
case string(_: [FieldValidator])
case integer(_: [FieldValidator])
}
let emailField: Field = .string([EmailValidator()])
What I've tried
I understand that in the Field enum I can't just throw in a FieldValidator because it needs to know what InputType of validator it requires. I expect that I need to tell it somehow, maybe something like this:
case string(_: [FieldValidator<String>])
case integer(_: [FieldValidator<Int>])
or this:
case string(_: [FieldValidator where InputType == String])
case integer(_: [FieldValidator where InputType == Int])
but these doesn't work. Is there a way to keep this kind of architecture?
Edit using struct instead of enum for field types:
struct StringField {
typealias InputType = String
let validators: [FieldValidator]
}
I still appear to have the same problem defining the set of validators (which must be provided when the Field is initialised): protocol 'FieldValidator' can only be used as a generic constraint because it has Self or associated type requirements.

I suppose what I'm trying to do is provide a mechanism by which someone can define a Field, define what type of value it holds, and define a set of reusable Validators which will operate on that value and determine if it's valid
You might be after something like this; it's stupid but effective, especially if there are not very many field types in question:
protocol FieldValidator {
associatedtype T
func validate(input:T)
}
class StringValidator : FieldValidator {
func validate(input:String) { fatalError("must override me") }
}
class IntValidator : FieldValidator {
func validate(input:Int) { fatalError("must override me") }
}
class ActualStringValidator : StringValidator {
override func validate(input:String) { print(input)}
}
enum Field {
case string([StringValidator])
case int([IntValidator])
}
As you can see, I've simply used the class hierarchy to solve the problem (so that we don't have to do type erasure). In particular, it is now legal to say:
let f = Field.string([ActualStringValidator()])
Here's how to test it:
let f = Field.string([ActualStringValidator()])
if case Field.string(let arr) = f {
for thing in arr {
thing.validate(input:"howdy")
}
}

Related

Swift Implicitly pass self as inout parameter for reference type

Is it possible to implicitly pass self as an inout parameter to modify a reference variable in place?
Here is a method which can convert an abstract base class into one of its concrete subclasses. My question is, must I always have that first argument, obj: inout AbstractBaseClass, or can I implicitly pass self. I realize that this might also be expressed as a static method.
func convertTo(_ obj: inout AbstractBaseClass, _ type: ConcreteClassTypes) {
switch type {
case .concreteClass1: obj = ConreteClass1()
case .concreteClass2: obj = ConcreteClass2()
}
}
Here is the full code:
class AbstractClass {
enum ConcreteType {
case concreteClass1
case concreteClass2
}
var id: Int = 0
fileprivate init() { }
func convert(_ obj: inout AbstractClass, to type: ConcreteType) {
let oldId = obj.id
switch type {
case .concreteClass1: obj = ConcreteClass1()
case .concreteClass2: obj = ConcreteClass2()
}
obj.id = oldId
}
class ConcreteClass1: AbstractClass {
override init() { super.init() }
}
class ConcreteClass2: AbstractClass {
override init() { super.init() }
}
}
var obj: AbstractClass = AbstractClass.ConcreteClass1()
obj.convert(&obj, to: .concreteClass2) //is there any way to eliminate this first argument?
Like matt, I'm not convinced that inout is the right tool for the job in this case.
Although that being said, if you insist on it, one way to achieve what you want is to (ab)use protocol extensions. They allow the definition of mutating methods, which pass the implicit self parameter as inout (to allow the mutation of adopting value types).
So you could say:
protocol AbstractClassProtocol {}
class AbstractClass : AbstractClassProtocol {
enum ConcreteType {
case concreteClass1
case concreteClass2
}
fileprivate init() {}
class ConcreteClass1: AbstractClass {
override init() { super.init() }
}
class ConcreteClass2: AbstractClass {
override init() { super.init() }
}
}
extension AbstractClassProtocol where Self == AbstractClass {
mutating func convert(to type: AbstractClass.ConcreteType) {
switch type {
case .concreteClass1:
self = AbstractClass.ConcreteClass1()
case .concreteClass2:
self = AbstractClass.ConcreteClass2()
}
}
}
var obj: AbstractClass = AbstractClass.ConcreteClass1()
obj.convert(to: .concreteClass2)
print(obj) // AbstractClass.ConcreteClass2
But it's a bit of a hack, and I'd be wary about using it.
...to modify a reference variable in place? Here is a method which can convert an abstract base class into one of its concrete subclasses...
You are not "modifying" or "converting" anything. You are substituting one object for another. Thus, there is no self that could be passed here; the idea of what you are doing is to destroy one self and provide another in its place.
That said, it's a little unclear what the inout variable is for. Why don't you just assign the new object in place of the old object?
func giveMeA( _ type: AbstractClass.ConcreteType) -> AbstractClass {
switch type {
case .concreteClass1: return AbstractClass.ConcreteClass1()
case .concreteClass2: return AbstractClass.ConcreteClass2()
}
}
var obj: AbstractClass = AbstractClass.ConcreteClass1()
obj = giveMeA(.concreteClass2)
The effect is identical to what you're doing. If you think it's not, you're just kidding yourself about what the inout parameter is doing.
I'm going to propose a completely different way of looking at what you're trying to do.
Don't have an abstract superclass. Don't have multiple subclasses. Have one class with multiple functional variants. The functional variants are expressed by a helper object — a struct — owned by the class instance.
So to change functionalities, you just set the helper to a different type of helper. Basically, you give your object a personality transplant.
I have an app that works that way. I have four view controllers that present slightly different info in slightly different ways. But in fact they are one view controller and an enum with four cases that dictates those differences. Thus, at any time the view controller can manifest itself as any of the four types.

How can I create a function in Swift that returns a Type which conforms to a protocol?

How can I create a function in Swift that returns a Type which conforms to a protocol?
Here is what I'm trying right now, but it obviously won't compile like this.
struct RoutingAction {
enum RoutingActionType{
case unknown(info: String)
case requestJoinGame(gameName: String)
case requestCreateGame(gameName: String)
case responseJoinGame
case responseCreateGame
}
// Any.Type is the type I want to return, but I want to specify that it will conform to MyProtocol
func targetType() throws -> Any.Type:MyProtocol {
switch self.actionType {
case .responseCreateGame:
return ResponseCreateGame.self
case .responseJoinGame:
return ResponseJoinGame.self
default:
throw RoutingError.unhandledRoutingAction(routingActionName:String(describing: self))
}
}
}
I would personally prefer returning an instance instead of a type but you can do it that way too. Here's one way to achieve it:
protocol MyProtocol:class
{
init()
}
class ResponseCreateGame:MyProtocol
{
required init() {}
}
class ResponseJoinGame:MyProtocol
{
required init() {}
}
enum RoutingActionType
{
case unknown(info: String),
requestJoinGame(gameName: String),
requestCreateGame(gameName: String),
responseJoinGame,
responseCreateGame
// Any.Type is the type I want to return, but I want to specify that it will conform to MyProtocol
var targetType : MyProtocol.Type
{
switch self
{
case .responseCreateGame:
return ResponseCreateGame.self as MyProtocol.Type
case .responseJoinGame:
return ResponseJoinGame.self as MyProtocol.Type
default:
return ResponseJoinGame.self as MyProtocol.Type
}
}
}
let join = RoutingActionType.responseJoinGame
let objectType = join.targetType
let object = objectType.init()
Note that your protocol will need to impose a required init() to allow creation of instances using the returned type.
Note2: I changed the structure a little to make my test easier but i'm sure you'll be able to adapt this sample to your needs.
Why do you not want to use simple:
func targetType() throws -> MyProtocol
?
EDIT:
I think you can't. Because if you return Type actually you return an instance of class Class and it can't conform your protocol. This runtime's feature was inherited from objective-c. You can see SwiftObject class.

Swift, classes based on extended protocol don't conform to original protocol

These protocols are giving me nightmares.
I am trying to implement a couple protocols and classes conforming to them, such that I can have default implementations, but customized implementations are available by extending the protocols/classes. So far, this is what I have:
protocol ProtA {
var aProperty: String { get set }
var anotherProperty:String { get set }
func aFunc (anArgument: String) -> String
}
protocol ProtB: ProtA {
var aThirdProperty: String { get set }
}
protocol ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA)
}
class ClassA: ProtA {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassB: ProtB {
var aProperty: String = "Hello, I'm a String."
var anotherProperty: String = "I'm not the same String."
var aThirdProperty: String = "I'm yet another String!"
func aFunc (anArgument: String) -> String {
return anArgument
}
}
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
Then, if I do
class ClassC: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aProperty) // Works fine.
}
}
But, if I do
class ClassD: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtA) {
print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty'
}
}
and, if instead I do
class ClassE: ProtC {
func doSomething(parameter: Int, with anotherParameter: ProtB) {
print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC'
}
}
What am I doing wrong?
The problem
When inheriting from a type, you cannot narrow down the types of the parameters used in overridden functions. This is what you've done by changing the parameter from type ProtA (a more general type), to ProtB (a more specific type).
This is a consequence of the Liskov substitution principle. Simply put, a subclass must be able to do (at minimum) everything that the superclass can do.
ProtC establishes that all conforming types have a function func doSomething(parameter: Int, with anotherParameter: ProtA), with type (Int, ProtA) -> Void).
Your modified function in ClassE has type (Int, ProtB) -> Void. However, this function can no longer act as a substitute for the one it overrides.
Suppose that it was possible to do what you tried. Watch what would happen:
let instanceConformingToProtA: ProtA = ClassA()
let instanceConformingToProtC: ProtC = ClassE()
// This would have to be possible:
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA)
But, ClassE() can't take instanceConformingToProtA as a valid argument to its second parameter, because it's a ProtA, not the required ProtB.
The solution
The solution to this problem is entirely dependant on what you're trying to achieve. I would need further information before being able to proceed.
As a rule of thumb, when overriding inherited members:
Parameter types must be the same, or more general.
E.g. you can't override a function with a parameter of type Car, and change the parameter type to RaceCar. Doing so breaks your classes ability to work with RaceCars, which it must be able to do by the LSP.
E.g. you can override a function with a parameter of type Car, and change the parameter to Vehicle. Doing so preserves your classes' ability to work with `Vehicles.
Return types must be the same, or more specific.
E.g. you can't override a function with a return type of Car with a function that returns Vehicle. Doing so would mean the returned value is "less powerful" than the super class guarantees it should be.
E.g. you can override a function with a return type of Car with a function that returns RaceCar. Doing so would mean the returned value is "more powerful", and it does at least as much as what the super class guarentees.
There's nothing wrong. You should just make sure that the declarations are semantically consistent.
You should either create ProtD declaring the method with a ProtB parameter OR unwrapping the gotten ParamA parameter to use it as ProtB.
func doSomething(parameter: Int, with anotherParameter: ProtB) {
if let a = anotherParameter as? ProtA {
print (a.aThirdProperty)
}
}

Collection of <type implementing protocol> in Swift [duplicate]

As an exercise in learning I'm rewriting my validation library in Swift.
I have a ValidationRule protocol that defines what individual rules should look like:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
The associated type InputType defines the type of input to be validated (e.g String). It can be explicit or generic.
Here are two rules:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
Elsewhere, I have a function that validates an input with a collection of ValidationRules:
static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult {
let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage }
return errors.isEmpty ? .Valid : .Invalid(errors)
}
I thought this was going to work but the compiler disagrees.
In the following example, even though the input is a String, rule1's InputType is a String, and rule2s InputType is a String...
func testThatItCanEvaluateMultipleRules() {
let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 }
let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2")
let invalid = Validator.validate(input: "", rules: [rule1, rule2])
XCTAssertEqual(invalid, .Invalid(["message1", "message2"]))
}
... I'm getting extremely helpful error message:
_ is not convertible to ValidationRuleLength
which is cryptic but suggests that the types should be exactly equal?
So my question is... how do I append different types that all conform to a protocol with an associated type into a collection?
Unsure how to achieve what I'm attempting, or if it's even possible?
EDIT
Here's it is without context:
protocol Foo {
typealias FooType
func doSomething(thing: FooType)
}
class Bar<T>: Foo {
typealias FooType = T
func doSomething(thing: T) {
print(thing)
}
}
class Baz: Foo {
typealias FooType = String
func doSomething(thing: String) {
print(thing)
}
}
func doSomethingWithFoos<F: Foo>(thing: [F]) {
print(thing)
}
let bar = Bar<String>()
let baz = Baz()
let foos: [Foo] = [bar, baz]
doSomethingWithFoos(foos)
Here we get:
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
I understand that. What I need to say is something like:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
Protocols with type aliases cannot be used this way. Swift doesn't have a way to talk directly about meta-types like ValidationRule or Array. You can only deal with instantiations like ValidationRule where... or Array<String>. With typealiases, there's no way to get there directly. So we have to get there indirectly with type erasure.
Swift has several type-erasers. AnySequence, AnyGenerator, AnyForwardIndex, etc. These are generic versions of protocols. We can build our own AnyValidationRule:
struct AnyValidationRule<InputType>: ValidationRule {
private let validator: (InputType) -> Bool
init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) {
validator = base.validate
}
func validate(input: InputType) -> Bool { return validator(input) }
}
The deep magic here is validator. It's possible that there's some other way to do type erasure without a closure, but that's the best way I know. (I also hate the fact that Swift cannot handle validate being a closure property. In Swift, property getters aren't proper methods. So you need the extra indirection layer of validator.)
With that in place, you can make the kinds of arrays you wanted:
let len = ValidationRuleLength()
len.validate("stuff")
let cond = ValidationRuleCondition<String>()
cond.validate("otherstuff")
let rules = [AnyValidationRule(len), AnyValidationRule(cond)]
let passed = rules.reduce(true) { $0 && $1.validate("combined") }
Note that type erasure doesn't throw away type safety. It just "erases" a layer of implementation detail. AnyValidationRule<String> is still different from AnyValidationRule<Int>, so this will fail:
let len = ValidationRuleLength()
let condInt = ValidationRuleCondition<Int>()
let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)]
// error: type of expression is ambiguous without more context

Returning a generic from a protocol-defined function in Swift

I declared a protocol with a generic function, but it seems that the type inference isn't working properly after implementing it.
protocol SearchableRealmModel {
static func search<Self: Object>(needle: String) -> Results<Self>?
}
class Thing: Object, SearchableRealmModel {
class func search<Thing>(needle: String) -> Results<Thing>? {
return realm()?.objects(Thing).filter("name == '\(needle)'")
}
}
let things = Thing.search("hello") // works but inferred type is Results<Object>?
The problem here is that the inferred type of things is Results<Object>?. I realize these variations can be used,
let things: Results<Thing>? = Thing.search("hello")
let things = Thing.search("hello") as Results<Thing>?
but having to specify the type every time is quite repetitive.
In my tests, using other types than Results<..>? kept the type inference intact. And this could be caused by having to specify the parent class in Self: Object (which is required because of Results).
Any help is appreciated.
This is a limitation of Swift's generics machinery. The compiler can generate a concrete signature for static func search(needle: String) -> Results<Object>? which satisfies the type constraint because Object subclasses will match this. You could probably file a bug towards bugs.swift.org because I think the Swift core team would also consider this to be a bug, if not very unexpected behavior.
However, you can modify your code to use protocol extensions to do what you want:
protocol SearchableRealmModel {}
extension SearchableRealmModel where Self: Object {
static func search(needle: String) -> Results<Self> {
return try! Realm().objects(Self).filter("name == '\(needle)'")
}
}
class Thing: Object, SearchableRealmModel {
dynamic var name = ""
}
let result = Thing.search("thing1") // => inferred as Results<Thing>
print(result.first?.name)
If you want custom implementations of search for other Realm models, you can reimplement the function there, which the compiler will prioritize over the protocol extension version:
class OtherThing: Object, SearchableRealmModel {
dynamic var id = ""
static func search(needle: String) -> Results<OtherThing> {
return try! Realm().objects(OtherThing).filter("id == '\(needle)'")
}
}