Swift protocol error: 'weak' cannot be applied to non-class type - swift

What's the difference between Protocols and class-bound Protocols, and which one we should use in Swift?
protocol A : class { ... }
protocol A { ... }
We get an error when attempting to add a weak delegate when the Protocol is not defined as : class:
protocol A { ... }
weak var delegate: A
Gives the error:
'weak' cannot be applied to non-class type
or
'weak' must not be applied to non-class-bound 'A'; consider adding a protocol conformance that has a class bound

Swift >= 4:
protocol A : AnyObject { ... {
Swift < 4:
protocol A : class { ... }
defines a "class-only protocol": Only class types (and not structures or enumerations) can adopt this protocol.
Weak references are only defined for reference types. Classes
are reference types, structures and enumerations are value types.
(Closures are reference types as well, but closures cannot adopt
a protocol, so they are irrelevant in this context.)
Therefore, if the object conforming to the protocol needs to be stored in a weak property then the protocol must be a class-only protocol.
Here is another example which requires a class-only protocol:
protocol A {
var name : String { get set }
}
func foo(a : A) {
a.name = "bar" // error: cannot assign to property: 'a' is a 'let' constant
}
This does not compile because for instances of structures and enumerations, a.name = "bar" is a mutation of a. If you define
the protocol as
protocol A : class {
var name : String { get set }
}
then the compiler knows that a is an instance of a class type to that
a is a reference to the object storage,
and a.name = "bar" modifies the referenced object, but not a.
So generally, you would define a class-only protocol if you need
the types adopting the protocol to be reference types and not value types.

If you are using Swift 4 or later, use AnyObject:
protocol A : AnyObject { ... }
Using class as before gives the warning and fix-it:
Using 'class' keyword to define a class-constrained protocol is deprecated; use 'AnyObject' instead
Replace 'class' with 'AnyObject'

You can make the protocol derive from any class type like NSObject or AnyObject:
protocol TopNewsTableDelegate : AnyObject {
func topNewsTableDidLoadedStories()
}

Or you can type like this
#objc protocol A { ... }
then you can make a weak delegate reference

protocol CustomProtocolName : NSObjectProtocol {
// ...
}

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

Error "Redundant constraint 'Self' : 'AnyObject'" - where is the `AnyObject`?

I found a strange Swift compiler message while developing (I'm using Swift 4.1):
protocol Foo: class where Self: NSObject { // (1)
// Redundant constraint 'Self' : 'AnyObject'
}
What is happening here?
First, this is not redundant. When I write
protocol Foo: class { } // (2)
I have a protocol that any object could potentially conform to, even objects that don't derive from NSObject. But I can create weak references: weak var f: Foo? is okay.
On the other hand, when I write
protocol Foo where Self: NSObject { } // (3)
I have a protocol where I cannot produce weak references: weak var f: Foo? is a compile time error.
Second, where does the AnyObject come from? I'm asking for NSObject. The NSObject is respected though: I cannot declare a class MyFoo: Foo { } because it rightly complains that it must inherit from NSObject?
Is this a bug in Swift? Or am I missing something? If it is a bug: is it a bug because snippet (3) does not let me take weak references? Or because of the compiler warning? Or both? And if I am missing something: what is it?
It is not possible to constrain a protocol to be subclasses of a specific class in Swift 4.1. You can inherit Foo from NSObjectProtocol, which probably matches your intent.
protocol Foo: NSObjectProtocol {
// ....
}
In Swift 4.2, what you've written is legal Swift and does what you expect.
From the Swift public API:
public typealias AnyObject
/// The protocol to which all class types implicitly conform.
So by declaring your protocol to conform to class it automatically conforms to AnyObject

How to make protocol associated type require protocol inheritance and not protocol adoption

In my swift project I have a case where I use protocol inheritance as follow
protocol A : class{
}
protocol B : A{
}
What Im trying to achieve next is declaring another protocol with associated type, a type which must inherit from protocol A. If I try to declare it as :
protocol AnotherProtocol{
associatedtype Type : A
weak var type : Type?{get set}
}
it compiles without errors but when trying to adopt AnotherProtocol in the following scenario:
class SomeClass : AnotherProtocol{
typealias Type = B
weak var type : Type?
}
compilation fails with error claiming that SomeClass does not conform to AnotherProtocol. If I understood this correctly it means that B
does not adopt A while Im trying to declare and asking you on how to declare an associated type which inherits from protocol A?
I made the above assumption based on fact that the following scenario compiles just fine
class SomeDummyClass : B{
}
class SomeClass : AnotherProtocol{
typealias Type = SomeDummyClass
weak var type : Type?
}
This is pretty interesting. It appears that once you constrain the type of an associatedtype in a given protocol, you need to provide a concrete type in the implementation of that protocol (instead of another protocol type) – which is why your second example worked.
If you remove the A constraint on the associated type, your first example will work (minus the error about not being able to use weak on a non-class type, but that doesn’t seem to be related).
That all being said, I can't seem to find any documentation in order to corroborate this. If anyone can find something to back this up with (or completely dispute it), I’d love to know!
To get your current code working, you can use generics. This will actually kill two birds with one stone, as both your code will now compile and you'll benefit from the increased type safety that generics bring (by inferring the type you pass to them).
For example:
protocol A : class {}
protocol B : A {}
protocol AnotherProtocol{
associatedtype Type : A
weak var type : Type? {get set}
}
class SomeClass<T:B> : AnotherProtocol {
typealias Type = T
weak var type : Type?
}
Edit: It appears the above solution won't work in your particular case, as you want to avoid using concrete types. I'll leave it here in case it's useful for anyone else.
In your specific case, you may be able to use a type erasure in order to create a pseudo concrete type for your B protocol. Rob Napier has a great article about type erasures.
It's a bit of a weird solution in this case (as type erasures are normally used to wrap protocols with associatedtypes), and it's also definitely less preferred than the above solution, as you have to re-implement a 'proxy' method for each method in your A & B protocols – but it should work for you.
For example:
protocol A:class {
func doSomethingInA() -> String
}
protocol B : A {
func doSomethingInB(foo:Int)
func doSomethingElseInB(foo:Int)->Int
}
// a pseudo concrete type to wrap a class that conforms to B,
// by storing the methods that it implements.
class AnyB:B {
// proxy method storage
private let _doSomethingInA:(Void)->String
private let _doSomethingInB:(Int)->Void
private let _doSomethingElseInB:(Int)->Int
// initialise proxy methods
init<Base:B>(_ base:Base) {
_doSomethingInA = base.doSomethingInA
_doSomethingInB = base.doSomethingInB
_doSomethingElseInB = base.doSomethingElseInB
}
// implement the proxy methods
func doSomethingInA() -> String {return _doSomethingInA()}
func doSomethingInB(foo: Int) {_doSomethingInB(foo)}
func doSomethingElseInB(foo: Int) -> Int {return _doSomethingElseInB(foo)}
}
protocol AnotherProtocol{
associatedtype Type:A
weak var type : Type? {get set}
}
class SomeClass : AnotherProtocol {
typealias Type = AnyB
weak var type : Type?
}
class AType:B {
// implement the methods here..
}
class AnotherType:B {
// implement the methods here..
}
// your SomeClass instance
let c = SomeClass()
// set it to an AType instance
c.type = AnyB(AType())
// set it to an AnotherType instance
c.type = AnyB(AnotherType())
// call your methods like normal
c.type?.doSomethingInA()
c.type?.doSomethingInB(5)
c.type?.doSomethingElseInB(4)
You can now use the AnyB type in place of using the B protocol type, without making it any more type restrictive.

Class-only generic constraints in Swift

I'm trying to mark a variable of a generic type as weak:
class X<T> {
weak var t: T?
}
If I don't put in any constraints for T I get the error weak cannot be applied to non-class type 'T'.
If I would only use use this with NSObject derived classes this would work:
class X<T: NSObject> {
weak var t: T?
}
But I also want to be able to use pure Swift classes.
For protocols it is possible to require that the implementor is of class type by using the class keyword:
protocol ClassType: class {
}
With the ClassType protocol I can now mark the variable as weak:
class X<T: ClassType> {
weak var t: T?
}
But I cannot add the class keyword directly to the generic parameter:
class X<T: class> { // Compile error
weak var t: T?
}
I can make the protocol solution work for all NSObject derived classes with an extension:
extension NSObject: ClassType {
}
But for pure Swift classes there is no common superclass that I could add this extension to. Is there a way to make this work without adding the ClassType protocol to each class I want to use the X class with? E.g. some special qualifier for the generic parameter like class X<T:ThisIsAClass>?
You want AnyObject, which the Swift docs describe as:
The protocol to which all classes implicitly conform.
class X<T: AnyObject> {
weak var t: T?
}
class C { }
let x = X<C>() // works fine
x.t = C()
// error: type 'Int' does not conform to protocol ‘AnyObject’
// (because Int is a struct)
let y = X<Int>()

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