Using Xcode Previews for UIKit
This post is brought to you by Emerge Tools, the best way to build on mobile.
Look, I still UIKit. Like, a lot.
In the real world, the older things are, the less useful we perceive them. Nobody gets pumped about a decade old car. Or, good luck selling a twenty year old lamp on marketplace.
But in software, old APIs are gold. I love old APIs.
They’re battle tested, work under duress and even though the edge cases no doubt still exist, for the most part they just work like you think they should work. And so it is, as we find ourselves wading through the relatively new waters of SwiftUI, some are quick to point out that it doesn’t do this or it’s missing that.
Honestly, I could care less for those arguments. Because UIKit never went anywhere, and you can mix and match the two whenever you want.
But!
The place I don’t really see developers mention the two worlds colliding1 is through Xcode Previews. Just as you can use representables to wrap up trusty UIKit controls, so too can you preview them without much effort. This is true for both single UIView
subclasses you’re working on to entire UIViewController
instances too.
It’s funny to me, because I never thought to really make this connection when SwiftUI was announced, but it’s an obvious conclusion if you think about it objectively:
- I can use UIKit views and controllers in SwiftUI.
- I can live preview SwiftUI with Xcode Previews.
- So, I can just toss UIKit stuff into SwiftUI code to simply preview them.
And my goodness, it’s worlds faster to use a preview than it is to run your app, navigate to this button, tap that icon and do whatever tomfoolery you’ve got to do just to see your work presented.
In fact, I use this technique nine times out of ten when developing UIKit examples for my Best-in-Class series:
Literally, all the code you need is directly below this sentence – you can copy and paste this right into any project to use this technique:
For View Controllers:
#if canImport(SwiftUI) && DEBUG
import SwiftUI
struct UIViewControllerPreview<ViewController: UIViewController>: UIViewControllerRepresentable {
let viewController: ViewController
init(_ builder: @escaping () -> ViewController) {
viewController = builder()
}
// MARK: - UIViewControllerRepresentable
func makeUIViewController(context: Context) -> ViewController {
viewController
}
}
#endif
For Single Views:
#if canImport(SwiftUI) && DEBUG
import SwiftUI
struct UIViewPreview<View: UIView>: UIViewRepresentable {
let view: View
init(_ builder: @escaping () -> View) {
view = builder()
}
// MARK: UIViewRepresentable
func makeUIView(context: Context) -> UIView {
return view
}
func updateUIView(_ view: UIView, context: Context) {
view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
view.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
}
Then, simply pop that into any SwiftUI file that adopts PreviewProvider
:
struct BestInClassPreviews_Previews: PreviewProvider {
static var previews: some View {
UIViewControllerPreview {
// Return whatever controller you want to preview
let vc = MyViewControllerToTest()
return vc
}
}
}
And off you go! It’s fast too, changing properties and the like is shown quickly. In some cases, you might need to force a refresh of the canvas, or you can do it live by just tapping the “Play” icon in the previewing canvas (saving you a lot of navigation work, too!):
So, even if you aren’t traveling in the lands of SwiftUI yet for whatever reason, there’s no reason not to leverage Xcode Previews with UIKit. Get the maturity of UIKit and the new bells and whistles of SwiftUI, all in one go.
Until next time ✌️
-
To wit, I believe I’ve only seen an NSHipster article really touch on it, and that was back when SwiftUI initially launched. ↩