0

I have created my own property wrapper for the theming of UI components like UIView, UILabel etc.

 class MyUIViewController: UIViewController {
    @Theme private override var view: UIView! // it doesnt work!!!
    @Theme private var myCustomView: UIView! // it works!!
 }

in this case, i will get a compile error "Cannot override with a stored property 'view'"

I know that the view is a property of UIViewController. Do you know if there is any possible way to apply the property wrapper to a stored(superclass) property? any suggestions would be appreciated :) thanks a lot!

justicepenny
  • 2,004
  • 2
  • 24
  • 48
  • `MyUIViewController` inherits from `UIViewController` which doesn't declare `myCustomView` property, so you should get an error on `myCustomView` too (something like "does not override any property from superclass") and your code shouldn't works. – rraphael Apr 22 '20 at 08:52
  • sorry typo, of coz it is not my real code. :) – justicepenny Apr 22 '20 at 09:07

1 Answers1

0

I found a way to do that but it's more like a hack than a good implementation (so I would not recommend it), and I haven't fully tested it (as it really on the UIViewController view loading mechanism, this can lead to some undefined behavior).

That said, in the property wrapper documentation you can find a "translation example" that explains how property wrapper works.

@Lazy var foo = 1738

// translates to:

private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
  get { return _foo.wrappedValue }
  set { _foo.wrappedValue = newValue }
}

So we can imitate this to manually wrap a superclass property.

Note that doing this on the view property is a bit special as the view is not loaded during the view controller initialization, but more like a lazy var.

@propertyWrapper
struct Theme<WrappedValue: UIView> {
    var wrappedValue: WrappedValue?
}

class Controller: UIViewController {
    override func loadView() {
        super.loadView()
        _view.wrappedValue = view
    }

    private var _view: Theme<UIView> = .init()
    override var view: UIView! {
        get {
            if _view.wrappedValue == nil {
                // This is a trick I would not recommend using, but basically this line
                // forces the UIViewController to load its view and trigger the
                // loadView() method.
                _ = super.view
            }
            return _view.wrappedValue
        }
        set {
            _view.wrappedValue = newValue
        }
    }
}

I made the wrapped value in the property wrapper optional because the view property is nil during the initialization process (as the view is not yet loaded)

rraphael
  • 10,041
  • 2
  • 25
  • 33