How to use class protocol as a generic? [duplicate] - swift

I'd like to have a generic weak reference to an object and parametrize it by a protocol that is class-bound.
Here is the code example that does not work in my case:
protocol Protocol: class { // also written as `protocol Protocol: AnyObject {`
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: AnyObject {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak<Protocol>(property: a) // error: 'Weak' requires that 'Protocol' be a class type
I get an error on last line: 'Weak' requires that 'Protocol' be a class type.
As Protocol will always be of class type (which is the same as AnyObject) shouldn't that be allowed by the compiler?
Is it possible to resolve that issue with swift 4?
If not, is it a limitation that can be resolved in a future version of swift or is it something impossible that the type system can not allow to happen?
A not accepted solution is to use #objc to the protocol declaration as in:
#obc protocol Protocol: class {
func function()
}
as this leads to limitations.

You are saying:
final class Weak<Type> where Type: AnyObject {
So you yourself are requiring that Type be a class (because that is exactly what AnyObject means).
A protocol is not a class. It is a protocol.

This is currently impossible because protocols do not conform to themselves. A protocol is not a type; it is a recipe for a type. As the simplest example of why this is hard, consider this situation:
protocol X {
init()
}
func f<T: X>(type: T.Type) -> T {
return type.init()
}
f(type: X.self)
This would create a value of type X, but would that be? This isn't allowed, and the mechanism that prevents it is that protocols do not conform to themselves. This crops up in a lots of other ways, and your example is one of them.
Some of this will be addressed when more of the Generics Manifesto is implemented, particularly the section Generalized Existentials, but currently what you want isn't possible. You can't have a "weak reference to some unknown thing." You need to know what that thing is.
The standard solution to this problem is a box, not a protocol. See Weak Arrays for an example.

If I understand your problem I design this solution
First I created a protocol called WeakProtocol, this will be our base of protocols
protocol WeakProtocol : AnyObject {}
After change your protocol to extends this new WeakProtocol
protocol Protocol: WeakProtocol {
func function()
}
after your Weak class you need apply a WeakProtocol type example
final class Weak<Type> where Type: WeakProtocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
Now to implement you don't need say it Protocol because the constructor receive the protocol.
Complete code is
protocol WeakProtocol : AnyObject {}
protocol Protocol: WeakProtocol {
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: WeakProtocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak(property: a)
You can too only change Protocol to extends AnyObject and Weak class to Type:Protocol, like this
protocol Protocol: AnyObject {
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: Protocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak(property: a)

Related

'Box' requires that 'CanFly' be a class type

Background
Let's consider the following working code
protocol CanFly { }
protocol Container {
associatedtype ContentType
var value: ContentType? { get }
}
class Box<V>: Container {
var value: V?
}
let box = Box<CanFly>()
box.value // <- has type `CanFly?`
Here Box accepts a protocol as generic type, beautiful isn't it?
Things get harder
Now I want to do a "small" change, I make the value property in Box weak 😱😱😱
class Box<V>: Container {
weak var value: V?
}
And of course I get this error
class Box<V>: Container {
weak var value: V? // 'weak' must not be applied to non-class-bound 'V'; consider adding a protocol conformance that has a class bound
}
I try to fix it constraining V to be an AnyObject 😏
class Box<V:AnyObject>: Container {
weak var value: V?
}
And I get this error 😨
let box = Box<CanFly>() // 'Box' requires that 'CanFly' be a class type
The problem
The problem here is that since I defined V:AnyObject I can no longer use the protocol CanFly as generic type of Box.
The question
I want
to be able to use a protocol as generic type of Box
AND the Box#value property to be weak
How can I do that?
Final considerations
I am looking for something like this
class Box<V:ProtocolForClassOnly>: Container {
weak var value: V?
}
P.S. The closest question on StackOverflow I could find is this one but unfortunately hasn't helped me.
So constrain your CanFly protocol to type AnyObject:
protocol CanFly: AnyObject { }
protocol Container {
associatedtype ContentType
var value: ContentType? { get }
}
class Box<V>: Container {
var value: V?
}
Then your code works:
func foo() {
let box = Box<CanFly>()
print(type(of:box.value))
}

swift protocol extension default implementation vs actual implementation in class

Consider the following code:
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
}
// does not specify its own name
class MyClass: MyProtocol {
}
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
}
let myClass = MyClass()
print("\(MyClass.name)")
//>>"unnamed"
let myClass2 = MyClass2()
print("\(MyClass2.name)")
//>>"Specific name"
Does swift guarantee that for classes (such as in this case MyClass2) that have an actual implementation of a protocol property, in this case "name", that this is used from the class, and not the one of the default "name" implementation via the protocol extension?
Having a default implementation for a required protocol function/property means that your conforming types won't have to implement that function/property, they can use the default implementation instead.
However, if a conforming type does implement the function/property, then the compiler will always call the more specific implementation, namely the one in your conforming class and not the default one.
So even if you stored an instance of MyClass2 in a variable of type MyProtocol, you'd still get the MyClass2 implementation when accessing the property on the variable.
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
The behaviour is different for non-required properties/functions declared and defined in a protocol extension. If you declare a property/function in the protocol extension only, then even if you provide a different implementation for that in a conforming class, you won't be able to access that implementation from a variable whose type is the protocol type rather than the specific conforming type.
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
// Optional protocol requirement
static var nonRequired: String {
return "nonRequired"
}
}
// does not specify its own name
class MyClass: MyProtocol { }
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
// Specific implementation
static var nonRequired: String {
return "Specific"
}
}
let myClass = MyClass()
MyClass.name
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
type(of: myClass2).nonRequired // "nonRequired"
MyClass2.nonRequired // "Specific"

Swift associatedType from protocol type - how to do that?

I'm having issues using associated type as protocol:
protocol Searchable{
func matches(text: String) -> Bool
}
protocol ArticleProtocol: Searchable {
var title: String {get set}
}
extension ArticleProtocol {
func matches(text: String) -> Bool {
return title.containsString(text)
}
}
struct FirstArticle: ArticleProtocol {
var title: String = ""
}
struct SecondArticle: ArticleProtocol {
var title: String = ""
}
protocol SearchResultsProtocol: class {
associatedtype T: Searchable
}
When I try to implement search results protocol, I get compile issue:
"type SearchArticles does not conform to protocol SearchResultsProtocol"
class SearchArticles: SearchResultsProtocol {
typealias T = ArticleProtocol
}
As I understand, it happens because T in SearchArticles class is not from concrete type (struct in that example), but from protocol type.
Is there a way to solve this issue?
Thanks in advance!
An associatedtype is not meant to be a placeholder (protocol), it is meant to be a concrete type. By adding a generic to the class declaration you can achieve the same result you want like this....
class SearchArticles<V: ArticleProtocol>: SearchResultsProtocol {
typealias T = V
}
Then, as you use SearchArticles around your app, you can declare let foo = SearchArticles<FirstArticle> or let foo = SearchArticles<SecondArticle>

How do you implement protocol methods that return covariant Selfs?

error: protocol 'Protocol' requirement 'instance' cannot be satisfied by a non-final class ('Class') because it uses 'Self' in a non-parameter, non-result type position
protocol Protocol {
var instance: Self {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Here is how I would express what I want, in C#. (C# does not, to my knowledge, have a way to enforce that the generic parameter "Self" is actually the Self we know from Swift, but it functions well enough as documentation that should make me do the right thing.)
interface Protocol<Self> where Self: Protocol<Self> {
Self instance {get;}
}
class Class: Protocol<Class> {
public Class instance {get {return new Subclass();}}
}
class Subclass: Class {}
…how that might look in a future version of Swift:
protocol Protocol {
typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf
var instance: FinalSelf {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
How I'm emulating the portion of that which is relevant to my problem:
protocol Protocol: ProtocolInstance {
static var instance: ProtocolInstance {get}
}
protocol ProtocolInstance {}
class Class: Protocol {
static var instance: ProtocolInstance {return Subclass()}
}
class Subclass: Class {}
And, here is what I believe to be the relevant portion of my code:
protocol Protocol {
static var 🎁: Self? {get} // an existing instance?
static var 🐥: Self {get} // a new instance
func instanceFunc()
}
extension Protocol {
static func staticFunc() {
(🎁 ?? 🐥).instanceFunc()
}
}
As it says, you can't do this, and for good reason. You can't prove you'll keep your promise. Consider this:
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
So x should be AnotherSubclass according to your protocol (that's Self). But it'll actually be Subclass, which is a completely different type. You can't resolve this paradox unless the class is final. This isn't a Swift limitation. This limitation would exist in any correct type system because it allows an type contradiction.
On the other hand, something you can do is promise that instance returns some consistent type across all subclasses (i.e. the superclass). You do that with an associated type:
protocol Protocol {
typealias InstanceType
var instance: InstanceType {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
Now x is unambiguously of type Class. (It also happens to be random other subclass, which is kind of weird, but that's what the code says.)
BTW, all of this usually suggests that you're using subclassing when you really shouldn't be. Composition and protocols would probably solve this problem better in Swift. Ask yourself if there's any reason that Subclass needs to actually be a subclass of Class. Could it be an independent type that conforms to the same protocol? All kinds of problems go away when you get rid of subclasses and focus on protocols.
I've been thinking about this more, and there may be a way to get what you're looking for. Rather than saying that all subclasses implement instance, attach instance as an extension. You can still override that if you want to return something else.
protocol Protocol {
init()
}
class Class: Protocol {
required init() {}
var instance: Class { return Subclass() }
}
extension Protocol {
var instance: Self { return self.dynamicType.init() }
}
class Subclass: Class {}
This dodges the inheritance problem (you can't create the same "AnotherClass returning the wrong type" this way).
This would actually make sense and work if you don't want to actually return Self for every subclass like this:
protocol Protocol : class {
typealias Sub : Self
var instance: Sub {get}
}
Which means that your protocol defines a typealias that has to be a subclass of itself. The following code would just work:
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Class().instance // Returns SubClass()
However the code above doesn't compile with the error
error: inheritance from non-protocol, non-class type '`Self`'
which I think is a bug, because Self is declared as a class type. You can however make it somehow work like this:
protocol Protocol : class {
typealias Sub : Class
var instance: Sub {get}
}
but then you don't have much of the protocol itself because only the class itself should ever conform to it.

Swift protocol forcing the Equatable protocol

I have define 2 protocols.
I need the first one (NameProtocol) to enforce the Equatable protocol.
While the other class (BuilderProtocol) have a method that return the first one (NameProtocol).
public protocol NameProtocol : Equatable {
var name: String { get }
}
public protocol BuilderProtocol {
func build() -> NameProtocol? // Compiler error
init()
}
The compiler error :
"Protocol 'NameProtocol' can only be used as a generic constraint because it has Self or associated type requirements"
I need the object return by build() to return an object conforming to the NameProtocol and on which I can define ==
Is there a way I can make this work?
Thanks
If using a typealias in BuilderProtocol how can I make the array declaration work?
public protocol OtherRelatedProtocol {
var allNames : Array<NameProtocol> { get }
}
Conclusion
I will remove the Equatable and implement an isEqual method.
public protocol NameProtocol {
func isEqual(nameable: NameProtocol) -> Bool
var name: String { get }
}
If you're familiar with Java or C#, Swift protocols are about halfway between generics and interfaces. One thing that you can do in a protocol, for instance, is this:
protocol Foo {
func compareWith(foo: Self)
}
Classes that implement this protocol will have a method compareWith that accept an object of their own type (and not an object of type Foo).
This is what the compiler calls "Self or associated type requirements", and this is how Equatable is defined (it needs an operator== that accepts two Self operands). The downside of these protocols, though, is that you can only use them as generic constrains: you can't use them as an expression type.
The solution is to use generics. In this case, you'd make your ProtocolBuilder protocol generic, with a constraint that the type implements NameProtocol.
public protocol NameProtocol : Equatable {
var name: String { get }
}
public protocol BuilderProtocol {
typealias T: NameProtocol
func build() -> T?
init()
}