12

I want to write a protocol with weak property requirement. Class that conforms it must be able to specify any type for this property. Also I don't want to specify an actual type, so it should be a type specified with some protocol. This code shows my idea for non-weak property:

protocol ObjectProtocol: class {
  typealias PropertyType
  var property: PropertyType {get set}
}

protocol FirstPropertyProtocol: class {}
protocol SecondPropertyProtocol: class {}

class FirstObjectImpl: ObjectProtocol {
  var property: FirstPropertyProtocol?
}

class SecondObjectImpl: ObjectProtocol {
  var property: SecondPropertyProtocol?
}

It works as expected.

I tried to do the same for weak property:

protocol ObjectProtocol: class {
  typealias WeakPropertyType: AnyObject //must be a class type
  weak var weakProperty: WeakPropertyType? {get set}
}

protocol WeakPropertyProtocol: class {}

class ObjectImpl: ObjectProtocol {
  weak var weakProperty: WeakPropertyProtocol?
}

And I got a compiler error:

Type 'ObjectImpl' does not conform to protocol 'ObjectProtocol'

Is there any way I can make this work?

Vlad
  • 7,199
  • 2
  • 25
  • 32
  • The problem is that a protocol does not conform to itself and not to any protocol that it inherits from, see http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself for a similar issue. In your case, the type WeakPropertyProtocol does *not* conform to AnyObject. – Martin R Mar 02 '16 at 19:26
  • Thanks for comment, Martin. Is there any other way to specify that my `WeakPropertyType` is a class type without this `AnyObject` constraint? – Vlad Mar 02 '16 at 22:52
  • Protocols can't currently require properties to be implemented as weak stored properties. https://stackoverflow.com/questions/47699813/weak-property-in-a-swift-protocol – Blazej SLEBODA Dec 08 '17 at 15:31

4 Answers4

5

I made it work with @objc attribute for WeakPropertyProtocol:

protocol ObjectProtocol: class {
  typealias WeakPropertyType: AnyObject //must be a class type
  weak var weakProperty: WeakPropertyType? {get set}
}

@objc protocol WeakPropertyProtocol {}

class SomeObjectImpl: ObjectProtocol {
  weak var weakProperty: WeakPropertyProtocol?
}

It's not a best solution because I concern about this note from apple doc

Note also that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes.

I can live with this restriction but I will appreciate any better solution.

Vlad
  • 7,199
  • 2
  • 25
  • 32
  • Compiler emits error: "'weak' cannot be applied to a property declaration in a protocol". Tested with Swift 5.7 (Xcode 14.0). – Blazej SLEBODA Oct 11 '22 at 21:07
4

I don't believe a protocol can enforce weak-ness. For example:

protocol ObjectProtocol: class {
  weak var weakProperty: AnyObject? {get set}
}

class ObjectImpl1: ObjectProtocol {
  weak var weakProperty: AnyObject?
}

class ObjectImpl2: ObjectProtocol {
  var weakProperty: AnyObject?
}

These both compile ok, even though the protocol has weak but ObjectImpl2 does not implement it.

EDIT: Is this what you're after?...

protocol ObjectProtocol: class {
  typealias WeakPropertyType: Any //must be a class type
  var weakProperty: WeakPropertyType? {get set}
}

protocol WeakPropertyProtocol: class {}

class ObjectImpl: ObjectProtocol {
  typealias WeakPropertyType = WeakPropertyProtocol
  weak var weakProperty: WeakPropertyProtocol?
}

This implementation requires use of Any rather than AnyObject, since WeakPropertyProtocol is a protocol rather than a class.

Or this?...

protocol WeakPropertyProtocol: class {}

protocol ObjectProtocol: class {
  typealias WeakPropertyType: AnyObject //must be a class type
  var weakProperty: WeakPropertyType? {get set}
}

class MyWeakClass: WeakPropertyProtocol {

}

class ObjectImpl: ObjectProtocol {
  typealias WeakPropertyType = MyWeakClass
  weak var weakProperty: MyWeakClass?
}

Either way, I think the key is in defining which class/protocol to use for WeakPropertyType.

Michael
  • 8,891
  • 3
  • 29
  • 42
  • It's interesting but I can't accept your answer. I don't really need to enforce weak-ness. But I need to be able to declare this property weak in class that conforms my protocol. Only property with class type can be weak and I can't find how to specify this without `AnyObject` constraint. – Vlad Mar 03 '16 at 06:26
  • I misunderstood your question. I will update my answer. – Michael Mar 03 '16 at 07:41
  • Thank you Michael! The second one is what I need. It works good but I am concerned that `weakProperty` in `ObjectProtocol` isn't specified with `weak` reference. Also I need to explicitly specify a type for `WeakPropertyType` alias in class implementation. Anyway your solution is best for me at the moment. – Vlad Mar 03 '16 at 08:10
  • I have been thinking more about your answer. I can't accept it because weak-ness of property isn't required in your solution. Yes, I already know that protocol can't enforce weak-ness for property but I like when code is self-documenting. So it's not an answer to my question about **weak** property requirement. – Vlad Mar 03 '16 at 09:04
2

As Swift 5.7, bundled with Xcode 14.0, the compiler emits the error: "'weak' cannot be applied to a property declaration in a protocol"

A possible protocol limitation workaround uses a computed variable and a supplementary 'weak' variable wrapped by the computed variable.

Workaround 1: This workaround doesn't force types that apply the protocol to set the property to weak

protocol SharedInformation {
  var name: UIViewController? { get set }
}

class Country: SharedInformation {

  private weak var _name: UIViewController?

  var name: UIViewController? {
    get {
      _name
    }
    set {
      _name = newValue
    }
  }
}

Workaround 2: This one forces a type that applies the protocol to have the property wrapped with weak storage.

protocol SharedInformation {
  var name: Weak<UIViewController> { get set }
}

class Country: SharedInformation {
  
  var name: Weak<UIViewController> = .init()
  
  func usage() {
    // set
    name.wrappedValue = UIViewController()
    // get
    let value = name.wrappedValue
  }
}

public struct Weak<Wrapped: AnyObject> {
  
  public weak var wrappedValue: Wrapped?
  
  public init(_ value: Wrapped? = nil) {
    self.wrappedValue = value
  }
}
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
-1

Swift 4 version.
I needed my view models to conform to a protocol. They mustn't retain a coordinator object:

protocol ViewModelType {
    associatedtype CoordinatorType: AnyObject
    weak var coordinator: CoordinatorType? { get }
}
SoftDesigner
  • 5,640
  • 3
  • 58
  • 47
  • 2
    Protocols can't currently require properties to be implemented as weak stored properties. https://stackoverflow.com/questions/47699813/weak-property-in-a-swift-protocol – Blazej SLEBODA Dec 08 '17 at 15:30