Leveraging SwiftUI for any app extension
by Thomas Durand
Since 2020, Widgets fully embrace SwiftUI by being a system extension fully based on them.
Building SwiftUI views instead of UIKit views is advantageous, including:
- Declarative syntax is easier than dealing with old school constraints
- Less code for equivalent user interfaces
- Live Preview to iterate faster over the development life cycle
Because Padlok consisted on a lot of SwiftUI views, and also to leverage the advantages cited above, I wanted to implement a Notification Content Extension using mostly exclusively SwiftUI views. But this method also work for any system extension that present views, including:
- Custom Keyboard Extension
- iMessage Extension
- Notification Content Extension
- Share Extension
And any other extension…
Padlok Notification built in SwiftUI
Containing UIHostingController with constraints
As we cannot use
UIHostingController directly because most of root controller are already some subclasses of
UIViewController. Instead we are going to use a child view controller
Clear background color
In some extensions like in Keyboard extensions, you might wanna keep the background clear so you get the neat gray used by default for keyboards.
But it turns out
UIHostingController is adding a white background by default. Luckily it’s easy enough to remove:
Somehow, I couldn’t auto-wire tint color in my extensions, but it’s easy enough to do by being able to access the Color asset:
Notification Content Extension: Self sizing issue
Notification Content extension expect us to size ourself using
It’s easy to get the minimal size of a SwiftUI view using UIKit APIs on the UIHostingController itself in the
didSet method for the contained attribute:
Specifically for Padlok, I found that adding a 8pt top padding worked well:
The case of live Previews
Sadly, almost every extensions will forbid you to preview directly in the extension target
This sadly have no solution, beside having your views included in your App Target as well, and using this target when previewing your views.
In Padlok, my shared views, and my extension views are in a Local Package that support Previews. The extension then consist only in the small bridge between UIKit and SwiftUI, and a little extension logic.
And the view is imported from this package, allowing me to leverage the whole Live Preview experience for my extensions.