SwiftGen
SwiftGen is a tool to automatically generate Swift code for resources of your projects (like images, localised strings, etc), to make them type-safe to use.
Then generate constants for: |
There are multiple benefits in using this:
- Avoid any risk of typo when using a String
- Free auto-completion
- Avoid the risk of using a non-existing asset name
- All this will be ensured by the compiler and thus avoid the risk of crashing at runtime.
Also, it’s fully customizable thanks to Stencil templates, so even if it comes with predefined templates, you can make your own to generate whatever code fits your needs and your guidelines!
Installation
There are multiple possibilities to install SwiftGen on your machine or in your project, depending on your preferences and needs:
Download the ZIP for the latest release
- Go to the GitHub page for the latest release
- Download the
swiftgen-x.y.z.zip
file associated with that release - Extract the content of the zip archive in your project directory
We recommend that you unarchive the ZIP inside your project directory and commit its content to git. This way, all coworkers will use the same version of SwiftGen for this project.
If you unarchived the ZIP file in a folder e.g. called swiftgen
at the root of your project directory, you can then invoke SwiftGen in your Script Build Phase using:
"${PROJECT_DIR}/swiftgen/bin/swiftgen" …
Via CocoaPods
If you’re using CocoaPods, simply add pod 'SwiftGen', '~> 6.0'
to your Podfile
.
Then execute pod install --repo-update
(or pod update SwiftGen
if you want to update an existing SwiftGen installation) to download and install the SwiftGen
binaries and dependencies in Pods/SwiftGen/bin/swiftgen
next to your project.
Given that you can specify an exact version for SwiftGen
in your Podfile
, this allows you to ensure all coworkers will use the same version of SwiftGen for this project.
You can then invoke SwiftGen in your Script Build Phase using:
if [[ -f "${PODS_ROOT}/SwiftGen/bin/swiftgen" ]]; then
"${PODS_ROOT}/SwiftGen/bin/swiftgen" …
else
echo "warning: SwiftGen is not installed. Run 'pod install --repo-update' to install it."
fi
Similarly, be sure to use
Pods/SwiftGen/bin/swiftgen
instead of justswiftgen
where we mention commands withswiftgen
in the rest of the documentation.
Note: SwiftGen isn’t really a pod, as it’s not a library your code will depend on at runtime; so the installation via CocoaPods is just a trick that installs the SwiftGen binaries in the Pods/ folder, but you won’t see any swift files in the Pods/SwiftGen group in your Xcode’s Pods.xcodeproj. That’s normal; the SwiftGen binary is still present in that folder in the Finder.
Via Homebrew (system-wide installation)
To install SwiftGen via Homebrew, simply use:
$ brew update
$ brew install swiftgen
This will install SwiftGen system-wide. The same version of SwiftGen will be used for all projects on that machine, and you should make sure all your coworkers have the same version of SwiftGen installed on their machine too.
You can then invoke swiftgen
directly in your Script Build Phase (as it will be in your $PATH
already):
swiftgen …
Via Mint (system-wide installation)
❗️
SwiftGen 6.0 or higher only.
To install SwiftGen via Mint, simply use:
$ mint install SwiftGen/SwiftGen
Compile from source (only recommended if you need features from the `stable` branch or want to test a PR)
This solution is when you want to build and install the latest version from stable
and have access to features which might not have been released yet.
- If you have
homebrew
installed, you can use the following command to build and install the latest commit:
brew install swiftgen --HEAD
- Alternatively, you can clone the repository and use
rake cli:install
to build the tool and install it from any branch, which could be useful to test SwiftGen in a fork or a Pull Request branch.
Some Ruby tools are used in the build process, and the system Ruby works well if you are running a recent macOS. However, if you are using rbenv
you can run rbenv install
to make sure you have a matching version of Ruby installed.
Then install the Ruby Gems:
# Install bundle if it isn't installed
gem install bundle
# Install the Ruby gems from Gemfile
bundle install
You can now install to the default locations (no parameter) or to custom locations:
# Binary is installed in `./build/swiftgen/bin`, frameworks in `./build/swiftgen/lib` and templates in `./build/swiftgen/templates`
$ rake cli:install
# - OR -
# Binary will be installed in `~/swiftgen/bin`, frameworks in `~/swiftgen/fmk` and templates in `~/swiftgen/tpl`
$ rake cli:install[~/swiftgen/bin,~/swiftgen/fmk,~/swiftgen/tpl]
You can then invoke SwiftGen using the path to the binary where you installed it:
~/swiftgen/bin/swiftgen …
Or add the path to the bin
folder to your $PATH
and invoke swiftgen
directly.
Known Installation Issues On macOS Before 10.14.4
Starting with SwiftGen 6.2.1, if you get an error similar to dyld: Symbol not found: _$s11SubSequenceSlTl
when running SwiftGen, you’ll need to install the Swift 5 Runtime Support for Command Line Tools.
Alternatively, you can:
- Update to macOS 10.14.4 or later
- Install Xcode 10.2 or later at
/Applications/Xcode.app
- Rebuild SwiftGen from source using Xcode 10.2 or later
Configuration File
❗️
If you’re migrating from older SwiftGen versions, don’t forget to read the Migration Guide.
SwiftGen is provided as a single command-line tool which uses a configuration file to define the various parsers to run (depending on the type of input files you need to parse) and their parameters.
To create a sample configuration file as a starting point to adapt to your needs, run swiftgen config init
.
Each parser described in the configuration file (strings
, fonts
, ib
, …) typically corresponds to a type of input resources to parse (strings files, IB files, Font files, JSON files, …), allowing you to generate constants for each types of those input files.
To use SwiftGen, simply create a swiftgen.yml
YAML file (either manually or using swiftgen config init
) then edit it to adapt to your project. The config file should list all the parsers to invoke, and for each parser, the list of inputs/outputs/templates/parameters to use for it.
For example:
strings:
inputs: Resources/Base.lproj
outputs:
- templateName: structured-swift5
output: Generated/Strings.swift
xcassets:
inputs:
- Resources/Images.xcassets
- Resources/MoreImages.xcassets
- Resources/Colors.xcassets
outputs:
- templateName: swift5
output: Generated/Assets.swift
Then you just have to invoke swiftgen config run
, or even just swiftgen
for short, and it will execute what’s described in the configuration file.
The dedicated documentation explains the syntax and possibilities in details – like how to pass custom parameters to your templates, use swiftgen config lint
to validate your config file, how to use alternate config files, and other tips.
There are also additional subcommands you can invoke from the command line to manage and configure SwiftGen:
- The
swiftgen config
subcommand to help you with the configuration file, especiallyswiftgen config init
to create a starting point for your config andswiftgen config lint
to validate that your Config file is valid and has no errors - The
swiftgen template
subcommands to help you print, duplicate, find and manage templates bundled with SwiftGen
Lastly, you can use --help
on swiftgen
or one of its subcommand to see the detailed usage.
Directly invoking a parser without a config file
While we highly recommend the use a configuration file for performance reasons (especially if you have multiple outputs, but also because it’s more flexible), it’s also possible to directly invoke the available parsers individually using swiftgen run
:
swiftgen run colors [OPTIONS] DIRORFILE1 …
swiftgen run coredata [OPTIONS] DIRORFILE1 …
swiftgen run fonts [OPTIONS] DIRORFILE1 …
swiftgen run ib [OPTIONS] DIRORFILE1 …
swiftgen run json [OPTIONS] DIRORFILE1 …
swiftgen run plist [OPTIONS] DIRORFILE1 …
swiftgen run strings [OPTIONS] DIRORFILE1 …
swiftgen run xcassets [OPTIONS] DIRORFILE1 …
swiftgen run yaml [OPTIONS] DIRORFILE1 …
One rare cases where this might be useful — as opposed to using a config file — is if you are working on a custom template and want to quickly test the specific parser you’re working on at each iteration/version of your custom template, until you’re happy with it.
Each parser command generally accepts the same options and syntax, and they mirror the options and parameters from the configuration file:
--output FILE
or-o FILE
: set the file where to write the generated code. If omitted, the generated code will be printed onstdout
.--templateName NAME
or-n NAME
: define the Stencil template to use (by name, see here for more info) to generate the output.--templatePath PATH
or-p PATH
: define the Stencil template to use, using a full path.- Note: you should specify one and only one template when invoking SwiftGen. You have to use either
-t
or-p
but should not use both at the same time (it wouldn’t make sense anyway and you’ll get an error if you try) --filter REGEX
or-f REGEX
: the filter to apply to each input path. Filters are applied to actual (relative) paths, not just the filename. Each command has a default filter that you can override with this option.- Note: use
.+
to match multiple characters (at least one), and don’t forget to escape the dot (\.
) if you want to match a literal dot like for an extension. Add$
at the end to ensure the path ends with the extension you want. Regular expressions will be case sensitive by default, and not anchored to the start/end of a path. For example, use.+\.xib$
to match files with a.xib
extension. Use a tool such as RegExr to ensure you’re using a valid regular expression. - Each command supports multiple input files (or directories where applicable).
- You can always use the
--help
flag to see what options a command accept, e.g.swiftgen run xcassets --help
.
Choosing your template
SwiftGen is based on templates (it uses Stencil as its template engine). This means that you can choose the template that fits the Swift version you’re using — and also the one that best fits your preferences — to adapt the generated code to your own conventions and Swift version.
Bundled templates vs. Custom ones
SwiftGen comes bundled with some templates for each of the parsers (colors
, coredata
, fonts
, ib
, json
, plist
, strings
, xcassets
, yaml
), which will fit most needs; simply use the templateName
output option to specify the name of the template to use. But you can also create your own templates if the bundled ones don’t suit your coding conventions or needs: just store them anywhere (like in your project repository) and use the templatePath
output option instead of templateName
, to specify their path.
?
You can use the swiftgen template list
command to list all the available bundled templates for each parser, and use swiftgen template cat
to show a template’s content and duplicate it to create your own variation.
For more information about how to create your own templates, see the dedicated documentation.
Templates bundled with SwiftGen:
As explained above, you can use swiftgen template list
to list all templates bundled with SwiftGen. For most SwiftGen parsers, we provide, among others:
- A
swift4
template, compatible with Swift 4 - A
swift5
template, compatible with Swift 5 - Other variants, like
flat-swift4/5
andstructured-swift4/5
templates for Strings, etc.
You can find the documentation for each bundled template here in the repo, with documentation organized as one folder per SwiftGen parser, then one MarkDown file per template. You can also use swiftgen template doc
to open that documentation page in your browser directly from your terminal.
Each MarkDown file documents the Swift Version it’s aimed for, the use case for that template (in which cases you might favor that template over others), the available parameters to customize it on invocation (using the params:
key in your config file), and some code examples.
Don’t hesitate to make PRs to share your improvements suggestions on the bundled templates
?
Additional documentation
Playground
The SwiftGen.playground
available in this repository will allow you to play with the code that the tool typically generates, and see some examples of how you can take advantage of it.
This allows you to have a quick look at how typical code generated by SwiftGen looks like, and how you will then use the generated constants in your code.
Dedicated Documentation in Markdown
There is a lot of documentation in the form of Markdown files in this repository, and in the related StencilSwiftKit repository as well.
Be sure to check the “Documentation” folder of each repository.
Especially, in addition to the previously mentioned Migration Guide and Configuration File documentation, the Documentation/
folder in the SwiftGen repository also includes:
- A
templates
subdirectory that details the documentation for each of the templates bundled with SwiftGen (when to use each template, what the output will look like, and custom parameters to adjust them, …) - A
SwiftGenKit Contexts
subdirectory that details the structure of the “Stencil Contexts”, i.e. the Dictionary/YAML representation resulting of parsing your input files. This documentation is useful for people wanting to write their own templates, so that they know the structure and various keys available when writing their template, to construct the wanted generated output accordingly. - Various articles to provide best practices & tips on how to better take advantage of SwiftGen in your projects:
- Integrate SwiftGen in your Xcode project — so it rebuilds the constants every time you build
- Configure SwiftLint to help your developers use constants generated by SwiftGen
- Create a custom template, and watch a folder to auto-regenerate an output every time you save the template you’re working on
- …and more
Tutorials
You can also find other help & tutorial material on the internet, like this classroom about Code Generation I gave at FrenchKit in Sept’17 — and its wiki detailing a step-by-step tutorial about installing and using SwiftGen (and Sourcery too)
Available Parsers
Asset Catalog
xcassets:
inputs: /dir/to/search/for/imageset/assets
outputs:
templateName: swift5
output: Assets.swift
This will generate an enum Asset
with one static let
per asset (image set, color set, data set, …) in your assets catalog, so that you can use them as constants.
Example of code generated by the bundled template
internal enum Asset {
internal enum Files {
internal static let data = DataAsset(value: "Data")
internal static let readme = DataAsset(value: "README")
}
internal enum Food {
internal enum Exotic {
internal static let banana = ImageAsset(value: "Exotic/Banana")
internal static let mango = ImageAsset(value: "Exotic/Mango")
}
internal static let `private` = ImageAsset(value: "private")
}
internal enum Styles {
internal enum Vengo {
internal static let primary = ColorAsset(value: "Vengo/Primary")
internal static let tint = ColorAsset(value: "Vengo/Tint")
}
}
internal enum Targets {
internal static let bottles = ARResourceGroupAsset(name: "Bottles")
internal static let paintings = ARResourceGroupAsset(name: "Paintings")
}
}
Usage Example
// You can create new images by referring to the enum instance and calling `.image` on it:
let bananaImage = Asset.Exotic.banana.image
let privateImage = Asset.private.image
// You can create colors by referring to the enum instance and calling `.color` on it:
let primaryColor = Asset.Styles.Vengo.primary.color
let tintColor = Asset.Styles.Vengo.tint.color
// You can create data items by referring to the enum instance and calling `.data` on it:
let data = Asset.data.data
let readme = Asset.readme.data
// you can load an AR resource group's items using:
let bottles = Asset.Targets.bottles.referenceObjects
let paintings = Asset.Targets.paintings.referenceImages
Colors
❗️
We recommend to define your colors in your Assets Catalogs and use thexcassets
parser (see above) to generate color constants, instead of using thiscolors
parser described below.
Thecolors
parser below is mainly useful if you support older versions of iOS where colors can’t be defined in Asset Catalogs, or if you want to use Android’scolors.xml
files as input.
colors:
inputs: /path/to/colors-file.txt
outputs:
templateName: swift5
output: Colors.swift
This will generate a enum ColorName
with one static let
per color listed in the text file passed as argument.
The input file is expected to be either:
- a plain text file, with one line per color to register, each line being composed by the Name to give to the color, followed by “:”, followed by the Hex representation of the color (like
rrggbb
orrrggbbaa
, optionally prefixed by#
or0x
) or the name of another color in the file. Whitespaces are ignored. - a JSON file, representing a dictionary of names -> values, each value being the hex representation of the color
- a XML file, expected to be the same format as the Android colors.xml files, containing tags
<color name="AColorName">AColorHexRepresentation</color>
- a
*.clr
file used by Apple’s Color Palettes.
For example you can use this command to generate colors from one of your system color lists:
colors:
inputs: ~/Library/Colors/MyColors.clr
outputs:
templateName: swift5
output: Colors.swift
Generated code will look the same as if you’d use a text file.
Example of code generated by the bundled template
Given the following colors.txt
file:
Cyan-Color : 0xff66ccff
ArticleTitle : #33fe66
ArticleBody : 339666
ArticleFootnote : ff66ccff
Translucent : ffffffcc
The generated code will look like this:
<div class="highlight highlight-source-swift position-relative" data-snippet-clipboard-copy-content="internal struct ColorName {
internal let rgbaValue: UInt32
internal var color: Color { return Color(named: self) }
///
/// Alpha: 100%
(0x339666ff)
internal static let articleBody = ColorName(rgbaValue: 0x339666ff)
///
/// Alpha: 100%
(0xff66ccff)
internal static let articleFootnote = ColorName(rgbaValue: 0xff66ccff)
…
}
“>
internal struct ColorName { internal let rgbaValue: UInt32 internal var color: Color { return Color(named: self) } /// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#339666"></span> /// Alpha: 100% <br/> (0x339666ff) internal static let articleBody = ColorName(rgbaValue: 0x339666ff) /// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#ff66cc"></span> /// Alpha: 100% <br/> (0xff66ccff) internal static let articleFootnote = ColorName(rgbaValue: 0xff66ccff) ... }