CLI tool
swhitty/SwiftDraw avatar
swhitty/SwiftDraw

SwiftDraw: an SVG parser and renderer that lives in Swift

Native SVG parser and renderer for SwiftUI, UIKit and AppKit, with tools for PNG, PDF, SF Symbols and Swift source.

654 stars74 forksSwiftZlib

At a glance

What is it?
SwiftDraw parses and renders SVG in pure Swift and displays the result natively in SwiftUI, UIKit and AppKit, with a command line tool that converts SVG to PNG, PDF, JPEG, SF Symbols and Swift source. The interesting engineering is in the rendering mode, the caching behaviour and the variant generation for custom SF Symbols.
Who is it for?
Adopt SwiftDraw if you are building an Apple platform app that needs SVG in the interface and would rather ship a Swift dependency than rasterise assets at build time or draw them by hand. Skip it if your SVGs rely on filter effects the renderer does not support, since the tool can only hide those elements, or if you need the same assets to look identical on Android or the web.
Can I use it commercially?
Yes. Zlib is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 59 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A pure-Swift SVG engine for all three Apple UI frameworks

SwiftDraw displays SVG images natively in SwiftUI, UIKit and AppKit, and it does that with a pure-Swift SVG parser and renderer rather than by wrapping a C library. The description also covers a command-line tool for converting SVGs to PNG, PDF, custom SF Symbols and Swift source code.

The pure-Swift part is the reason to care. Rendering SVG inside an app normally means shipping a WebView with an HTML wrapper, or rasterising every asset at build time and losing the ability to recolour or rescale them at runtime. A native renderer keeps the vector data in memory, so the artwork stays crisp at any size and can be tinted with the app's own styling.

The three framework integrations are not an afterthought either. SVGView is the SwiftUI entry point, UIImage(svgNamed:) creates a UIKit image directly, and NSImage(svgNamed:) does the same for AppKit, each from a bundle, from Data or from a file URL.

The project is distributed through Swift Package Manager, with a Package.swift at the root, and it also ships CocoaPods podspecs for the main library and for the DOM library. The repository topics name the scope precisely, including appkit, coregraphics, ios, macos, sfsymbol, svg, swiftui and uikit, and the badges show a Swift 6.0 package. The licence is Zlib, which is permissive and short, and there is no separate homepage beyond the repository and the documentation directory.

SVGView works like Image, including the modifiers

The SwiftUI API is deliberately familiar. The README says SVGView works much like SwiftUI's built-in Image view, and the basic usage is a single line:

swift
import SwiftDraw

SVGView("sample.svg")

By default the SVG is rendered at its intrinsic size, and marking it as resizable makes it flexible within a layout:

swift
SVGView("sample.svg")
    .resizable()
    .scaledToFit()

The modifiers that matter are the same ones Image has. scaledToFit scales proportionally so the SVG fits inside its container, scaledToFill fills the entire container and crops if necessary, and resizable(resizingMode: .tile) repeats the SVG as tiles across the available space.

The mode that is specific to SVG is the template rendering mode, which replaces every non-transparent pixel with the current foreground style, so one asset serves any tint the design system wants:

swift
SVGView("spider.svg")
    .renderingMode(.template)
    .foregroundStyle(
        LinearGradient(
            colors: [.blue, .purple],
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        )
    )

The caching behaviour is worth knowing. When loading by name, SVGView maintains an internal cache for efficient repeated lookups, and for more predictable performance, avoiding cache lookups or parsing, you can pass in an already-constructed SVG instance.

AsyncSVGView and the phase based initializer

Remote SVGs are loaded with AsyncSVGView, which takes a URL:

swift
AsyncSVGView(url: URL(string: "https://example.com/image.svg"))
    .resizable()
    .scaledToFit()

For custom loading and error states there is a phase-based initializer, and the phases are the standard SwiftUI shape you would expect, empty, success and failure:

swift
AsyncSVGView(url: url) { phase in
    switch phase {
    case .empty:
        ProgressView()
    case .success(let svg):
        SVGView(svg: svg)
    case .failure:
        Image(systemName: "exclamationmark.triangle")
    }
}

The design point is that the loading state is yours to render. A spinner, a retry button or a placeholder icon all come from your code, and the library hands you the parsed SVG in the success case so you can pass it straight to SVGView, which is the same path used when you construct the SVG yourself.

That pairing is consistent throughout the library. The synchronous API and the asynchronous API converge on the same SVGView(svg:) entry point, so an app can start from a bundle resource and later swap in a downloaded asset without changing how the image is displayed.

Rasterizing to UIImage and NSImage, with transforms applied first

SVG images can be loaded and rasterized to UIImage or NSImage, and the transform API is applied before the rasterization rather than after:

swift
let svg = SVG(named: "fish.svg")!   // 100x100
    .expanded(left: 10, right: 10)  // 120x100
    .scaled(2)                      // 240x200

imageView.image = svg.rasterize()   // 240x200

The comments in the example make the behaviour explicit, and it is the reason to care about the order. An expanded SVG has its canvas grown, and scaled multiplies the size, so the rasterized image reflects the transformed geometry rather than the original file.

The UIKit and AppKit entry points are one-liners that take the resource name and find it in a bundle, from Data or from a file URL:

swift
import SwiftDraw
let image = UIImage(svgNamed: "sample.svg")

and the same shape for NSImage. The naming symmetry with UIImage(named:) is deliberate, so code that loads a PNG by name needs only a different constructor.

For a team that already has a UIImage pipeline, this is the integration point: build the SVG, apply the transform, rasterize, and hand the result to the existing image view.

The command line tool converts to five kinds of output

The command line tool is a separate part of the project and does the batch work that an application would otherwise do at runtime. It converts SVGs to PNG, JPEG, SFSymbol and Swift source code, and the PDF format is in the usage line as well.

The help output shows the whole option surface:

code
usage: swiftdraw <file.svg> [--format png | pdf | jpeg | swift | sfsymbol] [--size wxh] [--scale 1x | 2x | 3x]

<file> svg file to be processed

Options:
 --format      format to output image: png | pdf | jpeg | swift | sfsymbol
 --size        size of output image: 100x200
 --scale       scale of output image: 1x | 2x | 3x
 --insets      crop inset of output image: top,left,bottom,right
 --precision   maximum number of decimal places
 --output      optional path of output file

 --hide-unsupported-filters   hide elements with unsupported filters.

The usage is short, for example:

bash
$ swiftdraw simple.svg --format png --scale 3x

Two option families are worth calling out. For Swift source output there is an --api key with the values swiftui, appkit or uikit, so the same SVG can be emitted as code for the framework you use. And the --hide-unsupported-filters option is an honest admission that the renderer does not implement every SVG filter, with a way to drop the affected elements rather than render them wrongly.

There is also a --legacy flag, described as using the original, less precise alignment logic from earlier swiftdraw versions, which tells you that the SF Symbol alignment behaviour has been rewritten at least once.

Percent dimensions need an explicit canvas size

One failure mode is called out in the README, and it is the one that catches people out.

When the root svg element uses percent values such as width="100%" or omits width and height entirely, SwiftDraw cannot determine the canvas size from the file alone. In those cases you provide an explicit canvas size with --size, which acts as the viewport that percent values resolve against and as a fallback when both width and height and the viewBox are missing:

bash
$ swiftdraw responsive.svg --format png --size 800x600

The fallback is described precisely too. If the SVG already declares a viewBox, missing width and height fall back to the viewBox dimensions automatically, and --size is not required.

That is a sensible rule. A viewBox is a real statement about the coordinate system and the intended aspect ratio, so it is a good source for a canvas size. Percent values are not, since they only mean something relative to a viewport that the file does not describe.

The same limitation applies to the in-process API, since the SVG type is what both the command line tool and the library use. A file that relies on percent dimensions needs an explicit size somewhere in the pipeline.

Installing the tool, and what the repository holds

The command line tool installs with Homebrew on macOS, and updating is the matching upgrade command:

bash
$ brew install swiftdraw
bash
$ brew upgrade swiftdraw

Alternatively the latest command line tool can be downloaded directly, as a zip named SwiftDraw.dmg.zip from the repository releases.

The repository layout shows the split between the library and the tool. There is a SwiftDraw/ directory for the main library, a CommandLine/ directory for the tool, a DOM/ directory with its own podspec, Documentation/ for the docs, Examples/ with a Basic Xcode project and a Samples.bundle/. At the root sit Package.swift, SwiftDraw.podspec.json, SwiftDrawDOM.podspec.json, codecov.yml, container-run-tests.sh and the LICENSE.txt.

The release notes in the tag names are a good summary of where the work has been going. Version 0.28.0 was Auto Generate SF Symbol Variants, 0.28.1 was a fix for support for em and ex units, and 0.29.0 was Template and AsyncSVGView, which lines up with the two features in this article.

The release dates are close together, 0.28.0 on 2026-07-03, 0.28.1 on 2026-07-15 and 0.29.0 on 2026-07-19, and the last push was on 2026-08-25. There are no GitHub releases beyond the tags, and the project is built and tested with a container script, container-run-tests.sh, alongside the build workflow badge and the coverage reporting.

Editorial conclusion

Adopt SwiftDraw if you are building an Apple platform app that needs SVG in the interface and would rather ship a Swift dependency than rasterise assets at build time or draw them by hand. Skip it if your SVGs rely on filter effects the renderer does not support, since the tool can only hide those elements, or if you need the same assets to look identical on Android or the web. Before you integrate, check your artwork against the unsupported filter behaviour and test a document with percent based dimensions, since those need an explicit canvas size. The last push was on 2026-08-25, the newest release is 0.29.0 from 2026-07-19, and the licence is Zlib.

Frequently asked questions

How do I display an SVG in SwiftUI with SwiftDraw?

Import SwiftDraw and use SVGView with the resource name, for example SVGView("sample.svg"). It renders at intrinsic size by default, and you can add .resizable() with .scaledToFit(), .scaledToFill() or the tile resizing mode.

How do I load a remote SVG with SwiftDraw?

Use AsyncSVGView with a URL. For custom loading and error states, the phase-based initializer gives you .empty, .success and .failure phases, and in the success case you can pass the parsed SVG to SVGView(svg:).

What happens when an SVG uses percent width and height?

SwiftDraw cannot determine the canvas size from the file alone in that case, so you pass an explicit size with --size, which acts as the viewport for percent values. If the SVG declares a viewBox, the viewBox dimensions are used automatically and --size is not needed.

What can the swiftdraw command line tool produce?

The tool converts SVG to PNG, PDF, JPEG, SF Symbols and Swift source code, with the --api key selecting swiftui, appkit or uikit for the generated Swift. It also offers --size, --scale, --insets, --precision and --output options, and a --hide-unsupported-filters flag for elements with filters the renderer does not support.

Official sources

  1. Issues
  2. License: Zlib
  3. README
  4. Releases
  5. swhitty/SwiftDraw on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/swhitty-swiftdraw.svg)](https://hysenlabs.com/projects/swhitty-swiftdraw)

Was this useful?