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

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

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

How to cast to protocol type in a class?

This is my code:
protocol ProtocolA {
static var myProperty: Int { get }
}
protocol ProtocolB {}
extension ProtocolB {
func letsDoSomething() {
(Self.self as! ProtocolA.Type).myProperty // Works
}
}
class MyClass {
func castSelfToProtocolAType() {
(Self.self as! ProtocolA.Type).myProperty // Doesn't work
(Self as! ProtocolA.Type).myProperty // Doesn't work also
}
}
How can I cast self in MyClass to the dynamic type (like in the protocol extension) ProtocolA.Type?
As per your question,
You are trying to convert your class instance to the protocol type instance, which is not possible because your class does not conform to that protocol.
The direct casting from class/struct instance to protocol type
instance is not possible, you can only convert to protocol type by
accepting that class/struct as a method parameter or by assigning to the other property which protocol type, this is known as
implicit type casting.
Please read this article to understand more about protocols and class types
https://medium.com/swift-india/protocol-the-power-of-swift-5dfe9bc41a99
https://medium.com/swift-india/protocol-the-power-of-swift-950c85bb69b1
https://medium.com/swift-india/protocol-the-power-of-swift-45e97f6531f9
https://medium.com/#hitendrahckr/protocol-the-power-of-swift-6906cdedd867
https://medium.com/swift-india/protocol-the-power-of-swift-1e5b86bfd1dc

Swift protocol as generic type

I have this kind of code:
protocol MyProtocol {
}
class P1: MyProtocol {
}
class P2: MyProtocol {
}
class C <T: MyProtocol> {
}
Then i need to define a variable to delegate all kinds of C<MyProtocol>:
var obj: C <MyProtocol>
But compile error comes:
Using 'MyProtocol' as a concrete type conforming to protocol 'MyProtocol' is not supported
How can I do?
This code:
class C <T: MyProtocol> { }
Means that C is a generic class that can specialize on any type T that conforms to MyProtocol.
When you declare:
var obj: C <MyProtocol>
You are (I think) trying to say that the var obj will be an instance of C specialized to some type that conforms to MyProtocol,but you can't specialize a generic class on a protocol type, because there is no such thing as a direct concrete instance of a protocol. There can only be instances of a type conforming to the protocol. And there can theoretically be many different types that conform to the protocol. So that notation doesn't really tell the compiler which specific specialization of C to use.
This shouldn't be a problem though, because you can write things like:
var obj: C<P1> = C<P1>()
or
var obj = C<P2>() // type is inferred
And within your class C you can still treat any uses of T as conforming to MyProtocol. So I think this should give you everything you need, as long as you remember that an instance of a generic class must be specialized to a single specific concrete type, not a protocol which could represent many possible concrete types.
You usually don't need to declare type for a Swift's variable. The compiler can infer type in most cases. Also, for generic class, you should let the compiler figure out what the generic classes resolve to:
class C<T: MyProtocol> {
var value: T
init (value: T) {
self.value = value
}
}
var obj = C(value: P1()) // type: C<P1>
// or:
var obj = C(value: P2()) // type: C<P2>

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

protocol associated type typealias assignment compile error

Following code:
protocol SomeProtocol {
typealias SomeType = Int // used typealias-assignment
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) {
print(someVar)
}
}
gives compile-time error:
Use of undeclared type 'SomeType'
Adding, say typealias SomeType = Double, to the SomeClass resolves the error.
The question is, what's the point of typealias-assignment part (which is optional btw) of protocol associated type declaration though?
In this case the assignment of Int to the typealias is equal to no assignment because it gets overridden by your conforming type:
// this declaration is equal since you HAVE TO provide the type for SomeType
protocol SomeProtocol {
typealias SomeType
func someFunc(someVar: SomeType)
}
Such an assignment provides a default type for SomeType which gets overridden by your implementation in SomeClass, but it is especially useful for protocol extensions:
protocol Returnable {
typealias T = Int // T is by default of type Int
func returnValue(value: T) -> T
}
extension Returnable {
func returnValue(value: T) -> T {
return value
}
}
struct AStruct: Returnable {}
AStruct().returnValue(3) // default signature: Int -> Int
You get the function for free only by conforming to the protocol without specifying the type of T. If you want to set your own type write typealias T = String // or any other type in the struct body.
Some additional notes about the provided code example
You solved the problem because you made it explicit which type the parameter has. Swift also infers your used type:
class SomeClass: SomeProtocol {
func someFunc(someVar: Double) {
print(someVar)
}
}
So SomeType of the protocol is inferred to be Double.
Another example where you can see that SomeType in the class declaration doesn't refer to to the protocol:
class SomeClass: SomeProtocol {
typealias Some = Int
func someFunc(someVar: Some) {
print(someVar)
}
}
// check the type of SomeType of the protocol
// dynamicType returns the current type and SomeType is a property of it
SomeClass().dynamicType.SomeType.self // Int.Type
// SomeType gets inferred form the function signature
However if you do something like that:
protocol SomeProtocol {
typealias SomeType: SomeProtocol
func someFunc(someVar: SomeType)
}
SomeType has to be of type SomeProtocol which can be used for more explicit abstraction and more static code whereas this:
protocol SomeProtocol {
func someFunc(someVar: SomeProtocol)
}
would be dynamically dispatched.
There is some great information in the documentation on "associated types" in protocols.
Their use is abundant throughout the standard library, for an example reference the SequenceType protocol, which declares a typealias for Generator (and specifies that it conforms to GeneratorType). This allows the protocol declaration to refer to that aliased type.
In your case, where you used typealias SomeType = Int, perhaps what you meant was "I want SomeType to be constrained to Integer-like behavior because my protocol methods will depend on that constraint" - in which case, you may want to use typealias SomeType: IntegerType in your protocol, and then in your class go on to assign a type to that alias which conforms to IntegerType.
UPDATE
After opening a bug w/ Apple on this and having had extensive discussion around it, I have come to an understanding of what the base issue is at the heart of this:
when conforming to a protocol, you cannot directly refer to an associated type that was declared only within that protocol
(note, however, that when extending a protocol the associated type is available, as you would expect)
So in your initial code example:
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) { // use of undeclared type "SomeType"
print(someVar)
}
}
...the error re: "use of undeclared type" is correct, your class SomeClass has not declared the type SomeType
However, an extension to SomeProtocol has access to the associated type, and can refer to it when providing an implementation:
(note that this requires using a where clause in order to define the requirement on the associated type)
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
extension SomeProtocol where SomeType == Int {
func someFunc(someVar: SomeType) {
print("1 + \(someVar) = \(1 + someVar)")
}
}
class SomeClass: SomeProtocol {}
SomeClass().someFunc(3) // => "1 + 3 = 4"
There is great article that actually gives you answer for your question. I suggest everyone to read it to get into type-aliases and some more advanced stuff that comes up when you use it.
Citation from website:
Conceptually, there is no generic protocols in Swift. But by using
typealias we can declare a required alias for another type.