2

Here's the standard boilerplate weak container in Swift.

struct Weak<T: AnyObject> {
  weak var value: T?
  init(value: T) {
    self.value = value
  }  
}

It works well unless you want T to be a protocol, e.g.,

protocol ImplementationHiding: class {}
class Implementation: ImplementationHiding {}
let w = Weak(value: Implementation() as ImplementationHiding)

This does not compile, sadly. The only way I've found to get it work is to introduce @objc on the protocol:

@objc protocol ImplementationHiding {}

The only way I've found around this is pretty ugly, since it throws out compile-time safety.

struct Weak<T> {
  private let get: () -> T?
  init(value: AnyObject, type: T.Type = T.self) {
    get = { [weak value] in value as! T? }
  }
  var value: T? {
    return get()
  }
}

How can I create Weak with a native Swift protocol as T?

Hamish
  • 78,605
  • 19
  • 187
  • 280
Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
  • Some digging I just did shows me that this is likely not possible because of https://bugs.swift.org/browse/SR-55. – Gregory Higley Nov 22 '17 at 17:59
  • You could get back type safety by passing a closure: `init(_ value: U, upcast: @escaping (U) -> T) { self.get = { [weak value] in value.map { upcast($0) } } }` (then when calling just pass `{ $0 }` to upcast from `Implementation` to `ImplementationHiding`). Though annoyingly that'll require the concrete type to be known at the construction of the wrapper, and won't allow `value` to be settable by the outside world. – Hamish Nov 22 '17 at 18:16
  • But yes, the main problem here is that [protocols don't always conform to themselves](https://stackoverflow.com/a/43408193/2976878). – Hamish Nov 22 '17 at 18:17
  • Yes, that appears to be the essence of SR-55. – Gregory Higley Nov 22 '17 at 20:14

1 Answers1

2

If you are fine with losing some compile time safety, you can change your Weak to accept Any as T and then store value as AnyObject:

struct Weak<T: Any>{
    weak var value: AnyObject?

    public var ref: T? {
        get {
            return value as? T
        }
    }

    init(value: T) {
        self.value = value as AnyObject
    }
}
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • You don't need `T: Any`. Simple `T` will work. Other than that, this works well and is very similar to my solution, though I like yours better. Strictly speaking, this does not answer the question, but I think it's the closest we're going to get. – Gregory Higley Nov 22 '17 at 23:42
  • Yes, I left it for educational purpose... Maybe I should mention that. – Dalija Prasnikar Nov 23 '17 at 11:02
  • I know this does not exactly answers the question, and I have been agonizing over the same issue for a long time. I would really love to see correct answer to your question :) – Dalija Prasnikar Nov 23 '17 at 11:05