When you start researching some vast topics in programming, the number of downloaded projects and created drafts gradually exceeds all conceivable and inconceivable limits. And then everything gets mixed up and lost. I seem to remember that you worked with it, but where, when? I tried to work in Playgrounds, but they, in my opinion, are not as stable as a regular project, the highlighting falls off, there is no way to do Debug normally. Recently, I started a single project for researching SwiftUI, and I throw all small things there. This helps to keep everything in one place, besides, searching within the project is much more convenient. While SwiftUI provides Previews for quick viewing of the View and even allows you to debug them, it is still not always enough. I would also like to check on the device. And if you keep all these Views inside one project,
SceneDelegate, which quickly gets tired. How cool it would be if we could see all of our test Views when we launch the application and be able to choose what to work with. Fantastic, you say? Not at all 🙂
The problem, I think, can be solved in more than one way. Offhand — to screw Sourcery, but it was interesting to solve it without auxiliary tools.
So, that is View and its Preview:
As we can see, the preview for the View is provided by the framework that implements
PreviewProvider. If anyone did not know, you can even create as many structures / classes as you like inside one file that will be implemented
PreviewProvider, and all of them will be displayed in the preview area. It may come in handy if we want to split ours
ContentView_Previewsinto several with different settings (although you can do the same inside one structure that implements
PreviewProvider, but this is not about that).
What is it
PreviewProvider? This is the protocol:
The main thing that can be learned from the code is not a simple protocol, but PAT (Protocol with Associated Type), which immediately complicates the matter. I have tried many options to provide the functionality I want with minimal effort.
Let’s start with how you can do such things in real-time? In Objective-C, we could do anything with reflection — get a list of all classes, examine their properties. In Swift, this whole thing is severely limited, and Mirror will not give us everything we need. Therefore, I had to look away
objc_getClassList: this is a method from the Obj-C runtime that allows you to get a list of all classes. Unfortunately, this is not the case for Swift structures, so all that remained was to get by with what was given.
Let’s analyze the solution in parts.
PreviewProviderwill not work normally with the system protocol due to the fact that it is PAT, so we will create an Erase version of this protocol:
- As you can see, I erased the type of y
Previews, creating a wrapper
anyPreviewsthat will return
AnyView. I don't really like these things, the potential loss of performance, but since this is not a production code, you can close your eyes to it.
name- a property that returns the name of our View, how it will be displayed in the list; given that all Previews have automatically generated names
ViewName_Previews, you can
_Previewscut it off.
- I added a property
starredbecause the number of Views will continue to increase, and starting to work with a new piece of code, I would like to see it at the top of the list. This can be done by overriding this property in the preview for the new View, returning
The list itself looks pretty simple.
All Views are sorted by name and split into 2 lists, starred and regular. Everything will look something like this:
SceneDelegatejust change the main view:
let contentView = PreviewsList()
There is one last point left: how to make our Previews appear on this list.
- add support
You can optionally override
This solution was written in a couple of hours to give you a quick test of the idea. If you wish, you can twist it in full, putting down tags, creation date for Preview, show the list not in the main one, but in the Debug window, which will allow you to use it even on a production project (do not forget to disable it in the Release build). In general, it all depends on your imagination 🙂
You can download the project with the basic implementation here: Github repo
If you found this article helpful, click the💚 or 👏 button below or share the article on Facebook so your friends can benefit from it too.