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)