Swift 4 - Subclass Generic Constraint from associatedtype - swift

I'd like to write a Swift protocol that requires a type to specify a base class and implement methods that operate on subclasses of that base class. Here's what that might look like (doesn't compile):
protocol Repository {
associatedtype BaseModel
//T must subclass BaseModel
func all<T: BaseModel>(from type: T.Type) -> [T]
}
But this generates the following compiler error:
Inheritance from non-protocol, non-class type 'Self.BaseModel'
This makes sense, because BaseModel could be specified with a struct type and subclassing wouldn't be allowed. So I tried creating an empty protocol, constrained to classes, to try to inform the compiler that this type will be a class type and allow a subclass constraint.
protocol Model: class { }
Then I constrained the BaseModel type using the Model class protocol:
associatedtype BaseModel: Model
But this generates the same compiler error from above. Is it possible to enforce a subclass constraint from an associatedtype on a protocol? I would expect the above to compile or for Swift to allow something like the following to allow subclass constraints:
associatedtype BaseModel: class

Associated Types should be used when type is unknown before the protocol is implemented. But if type is known no need to use associated type. I guess you could do this.
protocol Model: class { }
class BaseModel : Model { }
protocol Repository {
func all<T : BaseModel>(from type: T.Type) -> [T]
}

Related

Class conforming protocol throws "cannot conform to" class error

protocol AClass: class {
}
class Controller {
let classes: [AClass] = []
init() {
self.upload(classes: self.classes)
}
func upload<C: AClass>(classes: [C]) {
}
}
The line in the initializer has a compile-time error of:
Value of protocol type 'AClass' cannot conform to 'AClass'; only struct/enum/class types can conform to protocols
Why? The protocol can only be applied to a class. Do I need to tell the compiler something more?
When using generics (func upload<C: AClass>(classes: [C])), you ask for a generic type that conform to the protocol AClass.
However, in Swift a protocol doesn't conforms to itself, so using classes (which is AClass protocol) doesn't match the generic requirement (conforming to AClass).
In your case, I don't see the benefit of using a generic for the upload method. You can just use AClass protocol here:
class Controller {
let classes: [AClass] = []
init() {
self.upload(classes: self.classes)
}
func upload(classes: [AClass]) {
// here you can use any property / method defined in `AClass` protocol.
}
}

Multiple constraints on parameter of a generic function

Reading about generic functions in Swift, I see that it is possible to put some constraints on a parameter by requiring, that it is a subclass of a given class C, or that it implements a given protocol P.
But I wonder if there is a way to require both at the same time. I haven't found anything about that yet.
Does anyone know?
Actually You can do that.
If you have seen Codable in swift it is actually Decodable and Encodable
typealias Codable = Decodable & Encodable
So in some function if you are using generic T as Codable
struct StructOfCodable<T:Codable>: Codable {
....
}
Here is example
protocol Test {}
class TClass {
}
typealias Common = Test & TClass
func generic <T:Common>(method:T) {
}
Another way is protocol and class both can have super class. So you can create common protocol
like
protocol CommonInProtocolAndStruct { }
protocol ProtocolUsedAsConstraint:CommonInProtocolAndStruct {}
struct StructUsedAsConstraint:CommonInProtocolAndStruct {}
And any method you can use CommonInProtocolAndStruct as generic constraint
You can add any number of type constraints using a where clause. Examples:
import UIKit
func f<T>(t: T) where T: UIView, T: Encodable {}
class C<T> where T: UIView, T: Encodable {}

Validate conformance object to protocol with associated type

This is my code:
protocol Protocol {
associatedtype A
}
class Class: Protocol {
typealias A = Int
}
assert(type(of: Class()) == Protocol.self)
The error is ofcourse:
Protocol 'Protocol' can only be used as a generic constraint because
it has Self or associated type requirements
I understand the error, but I hope there is some way to check if a given object conform to a protocol with an associated type, without the need of binding/casting.

Swift: extending sequences with a generic element [duplicate]

I have a class that takes a generic class Collection: <T: Model> (Model is a class) and a protocol (Resource) that some of the subclasses of Collection implement:
class Collection: <T: Model> {
typealias Callback = (result: Collection <T>) -> ()
}
protocol Resource {...}
Is it possible to write a protocol extension where Self is an instance of Collection?
Trying to extend the protocol with the class that takes a generic:
extension Resource where Self: Collection {
func fetch() {}
}
Gives:
Reference to generic type 'Collection' requires arguments in <...>
Trying to extend the class that takes a generic with the protocol:
extension Collection where Self: Resource {
func fetch(callback: Callback?) {}
}
Gives:
'Self' is only available in a protocol or as the result of method in a class
I'm not sure how to proceed. The goal is for the function to only be available on instances of Collection that conform to Resource.
The problem is Collection is a generic class so every where you declare it, you must attached the specialized type, like Collection<T>. However extension to a protocol can't be specified with a generic type so you end up not being able to supply T to Collection.
In your case though, T is constrained to be of type Model so why not use that in the default protocol implementation:
extension Resource where Self: Collection<Model> {
func fetch() {
// default implementation
}
}

Further constraining a generic function from a Swift Protocol

I have a Swift protocol defined like this:
protocol MyProtocol {
func genericMethod<T:MyProtocol>(param:T) -> ()
}
I can implement the generic method in a base class like this:
class MyBaseClass : MyProtocol {
func genericMethod<T where T:MyProtocol>(param:T) -> () {
println("Performing generic method for type \(T.self)")
}
}
class MySubClass : MyBaseClass {
...
}
So far, so good. I can implement this method and it compiles and runs just fine.
Now, I want to do something similar but in my base class I want to further constrain the type of the generic method by requiring it to conform with a protocol such as Comparable. I try this:
class MyBaseClass : MyProtocol {
func genericMethod<T where T:MyProtocol, T:Comparable>(param:T) -> () {
println("Performing generic method for type \(T.self)")
}
}
Once I add this additional constraint on type T, the class MyClass will not compile because it does not conform to the protocol anymore.
It seems like adding an additional constraint on a generic type should not cause it to cease conforming with a protocol. What am I missing? It seems to me that the protocol is saying that genericMethod must be passed a parameter of a type that conforms with MyProtocol. When I go to implement this in MyBaseClass - just one possible implementation of MyProtocol - that I should be able to restrict that implementation further by saying that the parameter myst conform with Comparable in addition to MyProtocol
Is there a way to refine a generic type in a base implementation like I'm trying to do here?
Adding the additional constraint on a generic type should cause it to cease conforming with the protocol because the protocol is supposed to guarantee conformance, and conformance cannot be guaranteed with subtypes that aren't Comparable. If you want all MyProtocol objects to conform to Comparable then you should make it part of the MyProtocol definition.
protocol MyProtocol: Comparable {
//...
}
I haven't tried this, but it might also work if you make MyBaseClass a Comparable type.
One solution is to go the other way - define your protocol's version of the generic as the most restrictive case. This compiles:
protocol P {
func genericMethod<T where T:P, T:Comparable>(param:T) -> ()
}
class C1 : P {
func genericMethod<T> (param:T) -> () {} // compiles even though omits Comparable
func test() {
genericMethod(C1()) // compiles even though C1 is not a Comparable
}
}