When working on large projects, development of UI components can easily consume too much time. SwiftUI tries to solve such issues by using Previews, but even Previews have their own problems. In this post I will go through some techniques that make UI development more effective in UIKit. However, they are applicable in SwiftUI as well.
Issue: UI development
If you have a large app with a lot of screens, creating UI for something that is displayed somewhere deep in the app, it is really annoying to always need to go through the whole app. And you have to repeat the whole process every time you change a line of code, which is very inefficient. So let’s say you are creating an UIView…how do you display it without running the whole app?
Well, you can divide your large app into smaller apps. This has several prerequisites, depending on how you divide your apps, at least some level of modularization is required – you need to be able to import your view in two (or more) apps and you cannot import an app target into another app target. Modularization itself is a great idea and you should use it anyway. However, by creating additional apps in your project you need to deal with various overheads – you need another app delegate, Info.plists, code signing if you ever want to run it on a device…I am not sure this is what a developer wants…
Live preview in Xcode Playground
To get a quick preview of your view in development, you can also use Xcode playgrounds. Using it is pretty similar to example apps but without the disadvantages. You just create a playground and place it into your workspace. You do not need an app delegate, you will just create a playground page for each view and assign it to the live preview. To be able to use your view in the playground you still need to have your view inside a framework module, you cannot import app targets into the playground. On the other hand you don’t need Info.plist, nor do you need code signing, which means you cannot run it on a device.
But speaking from experience using Xcode playgrounds in large projects does not have great performance and ends up with long indexing and occasional weird compilation issues. Also sometimes the view looks different in the playground compared to the real appearance in the app.
You can use SwiftUI Previews even for UIKit views, all you need is to conform it to UIViewRepresentable and that’s it. For me it has pretty much the same issues as live previews in Xcode Playground. With one additional problem – you need to conform your view to UIViewRepresentable even though you do not intend to use it in SwiftUI. I don’t like that very much.
For me the solution is snapshot testing. I call it snapshot driven development even though it is not really precise. When creating UI I usually start with creating its class and a test for it and as I iterate view’s code I just run that particular test again and again and record new snapshots. If you create a good helper function, you can cover differences in various trait collections, like iPad layout, dark mode appearance, dynamic type, etc. 🙂
This way a lot of cases will be covered with a single run of a test, which can be pretty fast (well, clean run is a bit longer) but you end up with a test that will reduce the risk of any unwanted UI changes and I think this is something a good developer always wants.