The missing SwiftUI packages
So I’ve been working with SwiftUI for over a year now and using it in production apps. I even built an entire app (iOS, macOS and iPadOS) in SwiftUI 2.
However those other projects were mostly targeted for iOS 13 and so, as I was using newer features I found I was missing them in iOS 13. So, I decided to back-port a few of them myself.
In addition, I sometimes found entire features or components we had in UIKit simply didn’t exist. Plus along the way I even came up with a few neat ideas myself.
The result, the SwiftUI+ Collection.
A Swift Package Collection that contains a dozen or so tiny packages (some contain multiple ‘features’ themselves) that you can pick and choose and include in your own projects. Almost all are supported from iOS 13+ and many support more than just iOS as well.
The collection is still growing as I’m moving things into it slowly to ensure I only include the best of the best, fully documented and production ready. As of September 2021 the following features are now available through the included packages.
Back-ports
SwiftUI 2 was a fairly major update and enabled me to write an entire app for over a year in just SwiftUI. To enable similar results in iOS 13 I had to back-port several features that I found particularly useful and unfortunately missing.
- DefaultStorage – AppStorage
- Refreshable – iOS 15 but back ported to iOS 13+
- ScaledMetric
Property Wrappers
The following property wrappers (just like the back-ports above) are all dynamically updating and support animations so they’re good SwiftUI citizens and behave just as you’d expect.
- Media – contains various wrappers that make it easy to fetch and observe changes to the user’s photo library
- Contacts – full support for containers, groups and contacts and external changes are reflected automatically
Views & Containers
Another great thing about SwiftUI is the layout system. Its so much more expressive and powerful IMO than auto layout constraints. But again, there were newer features I wanted to access and also some neat ideas I had that I felt were more universally useful, so I’m releasing those as well. All provided in a package, Containers
- ScrollableView – wraps a
ScrollView
but adds support forcontentMode
, see the Demo app for an example - FittingGeometryReader – a
GeometryReader
that automatically sizes itself to hug its content while still providing a proxy containing the geometry - PageView – wraps the pure SwiftUI implementation in iOS 14 but adds support for auto-sizing its height to match the content
- LayoutReader – A custom layout view that provides UIKit layout info like the
readableContentSize
- UIKitView – simplifies ad-hoc usage of a UIKit view using a custom
UIViewBuilder
closure - TextView – full API support through modifiers, closures for events (like text changes), placeholder support (including custom SwiftUI views) and supports both scrolling and auto-sizing layouts.
- WebView (coming soon)
UIKit Sheets
If you’ve implemented a UIActivityViewController
, MFMailComposeViewController
or any other UIKit based controller in SwiftUI before, you’ll know that it’s unfortunately trickier than you might think. The main issue (especially in iOS 13) is being limited to only a single sheet
modifier (see Presentation below for this solution) that doesn’t allow for fullscreen support. But other things go wrong as well. For example the ‘Share’ sheet, when presented correctly, presents a kind of half-sheet by default and of course is presented as a popover on iPad. If you try and support this in a more obvious fashion, you’ll quickly see some odd layout issues.
The solution is a little more complex and warrants its own article, but suffice to say, I have it solved for you. Currently I’m providing wrappers for the following controller’s and they all present perfectly across all versions of iOS and iPadOS.
- ActivityView – share sheet
- MailView
- ImagePickerView – included in the
Media
package, via a modifier - CameraView – included in the
Media
package, via a modifier - SafariView (coming soon)
Presentation
This is a package all its own, however it deserves a special mention because its something I’ve seen time and time again on StackOverFlow and other sites. Its not a simple challenge and took me a while to solve without any hacks, but here it is.
It utilises a custom present
modifier and associated presentation
environment value with an identical API. However the modifier has additional arguments, providing access to features we’re more accustomed to.
For example:
- ModalPresentationStyle
- ModalTransitionStyle
- isModalInPresentation
Best of all, its fully supported all the way back to iOS 13 😬
Summary
I have a lot more still to come, and I plan on writing more articles about the process and how I’ve come to solve many of the problems facing SwiftUI developers especially if you need to support iOS 13+.
I hope you find these particularly useful and I look forward to contributions (if they make sense) both to the individual packages as well as the larger collection itself.
Checkout the code on Github or grab the collection and add it to Xcode 13 now.