Save Time With Regular Expressions in Swift

Use R.swift to clean up NSLocalizedString and UIImage references

Alejandro Zielinsky
Better Programming

--

Photo by Brad Neathery on Unsplash

I was recently tasked with implementing a fantastic library, named R.swift, that cleans up all of the resources in your project and allows them to be easily referenced.

This article won’t go into detail about the library too much. Instead, I would like to show you how useful the Xcode “find and replace with regular expressions” feature is, and how it helped me save a bunch of time I would’ve spent tirelessly fixing hundreds of lines of code one-by-one.

The task was to replace all of our NSLocalizedString and UIImage references using the R.swift way of getting a resource.

For example, getting a localized string like this:

NSLocalizedString("common_cancel", comment: "Cancel")

Had to be replaced by the following:

R.string.localizable.common_cancel()

Since we have over 1,000 localized string and image resources in our app, replacing every reference one at a time would’ve been quite a time-consuming task to undertake. Luckily, I discovered a nifty Xcode feature that was exactly what I needed.

Find and Replace Using Regular Expressions

A regular expression, or regex, allows you to define a search pattern using a special syntax, that matches whatever text you may be looking for. It also allows you to capture parts of text for reuse.

In case you haven’t used this Xcode feature, you can find the option in your project navigator.

Click on “Find”, switch it to “Replace”. Click on “Text” and switch it to “Regular Expression”.

Consider this expression:

NSLocalizedString\("(.*)",(?:.*)

What this does is it looks for any text that starts with NSLocalizedString, and stores the contents of the first parameter’s quotation marks. Since this is all I need to capture, I ignore every character after the first parameter.

Let’s break down the expression a bit more.

The backslash after NSLocalizedString is needed to escape the open parenthesis character.

Note that special characters need to be escaped using a backslash. If you run into a problem where your expression isn’t working, make sure you are escaping special characters.

(.*) The parentheses create a numbered capturing group. This allows you to store part of the string that matches your expression to be extracted for later.

The dot indicates any character, and the asterisk indicates zero or more instances of the preceding regex token. Therefore, capturing whatever the contents of the parentheses may be.

From my example, it would capture common_cancel, which is the name of my localized string resource. This is the part of the text I care about and need for my string replacement.

(?:.*) The parentheses with a question mark and colon create a non-capturing group. The contents of which get discarded. Since the parentheses contain another dot and asterisk, it ignores the rest of the characters in my line of code.

So, now that my regular expression matches all of the instances of NSLocalizedString in my project, I am ready to replace them with the following line:

R.string.localizable.$1()

$1 is the numbered reference that contains my captured text. If I hit replace, I get exactly what I needed.

R.string.localizable.common_cancel()

Amazing! All of the hundreds of instances that the expression matched are instantly replaced with the new line of code I need. A tremendous amount of time saved.

Updating Image Resources

The next update I had to make was very similar to the previous example. The resource this time being every UIImage reference in my project.

Getting an image like this:

UIImage(named: "menuIcon")

Had to be replaced by the R.swift way of referencing an image:

R.image.menuIcon()

Again, the only thing that needed to be captured is the name of the image. In this example, it was menuIcon.

This is the regular expression I used:

UIImage\(named: "(.*)"\)

It matched all of my UIImage references and stored the image name that I needed. Similar to the previous example, I replaced all of the references with this line:

R.image.$1()

Resulting in the following:

R.image.menuIcon()

At this point, I was done. What could have taken me several hours took me minutes instead.

If you encounter a similar type of tech debt task, try and see if you can utilize finding and replacing with regexes. I was a complete beginner with regular expressions while working on this task, so it was a nice achievement for me. I was quite thrilled when I got everything to work.

Hopefully, this article was helpful for some of you. Lastly, check out R.swift. It’s an amazing tool that I highly recommend, and will be using for all my future iOS projects.

--

--