Skip to content

Repository files navigation

BuildCodeCovPlatformsSwift 6.0

Introduction

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.

Usage

SwiftUI

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
Spider SVG rendered using original modeSpider SVG rendered with a blue-to-purple gradient using template mode

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:

varimage:SVGvarbody:someView{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 inswitch phase {case.empty:ProgressView()case.success(let svg):SVGView(svg: svg)case.failure:Image(systemName:"exclamationmark.triangle")}}

Loading and Transforming SVGs

SVG images can be loaded and rasterized to UIImage or NSImage:

letsvg=SVG(named:"sample.svg", in:.main)!
imageView.image = svg.rasterize()

Transformations can be added before rasterizing:

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

UIKit

Create a UIImage directly from an SVG within a bundle, Data or file URL:

import SwiftDraw
letimage=UIImage(svgNamed:"sample.svg")

AppKit

Create an NSImage directly from an SVG within a bundle, Data or file URL:

import SwiftDraw
letimage=NSImage(svgNamed:"sample.svg")

Command line tool

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 pdf

Resolving relative dimensions

When 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 800x600

If the SVG already declares a viewBox, missing width/height fall back to its dimensions automatically and --size is not required.

Installation

You can install the swiftdraw command-line tool on macOS using Homebrew. Assuming you already have Homebrew installed, just type:

$ brew install swiftdraw

To update to the latest version once installed:

$ brew upgrade swiftdraw

Alternatively download the latest command line tool here.

SF Symbol

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

Alignment

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,30

Insets 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,30

Variants can also be aligned using --ultralightInsets and --blackInsets.

Auto-generated weight variants

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.0

Notes:

  • An explicit --ultralight or --black SVG always wins over the matching --*-stroke-width flag. A warning is printed if both are supplied.
  • The scaler walks element attributes, inline styles, and <style> rules. If the regular SVG has no stroke-width values to scale, a warning is printed and the regular paths are reused unchanged.
  • Only stroke-width is scaled today. Future work could add scaling for related attributes (for example, dash arrays).

Swift Code Generation

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"?>
<svgversion="1.1"xmlns="http://www.w3.org/2000/svg"width="160"height="160">
<rectwidth="160"height="160"fill="snow" />
<pathd="m 80 30 a 50 50 0 1 0 50 50 h -50 z"fill="pink"stroke="black"stroke-width="2"/>
</svg>
structSimpleView:View{varbody:someView{if isResizable {
canvas
.frame(idealWidth:160.0, idealHeight:160.0)}else{
canvas
.frame(width:160.0, height:160.0)}}privatevarisResizable=falsefunc resizable()->Self{varcopy=self copy.isResizable =truereturn copy
}varcanvas:someView{Canvas(
opaque:false,
colorMode:.linear,
rendersAsynchronously:false){ context, size inletscale=CGSize(width: size.width /160.0, height: size.height /160.0) context.withCGContext{ ctx in
ctx.scaleBy(x: scale.width, y: scale.height)letrgb=CGColorSpaceCreateDeviceRGB()letcolor1=CGColor(colorSpace: rgb, components:[1,0.98,0.98,1])!
ctx.setFillColor(color1)
ctx.fill(CGRect(x:0, y:0, width:160, height:160))letcolor2=CGColor(colorSpace: rgb, components:[1,0.753,0.796,1])!
ctx.setFillColor(color2)letpath=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)letcolor3=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.

Credits

SwiftDraw is primarily the work of Simon Whitty.

(Full list of contributors)

About

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

Topics

Resources

Stars

643 stars

Watchers

11 watching

Forks

Releases

Packages

Used by

Contributors

Languages