I hadn’t really paid too close attention to Swift’s property wrapper features until my curiosity was piqued earlier this year by Apple’s introduction of some very interesting wrappers designed to work with UIViewController, NSViewController, and NSWindowController. The aim in each case is to address this vexing situation:
// Will be non-nil after loadView var important: Thingy! = nil
By wrapping in such a way that guarantees the view will get loaded if you access the property:
@ViewLoading var important: Thingy
Anybody who has done substantial work on iOS or Mac platforms can appreciate how helpful this is, to convert an otherwise fragile implicitly unwrapped optional into a totally predictable non-optional property. Apple’s new property wrappers make this possibly by essentially rewriting the “important” property with boiler-plate code that first loads the view if needed, and then returns the underlying wrapped value. As Apple says in their documentation, this stands to prove particularly useful for @IBOutlet properties, which can all be converted to using this mechanism.
Unfortunately, Apple’s new property wrappers are only available for pretty recent deployment targets: iOS 16.4 and macOS 13.3. I am excited to start using these, but I still need to deploy to earlier targets. So I set out to re-implement something similar on my own.
The magic in Apple’s wrappers depend upon an under-documented but widely used alternative mechanism for property wrappers: the ability to define getters and setters with knowledge of the containing object’s type, and access to the specific instance. The “subscript” variant of property wrappers is described in the original proposal, but has never been publicly endorsed, as far as I know, by Apple or the Swift team.
Usually I would shy away from undocumented features, but in this situation I believe it’s safe to use the undocumented subscript functionality because the result of doing so is to instruct the compiler to generate wrapper code that is compiled into the resulting binary. In other words: there are no private runtime dependencies that are being assumed by opting into this more sophisticated form of property wrapper. It’s just a question of getting the Swift compiler to write the pertinent code for us.
There are plenty of tutorials about how to use these subscript-based property wrappers, but I thought it would be useful to share a reference implementation that boils it down to how you might write one of these @ViewLoading style property wrappers yourself. MagicLoading is a nascent GitHub repository that aims to do that. It implements only one such wrapper: MagicViewLoading, which is intended to replicate UIViewController.ViewLoading.
For my own purposes, I’d like to add other wrappers to handle the NSWindowController and NSViewController cases, but I’m also a little annoyed by the prospect of having to replicate that ugly “subscript” code everywhere. I was curious about whether I could somehow factor out the subscript wrapper and allow other wrappers to be declared by just providing the “getter logic” and “setter logic”.
I made a few attempts at declaring a more generic property wrapper that exposed var properties for getting and setting, but I ran into issues figuring out how to declare concrete types that could take advantage of the more general wrapper without introducing complexity at the property declaration site. If anybody has clever ideas about how to approach that, I’d love to be able to have `@MagicLoading` be a property wrapper type that other wrappers could inherit from or compose in some way.