Swift: extending sequences with a generic element [duplicate] - swift

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
}
}

Related

Instantiate collection without knowing concrete type

I have a class which is generic over a type T which conforms to the Collection protocol. Now I want to instantiate the collection without knowing the concrete type of the collection, is this possible?
class FileStore<T: Collection>{
var collection: T
init(){
collection = T() // This will never work
}
}
Collection doesn't have init amongst its protocol requirements, so you cannot directly initialize a Collection instance. You have several solutions though. You can either restrict your generic type constraint to a type that does guarantee having an init (such as Array) or you can create your own protocol requiring init, make T require conformance to that protocol and extend all Collections that you want to be able to store to conform to your protocol.
The second approach is shown below:
protocol Initializable {
init()
}
class FileStore<T: Collection> where T: Initializable {
var collection: T
init(){
collection = T.init()
}
}
// Extend the `Collection` conformant types
extension Array: Initializable {}
extension Dictionary: Initializable {}
extension Set: Initializable {}
// Create a FileStore
FileStore<Array<Int>>()
FileStore<[String:Int]>()
FileStore<Set<String>>()
Or depending on which exact types you want to be able to store, using the built-in RangeReplaceableCollection as your type constraint is even better. (Bear in mind that quite a few standard library types don't conform to RangeReplaceableCollection that do conform to Collection and have empty inits, such as Dictionary, Set, etc.).
class OtherFileStore<T: RangeReplaceableCollection> {
var collection = T.init()
}

Storing a generic conforming to an associated type inside A collection

I am trying to store a generic who uses an an associated type, however when trying to create a type which should conform to the generic type I describe in the generic list at the top of class A, but I get the error.
"Cannot invoke 'append' with an argument list of type '(B)'"
How can I properly declare the generic so that this code works?
class A<DataType: Any, AssociatedType: Runable> where
AssociatedType.DataType == DataType {
var array = Array<AssociatedType>()
func addAssociatedValue(data: DataType) {
array.append(B(data: data))
}
func runOnAll(with data: DataType) {
for item in array {
item.run(with: data)
}
}
}
class B<DataType>: Runable {
init(data: DataType) { }
func run(with: DataType) { }
}
protocol Runable {
associatedtype DataType
func run(with: DataType)
}
I am also using Swift 4.2 so if there is a solution that uses one of the newer Swift features that will also work as a solution.
B conforms to Runnable, yes, but you can't put it into an array that's supposed to store AssociatedTypes. Because the actual type of AssociatedType is decided by the caller of the class, not the class itself. The class can't say, "I want AssociatedType to always be B". If that's the case, you might as well remove the AssociatedType generic parameter and replace it with B. The caller can make AssociatedType be Foo or Bar or anything conforming to Runnable. And now you are forcing to put a B in.
I think you should rethink your model a bit. Ask yourself whether you really want AssociatedType as a generic parameter.
You could consider adding another requirement for Runnable:
init(data: DataType)
And add required to B's initializer. This way, you could write addAssociatedValue like this:
func addAssociatedValue(data: DataType) {
array.append(AssociatedType(data: data))
}

Swift 4 - Subclass Generic Constraint from associatedtype

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]
}

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
}
}

How do I specify that a non-generic Swift type should comply to a protocol?

I'd like to implement a Swift method that takes in a certain class type, but only takes instances of those classes that comply to a specific protocol. For example, in Objective-C I have this method:
- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter;
where GPUImageOutput is a particular class, and GPUImageInput is a protocol. Only GPUImageOutput classes that comply to this protocol are acceptable inputs for this method.
However, the automatic Swift-generated version of the above is
func addFilter(newFilter: GPUImageOutput!)
This removes the requirement that GPUImageOutput classes comply with the GPUImageInput protocol, which will allow non-compliant objects to be passed in (and then crash at runtime). When I attempt to define this as GPUImageOutput<GPUImageInput>, the compiler throws an error of
Cannot specialize non-generic type 'GPUImageOutput'
How would I do such a class and protocol specialization in a parameter in Swift?
Is swift you must use generics, in this way:
Given these example declarations of protocol, main class and subclass:
protocol ExampleProtocol {
func printTest() // classes that implements this protocol must have this method
}
// an empty test class
class ATestClass
{
}
// a child class that implements the protocol
class ATestClassChild : ATestClass, ExampleProtocol
{
func printTest()
{
println("hello")
}
}
Now, you want to define a method that takes an input parameters of type ATestClass (or a child) that conforms to the protocol ExampleProtocol.
Write the method declaration like this:
func addFilter<T where T: ATestClass, T: ExampleProtocol>(newFilter: T)
{
println(newFilter)
}
Your method, redefined in swift, should be
func addFilter<T where T:GPUImageOutput, T:GPUImageInput>(newFilter:T!)
{
// ...
}
EDIT:
as your last comment, an example with generics on an Enum
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
Specialized with protocol conformance:
enum OptionalValue<T where T:GPUImageOutput, T:GPUImageInput> {
case None
case Some(T)
}
EDIT^2:
you can use generics even with instance variables:
Let's say you have a class and an instance variable, you want that this instance variable takes only values of the type ATestClass and that conforms to ExampleProtocol
class GiveMeAGeneric<T: ATestClass where T: ExampleProtocol>
{
var aGenericVar : T?
}
Then instantiate it in this way:
var child = ATestClassChild()
let aGen = GiveMeAGeneric<ATestClassChild>()
aGen.aGenericVar = child
If child doesn't conform to the protocol ExampleProtocol, it won't compile
this method header from ObjC:
- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter { ... }
is identical to this header in Swift:
func addFilter<T: GPUImageOutput where T: GPUImageInput>(newFilter: T?) { ... }
both method will accept the same set of classes
which is based on GPUImageOutput class; and
conforms GPUImageInput protocol; and
the newFilter is optional, it can be nil;
From Swift 4 onwards you can do:
func addFilter(newFilter: GPUImageOutput & GPUImageInput)
Further reading:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
http://braking.github.io/require-conformance-to-multiple-protocols/
Multiple Type Constraints in Swift