SwiftDraw displays SVG images natively in SwiftUI, UIKit and AppKit. It includes a pure-Swift SVG parser and renderer, plus a command-line tool for converting SVGs to PNG, PDF, custom SF Symbols and Swift source code.
SVGView works much like SwiftUI’s built-in Image view:
import SwiftDraw
SVGView("sample.svg")By default, the SVG is rendered at its intrinsic size. To make it flexible within layouts, mark it as resizable:
SVGView("sample.svg")
.resizable()
.scaledToFit()SVGView works just like Image:
.scaledToFit()to scale proportionally so the SVG fits inside its container..scaledToFill()to fill the entire container, cropping if necessary..resizable(resizingMode: .tile)to repeat the SVG as tiles across the available space.
SVGs can also be rendered as templates, replacing every non-transparent pixel with the current foreground style:
SVGView("spider.svg")
.renderingMode(.template)
.foregroundStyle(
LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
).original |
.template |
|---|---|
![]() |
![]() |
When loading by name, SVGView maintains an internal cache for efficient repeated lookups.
For more predictable performance (avoiding cache lookups or parsing), you can pass in an already-constructed SVG instance:
var image: SVG
var body: some View {
SVGView(svg: image)
}Remote SVGs can be loaded using AsyncSVGView:
AsyncSVGView(url: URL(string: "https://example.com/image.svg"))
.resizable()
.scaledToFit()For custom loading and error states, use the phase-based initializer:
AsyncSVGView(url: url) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let svg):
SVGView(svg: svg)
case .failure:
Image(systemName: "exclamationmark.triangle")
}
}SVG images can be loaded and rasterized to UIImage or NSImage:
let svg = SVG(named: "sample.svg", in: .main)!
imageView.image = svg.rasterize()Transformations can be added before rasterizing:
let svg = SVG(named: "fish.svg")! // 100x100
.expanded(left: 10, right: 10) // 120x100
.scaled(2) // 240x200
imageView.image = svg.rasterize() // 240x200Create a UIImage directly from an SVG within a bundle, Data or file URL:
import SwiftDraw
let image = UIImage(svgNamed: "sample.svg")Create an NSImage directly from an SVG within a bundle, Data or file URL:
import SwiftDraw
let image = NSImage(svgNamed: "sample.svg")The command line tool converts SVGs to other formats: PNG, JPEG, SFSymbol and Swift source code.
copyright (c) 2026 Simon Whitty
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.
Available keys for --format swift:
--api api of generated code: swiftui | appkit | uikit
Available keys for --format sfsymbol:
--insets alignment of regular variant: top,left,bottom,right | auto
--size size category to generate: small, medium large. (default is small)
--ultralight svg file of ultralight variant
--ultralight-insets alignment of ultralight variant: top,left,bottom,right | auto
--ultralight-stroke-width auto-generate ultralight variant by scaling regular stroke-width: 0.5 | 50%
--black svg file of black variant
--black-insets alignment of black variant: top,left,bottom,right | auto
--black-stroke-width auto-generate black variant by scaling regular stroke-width: 2.0 | 200%
--legacy use the original, less precise alignment logic from earlier swiftdraw versions.
$ swiftdraw simple.svg --format png --scale 3x$ swiftdraw simple.svg --format pdfWhen the root <svg> uses percent values (width="100%") or omits width/height entirely, SwiftDraw cannot determine the canvas size from the file alone. In those cases provide an explicit canvas size with --size, which acts as the viewport that percent values resolve against and as a fallback when both width/height and viewBox are missing:
$ swiftdraw responsive.svg --format png --size 800x600If the SVG already declares a viewBox, missing width/height fall back to its dimensions automatically and --size is not required.
You can install the swiftdraw command-line tool on macOS using Homebrew. Assuming you already have Homebrew installed, just type:
$ brew install swiftdrawTo update to the latest version once installed:
$ brew upgrade swiftdrawAlternatively download the latest command line tool here.
Custom SF Symbols can be created from a single SVG. SwiftDraw expands strokes, winds paths using the non-zero rule and aligns elements generating a symbol that can be imported directly into Xcode.
$ swiftdraw key.svg --format sfsymbol
Optional variants --ultralight and --black can also be provided:
$ swiftdraw key.svg --format sfsymbol --ultralight key-ultralight.svg --black key-black.svg
By default, SwiftDraw automatically sizes and aligns the content to the template guides. The auto alignment insets are output by the tool:
$ swiftdraw simple.svg --format sfsymbol --insets auto
Alignment: --insets 30,30,30,30Insets can be provided in the form --insets top,left,bottom,right specifying a Double or auto for each edge:
$ swiftdraw simple.svg --format sfsymbol --insets 40,auto,40,auto
Alignment: --insets 40,30,40,30Variants can also be aligned using --ultralightInsets and --blackInsets.
When matching ultralight or black SVGs are not available, SwiftDraw can synthesize them from the regular SVG by scaling every stroke-width value before strokes are expanded. Both decimal and percent forms are accepted:
$ swiftdraw key.svg --format sfsymbol \
--ultralight-stroke-width 50% \
--black-stroke-width 2.0Notes:
- An explicit
--ultralightor--blackSVG always wins over the matching--*-stroke-widthflag. A warning is printed if both are supplied. - The scaler walks element attributes, inline styles, and
<style>rules. If the regular SVG has nostroke-widthvalues to scale, a warning is printed and the regular paths are reused unchanged. - Only
stroke-widthis scaled today. Future work could add scaling for related attributes (for example, dash arrays).
Swift source code can also be generated from an SVG using the tool:
$ swiftdraw simple.svg --format swift<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="160" height="160">
<rect width="160" height="160" fill="snow" />
<path d="m 80 30 a 50 50 0 1 0 50 50 h -50 z" fill="pink" stroke="black" stroke-width="2"/>
</svg>struct SimpleView: View {
var body: some View {
if isResizable {
canvas
.frame(idealWidth: 160.0, idealHeight: 160.0)
} else {
canvas
.frame(width: 160.0, height: 160.0)
}
}
private var isResizable = false
func resizable() -> Self {
var copy = self
copy.isResizable = true
return copy
}
var canvas: some View {
Canvas(
opaque: false,
colorMode: .linear,
rendersAsynchronously: false
) { context, size in
let scale = CGSize(width: size.width / 160.0, height: size.height / 160.0)
context.withCGContext { ctx in
ctx.scaleBy(x: scale.width, y: scale.height)
let rgb = CGColorSpaceCreateDeviceRGB()
let color1 = CGColor(colorSpace: rgb, components: [1, 0.98, 0.98, 1])!
ctx.setFillColor(color1)
ctx.fill(CGRect(x: 0, y: 0, width: 160, height: 160))
let color2 = CGColor(colorSpace: rgb, components: [1, 0.753, 0.796, 1])!
ctx.setFillColor(color2)
let path = CGMutablePath()
path.move(to: CGPoint(x: 80, y: 30))
path.addCurve(to: CGPoint(x: 30, y: 80),
control1: CGPoint(x: 52.39, y: 30),
control2: CGPoint(x: 30, y: 52.39))
path.addCurve(to: CGPoint(x: 80, y: 130),
control1: CGPoint(x: 30, y: 107.61),
control2: CGPoint(x: 52.39, y: 130))
path.addCurve(to: CGPoint(x: 130, y: 80),
control1: CGPoint(x: 107.61, y: 130),
control2: CGPoint(x: 130, y: 107.61))
path.addLine(to: CGPoint(x: 80, y: 80))
path.closeSubpath()
ctx.addPath(path)
ctx.fillPath()
ctx.setLineCap(.butt)
ctx.setLineJoin(.miter)
ctx.setLineWidth(2)
ctx.setMiterLimit(4)
let color3 = CGColor(colorSpace: rgb, components: [0, 0, 0, 1])!
ctx.setStrokeColor(color3)
ctx.addPath(path)
ctx.strokePath()
}
}
}
}Source code can be generated using www.whileloop.com/swiftdraw.
SwiftDraw is primarily the work of Simon Whitty.

