How does this work?

In order to generate an icon, we have to essentially render a SwiftUI View into a hosting view, resize the view, create bitmaps for each of those view sizes and insert those into an AppIcon.appiconset inside the app’s Assets.xcassets . One of the benefits of using SwiftUI will be that we can render the View at different sizes first and generate a new bitmap from each of those instead of just scaling a bitmap. Yay, vectors! SwiftUI even does some tricks under-the-hood to pixel fit your shapes or text to whatever pixel size you are representing, so the bitmaps at smaller scales should look as good as possible.

Then, to render these bitmaps as a build step, we can use AppKit’s implementation of SwiftUI (in Catalina only) to render the Views from the command-line using a Swift script that ‘imports’ your Icon. So in your iOS target, you will have an Icon.swift that is shared between the Run Script phase and the iOS target, but all the other helper code will just be used by the Run Script phase.

Generating the Icon during Builds

The main generate.swift script that will run in your build is fairly simple and can check the build environment for TARGETED_DEVICE_FAMILY in order to generate only the necessary icons:

Then you’ll need to actually execute the script as a Build Phase (see Step 3 of Quick Setup above), which is made slightly more complicated by the fact that we need to smush together several swift files in order to run this as one ‘script’.

Now when you re-build, you get your new icon set automatically generated and based on whatever device families (iPad or iPhone) you have selected in your project. While not currently supported here, it wouldn’t be terribly difficult to add support for macOS, watchOS, tvOS, etc. Reach out if you do!

The end-result should now be a fully generated icon using SwiftUI drawing primitives. Now you can even embed Icon() into your app’s UI alongside all your other SwiftUI views!

Next Steps

This is a far from perfect setup. In fact, I’d go as far as to say it’s a bit of a hack. Ideally we’d be able to include this script as easily as a dependency, but because the icon has to be both included in your app and compiled on the build system, we have to jump through some hoops.

We’ve been using this in an upcoming product and are loving the flexibility and ease of expressing your app’s icon as a just another view.

We’re always open to feedback, so feel free to reach out if you have some ideas!