Note: This is post discusses Shark, which uses Swift 2.0 and it’s toolchain. Make sure you’re using the correct toolchain by doing
sudo xcode-select -switch /Applications/Xcode-beta.app/Contents/Developer/before you begin.
I’ve been working on a small Swift script called Shark for a while. It aims to leverage Swift’s strong type system in order to make image loading type-safe.
Let’s build an example project that shows how to use Shark. Then at the end, we’ll shorty discuss how it works.
Before we begin let’s see how image loading looks with and without Shark:
###Installing Shark Clone the repo if you haven’t already. https://github.com/kaandedeoglu/Shark. The repo contains the source to the script, and also a compiled executable. There are two ways to setup Shark:
Shark.swiftfile to your project directory. Make sure you don’t add it to your project. Keeping the file in some other location works as well, but will cause big headaches if you’re working in a team
Move the executable named
sharkto /usr/local/bin. Since the library is rapidly evolving at the time of writing, doing this every time there’s an update can be mildly annoying.
If you don’t like the sound of adding an unknown executable to your bin folder. You can take a look at the source, and use
xcrun -sdk macosx swiftc Shark.swift -o sharkto build your own executable.
I’m looking for ways to make the installation easier - any ideas are very welcome
Start up Xcode 7. Start by creating an new “Single Screen Application”. For the purposes of this tutorial, I called mine ManyImages
Our empty, boring project
Next, we are going to add some images to the project. After all, we called it ManyImages. For the sake of easiness, I just copied the contents of the image assets folder of another project of mine. I suggest you do the same instead of looking for images to include.
much image, very pixel, wow
I found out that using Shark, it’s almost always better to use folders to group images. This way, code completion makes it much easier to find the image you’re looking for.
Use the small + icon at the bottom left of the asset catalogue screen to add new folders
Looks much better. Now to the fun part:
Go to project settings, select your target, and switch to the
Build Phases tab. Use the + icon at the top of the page and select New Run Script Phase. This will create a new run script. Drag the newly created
Run Script tab to place it below
Target Dependencies and above
Compile Sources. You can also click on the word
Run Script and rename it to
Shark. Your screen should look like this when you’re done.
Now, expand the run script (now called Shark) tab. Shark takes two parameters. The path to the image assets folder, with an extension
.xcassets, and an output directory where the generated file will be written.
If you’ve followed step 1 in the installation directions. Then the script will look like:
And if you’ve followed step 2 and moved the shark executable in /usr/local/bin, the script will look like:
Both cases are displayed below:
Running Shark with the Swift toolchain
Running Shark with the executable
With this setup, we’ve told Xcode to run Shark before each build. Open your project folder, and you should see a file called
Drag and add this file to your project, we are almost there!
Let’s take a look at the generated file:
So simple, but so useful
We can see that Shark has created a nested enum for each folder, and a case for each image.
The file has also some
Swift 2.0 Protocol Extension magic to add a computed property named
image to each generated enum, as well as an extension on
With all this hard work behind us, it’s time to reap the rewards. Switch to
ViewController.swift, and let’s try loading an image using Shark.
Notice how we get code completion suggestions from Xcode as we type.
Who doesn’t love code completion?
With our type safe enums, we’ll never make typing mistakes when loading images. Better yet, with good enough grouping and naming, we won’t have to constantly switch to look up image names.
How it works
Under the cover, what Shark does is pretty simple. It walks down the .xcassets folder, creates a model of the folder structure, and uses that to generate a text output which get’s written to a file.
I just want to point out the data type I used to represent the folder structure. I think it’s very neat, and without
Swift things might have gotten a lot uglier.
This concise, 4 line
enum is all it took to successfully describe the folder structure.
The fact that we can pattern match on this is plain amazing, and it allows for easy construction of recursive functions.
Consider this function signature, also from the script:
When we think in terms of pattern matching, we automatically split the problem into smaller chunks. The implementation of the above function looks something like this:
The recursive call to
createEnumDeclarationForResources is easy to understand thanks to the easy decomposition of .Directory values through pattern matching.