Passing object conforming to AnyObject protocol into generic requiring AnyObject - swift

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.

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

Is it possible to have a property containing generic protocol in Swift?

Let's say I have a protocol
protocol MyProtocol {
associatedtype DoSmthValue
func doSmth(_ value: DoSmthValue) -> String
}
I also have another protocol:
protocol AnotherProtocol {
associatedtype T: MyProtocol
var items: [T] { get set }
}
and finally my class where I want to use it:
class MyClass: AnotherProtocol {
typealias T = MyProtocol // this code returns an error
var items = [T]
}
But this code returns me MyProtocol can only be used as a generic constraint because it has Self or associated type requirements which is fair enough. I would like to use it like typealias T = MyProtocol<String> which is not allowed in Swift. So is there a way?
AnotherProtocol has an associated value which must be some type that conforms to MyProtocol. A protocol cannot conform to protocols, including itself, so that type cannot be MyProtocol; it has to be a type conforming to MyProtocol.
var items, then, has to be an array of whatever that type is.
So, let's say you have a type conforming to MyProtocol which defines the associated type to be String:
struct Foo: MyProtocol {
func doSmth(_ value: String) -> String {
value
}
}
Then you can define MyClass as:
class MyClass: AnotherProtocol {
var items: [Foo]
init(_ items: [Foo]) { self.items = items }
}
Of course, if you don't want it to be specific to Foo, you can make it generic with respect to any type T that conforms to MyProtocol and has an associated type of String:
class MyClass<T: MyProtocol>: AnotherProtocol where T.DoSmthValue == String {
var items: [T]
init(_ items: [T]) { self.items = items }
}
let myClass = MyClass([Foo(), Foo(), Foo()])
New Dev's answer is exactly right about how to fix this, but it's also worth realizing why this set of protocols is requiring something that doesn't make sense.
Let's pretend your MyClass definition were legal. I could then write the following code:
for item in items {
// item is of pseudo-type "MyProtocol"
// (that's illegal, but we're pretending it's not)
item.doSmth(... what would go here? ...)
}
As written, DoSmthValue could require literally any type, and you'd have to provide it. But you don't know what it is (and you can't know what it is). There's no way to make use of this protocol; you can't generate the things it requires. So it's not meaningful for item to be of type MyProtocol. You need to go back to thinking about what you want the calling code to look like, and that will tell you what kinds of protocols you need. Starting with the protocols (especially if you're new to this) will typically get you in trouble.

AnyObject and generics in 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())

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>