Using Custom Symbols in iOS 18's Control Center Widgets

One of the headline features of iOS 18 is the ability for apps to create their own Control Center widgets.

Without getting into the specifics of creating a Control Center widget, this article talks about using custom symbols in those widgets.


In your ControlWidget implementation, you define the image and text using SwiftUI's Label. This allows you to use a system SF symbol as follows:

ControlWidgetButton(
    action: YourAppIntent()
) {
    Label("Do Something", systemImage: "checkmark")
}

With SF symbols, you can also create custom symbols, and use them in your Control Center widgets as follows:

ControlWidgetButton(
    action: YourAppIntent()
) {
    Label("Do Something Else", symbol: "my_custom_symbol")
}

In this instance, the asset catalog containing my_custom_symbol must be a part of the app's widgets extension.

Unfortunately, it isn't possible to non-symbol images, or images generated at run-time: the custom symbols must be embedded into your app.


In Streaks, we have a large icon library of over 750 icons (and always expanding!). We've spent a lot of time over the years on our icon processing pipeline, and before implementing widgets we were already using SVG / vector images in the app.

Obviously though, to convert this many icons into custom SF symbols is somewhat infeasible, so we sought to automate this.

To achieve this, we're using SwiftDraw, which has a specific option SF symbols:

swiftdraw my_icon.svg \
    --format sfsymbol \
    --output ./path/to/my_custom_symbol.svg
How symbols appear in an Xcode asset catalog.

Our updated icon processing pipeline also involves importing this into the symbols asset catalog. To achieve this, a .symbolset directory must be created, and within it the SVG file and a corresponding Contents.json file:

{
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "symbol-rendering-intent" : "template"
  },
  "symbols" : [
    {
      "filename" : "symbol_blender.svg",
      "idiom" : "universal"
    }
  ]
}

An asset catalog entry needs a Contents.json file.

And now, with the symbols in place in the app, when the user adds a control centre widget, it can use one of the many icons from Streaks:

How various tasks from Streaks can be represented in the Control Center.

Other notes from this implementation:

  • Converting from using SVG / vector images to custom symbols makes the built app smaller! If you use an SVG, the finals Assets.car will also includes a rasterized PNG of the SVG. When using custom symbols, this PNG isn't included.
  • In Streaks we allow users to flip/mirror/rotate icons. We have had to forego showing these transformations in control centre widgets, as we'd need to bake in every combination into the catalog.