Stay DRY & Typesafe with R.swift
There are nothing quite as annoying as catching yourself misspelling a name for an external resource in your Swift code, causing your app to fail without any help from the compiler. In this article I’ll show you how to avoid a small subset of those errors where you didn’t get the key for a localization quite right, or you misspelled the name of an image incorrect causing your app to behave strangely.
Back in 2014 I briefly dived head first into Android development for a semester and had a small taste of writing apps for the “other side”. It was a very interesting experience and I both negative and positive takeaways. One of the more interesting tools in the Android developer bag is how resources are accessed by the application code. On the Android platform there is a constant class named R. This class is accessible from anywhere in your code and automatically generated for you by the Android toolchain. Through the R class any resource for your application can be accessed via static strongly typed variables that doesn’t allow the developer to screw up with spelling mistakes.
Below is an example of how you access an image view in your layout and display an image from your apps resources using R.
In Swift we are not quite as lucky to have anything like this provided to us by the platform. We do have Asset Catalogues but images, localization, font files, etc. are all referred to using strings. Luckily, we can change that with R.swift.
Tell me more about this R.Swift
R.swift makes use of a little compiled swift binary that will look through your project directory and identify any resource files that you might want to use in your Xcode project. Right off the bat it will find any images (inside or outside of Asset Catalogues), string files, JSON files, Storyboards and Nibs, font files and any files of other types that isn’t code. The output of the binary is a R.generated.swift file that looks something like this:
Sorry about that, I know that was quite a bit. I shortened the output a few places with a … comment.
In the simplest form the output is a simple Swift file containing a struct named R which has static properties for all resource files in your project.
The output is done cleverly such that localization will be encapsulated in calls to NSLocalizedString, saving you time and effort in localization. It will even detect variables in your localization and provide you a function for your localization that takes the needed arguments.
For images it will automatically return a UIImage
and for other files it will give you a NSURL so you can read directly from the files easily.
The most annoying part about R.swift is getting it setup. It’s fairly simple if you’re using CocoaPods since that will automagically modify your Xcode project file and copy in the needed binary. I am however an avid Carthage addict and tend to stay away from CocoaPods. Since Carthage will only build frameworks for you we’ll have to configure the rest on our own.
To generate the “magic” R.generated.swift file we need the binary “rswift” on our system. You can install it manually by downloading the zip file of the latest release from the R.swift repo. I, however prefer to include the binary in the project directory so that it is not another external dependency my fellow developers have to update and install. I downloaded the latest release and put into a directory called External in my project directory’s root.
You can manually run the rswift binary to generate the output from the terminal which is a bit tedious or you can set up a build phase in Xcode to have the file generated every time you build your project.
To get the build phase set up, find your project file in the file navigator and click your application target. Find the Build Phases tab and click the tiny plus and select Add Run Script build phase. I’ve added the rswift binary in a subdirectory so I need to specify what the source root is and where I would like the generated output to be. I like my R.generated.swift file to be in the folder Resources.
Now every time I build the project the generated R file is updated. Now add the R.generated.swift file to your project.
Here is a tiny ProTip: Make sure to add your new build phase before your compile sources build phase. Otherwise you may end up in situations where your code won’t compile because the generated output is not properly generated yet.
You also need a little bit of runtime help in the form of a framework. If you’re using Carthage it is as easy as adding any other framework dependency. You can also download the source and build the framework from scratch if you’d like.
I love how the compiler and autocompletion in Xcode now will help you to find the resources in your project, save you grief from misspellings and even save you a bit of typing. It’s a little cumbersome to get everything set up, but on a longer running project I deem it to be worthwhile. I only hope that we someday may get some tooling support more akin to what Android developers have in R.