AnyObject and generics in Swift - swift

This code fails to compile with Swift 5.1
import Foundation
protocol SomeClassProtocol: AnyObject {}
class SomeClass: SomeClassProtocol {}
class GenericClass<T:AnyObject> {
weak var t: T?
init(t: T) {
self.t = t
}
}
let test = GenericClass<SomeClassProtocol>(t: SomeClass())
The error is
'GenericClass' requires that 'SomeClassProtocol' be a class type
Does the compiler really need a class type here instead of a class-only protocol?

Yes. Though the syntax is the same (a colon), protocol inheritance is not the same thing as protocol conformance. Protocols do not conform to protocols; only types can conform to protocols. (AnyObject is not special in this regard; your question is good but the title isn't getting at the issue.)
In your example:
Your T needs to conform to AnyObject. SomeClassProtocol does not conform to AnyObject. But any types that conform to SomeClassProtocol will conform to AnyObject.
So you need to pick which of these you really want:
1.
let test = GenericClass( t: SomeClass() )
(test is a GenericClass<SomeClass>.)
2.
class Class {
weak var object: AnyObject?
init(object: AnyObject) {
self.object = object
}
}
Class( object: SomeClass() )
You do have the option of subclassing, if that would be useful.
class GenericClass<T: AnyObject>: Class {
var t: T? {
get { object as? T }
set { object = newValue }
}
}

Does the compiler really need a class type here instead of a class-only protocol?
Yes, it does. I think the problem is just understanding what this means:
class GenericClass<T:AnyObject>
That means: "To resolve GenericClass, the parameterized type T must be some type that is a class." Examples would be UIView, NSString, etc.
Okay, so:
let test = GenericClass<SomeClassProtocol>(t: SomeClass())
So, SomeClassProtocol is none of those; it isn't the name of a class. It's the name of a protocol.
A further difficulty may be understanding protocols as types. They are not really full-fledged types.

You don't need to specify T explicitly.
Change your code from this:
let test = GenericClass<SomeClassProtocol>(t: SomeClass())
To this:
let test = GenericClass(t: SomeClass())

Related

Passing object conforming to AnyObject protocol into generic requiring AnyObject

Assuming this code:
protocol Test: AnyObject {}
class RealTest: Test {}
class Wrapper<A: AnyObject> {
weak var value: A?
init(_ value: A) {
self.value = value
}
}
let realTest = RealTest()
let wrapper = Wrapper(realTest)
the code above works and wrapper is created. However, when I change the realTest to:
let realTest: Test = RealTest()
I get the error message
Generic class 'Wrapper' requires that 'Test' be a class type
Is there a solution to this, as I need the AnyObject requirement and I only know the protocol the objects are conforming to?
Protocols do not conform to themselves (or to any protocol). Only concrete types can conform to a protocol.
If the only thing you need to know about this type is that it is a class (AnyObject), then you don't want a generic here, you just want to pass the protocol type itself (technically the "existential").
class Wrapper {
let value: AnyObject
init(_ value: AnyObject) {
self.value = value
}
}
If you need a generic here for some other reason, then the generic must be over a concrete type, not a 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.

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>()