Search and Replace in Xcode with Regular Expressions
Recently I ran into a problem where I wanted to search for a pattern in the Xcode project and replace the found string with another one. In the past, I had had a hard time dealing with Regular expressions. This time I decided I was going to learn more about them and if possible write a post about how to use them. So here we go. How to Search and Replace in Xcode project with Regular Expressions
Enabling search and replace with regular expressions option in Xcode
To find a keyword with RegEx, go to project navigator in the left side pane. Select Find
and then select Regular Expression
option. This will enable finding with given regular expression statement
Replacing with regular expression
To replace a keyword with RegEx, go to the same project navigator in the left side pane. Select Replace
and then select Regular Expression
option. This will enable replacing with the given statement with regular expression format
Searching for an expression in the Xcode project
Say I want to search methods in the project which take NSString
as an input and return nothing. For example,
- (void)foo:(NSString*)foo
I can simply do - \(void\)(.*):\(NSString\*\)(.*) \{
What this expression does is, it tells the search operator not to worry about method name and named parameter associated with methods matching given regular expression. All we are searching for is the method that takes NSString
as an input parameter and returns nothing.
The values found using the input RegEx can be extracted using variable $1
and $2
in the example above. As you may have noted, RegEx won't work with special characters such as open and close brackets. You will have to escape them manually using backslash characters to enable searching.
In case you run into problem with RegEx containing special characters, make sure you escape them before performing any more debug steps.
To give you another example, say I want to find all return statements returning array count, I can use either of the following statements. (Depending on your coding guidelines)
return \[(.*) count\]$
or return (.*).count$
.
Where the name of the array in question is stored in the special variable $1
.
Please note that we're also specifying$
as an end of string characters, otherwise we will find those string too which containcount
as a substring and not as an end keyword
Replacing an expression in the Xcode project
Now since we are past the initial formalities, let's dive into a slightly more complex RegEx operation of replacing an expression. Let's continue from a couple of previous examples.
As we saw, - \(void\)(.*):\(NSString\*\)(.*) \{
can be used to search all methods taking NSString
as an input parameter and returning nothing. Let's say we want to replace them with another parameter, say bar
. So all methods like
- (void)foo:(NSString*)foo
will now look like,
- (void)foo:(NSString*)foo andBar:(NSString*)bar
.
How do we do it?
the solution is simple. You get all expressions matching with the given RegEx into pre-determined variables such as $1
, $2
and so on. So all you have to do it, just put them into a replacement string and append any extra expression you want. Using the same principle, we can perform replacement with the following string.
First off, search for this,
- \(void\)(.*):\(NSString\*\)(.*) \{
And then replace it with,
- \(void\)$1:\(NSString\*\)$2 andBar:(NSInteger)bar \{
Here, the original method signature remains unchanged and that's why we chose to re-use them in the form of $1
and $2
. and final result throughout your project is,
- (void)foo:(NSString*)foo andBar:(NSInteger)bar {
On a similar note, if you have changed your internal guidelines, and decided to follow another syntax to get NSArray
count. ([array count] to array.count), this can be done by the following RegEx.
Simply replace
\[(.*) count\]$
with
$1.count
It will replace all message sendings with parameter access for NSArray
with dot
syntax
Update:
We recently ran a SwiftLint on our codebase and caught a few warnings related to legacy functions.
For example, the lint tool asked me to replace older CGRectMake
with CGRect
and so on. Below is how I did to find and replace other legacy expressions
- CGRectMake
// Searching all legacy `CGRectMake` structs
// Search with following RegEx
CGRectMake\([ ]{0,1}(.*),[ ]{0,1}(.*),[ ]{0,1}(.*),[ ]{0,1}(.*)\)
// [ ]{0,1} says value followed by zero or more spaces.
// We use this because of uncertain number of spaces before comma.
// (.*) captures values in the form of $1, $2, etc.
// Replacing all legacy `CGRectMake` structs with new syntax
// Replace the found RegEx with following expression
CGRect\(x: $1, y: $2, width: $3, height: $4\)
// Where $1, $2 represent the values caught by previous expression matching values in the form (.*) around commas.
- CGSize
// RegEx to search for
CGSizeMake\([ ]{0,1}(.*),[ ]{0,1}(.*)\)
// RegEx to replace with
CGSize\(width: $1, height: $2\)
- NSMakeRange
// RegEx to search for
NSMakeRange\([ ]{0,1}(.*),[ ]{0,1}(.*)\)
// RegEx to replace with
NSRange\(location: $1, length: $2\)
Here are some more examples of search and replace operations using regular expressions in Xcode,
A. Adding a new parameter to the method
Example method call: myPrint(number: 100)
RegEx for search: myPrint\( (.*)\)$
RegEx for replace: myPrint\($1, number: 2\)
B. Removing parameter from method
Example method call: myPrint(number: 100, number2: 200)
RegEx for search: myPrint\( (.*), (.*)\)
RegEx for replace: myPrint\($1\)
These are all the examples I could find to demonstrate search and replace operation using regular expressions. If you have specific problem use-cases which can be solved using regular expressions in Xcode, please let me know. I will be happy to amend the existing list of examples with new ones
Hope this helps if you are dealing with relatively large codebase. Simple expressions are easy to search and replace, but if you can use RegEx to search and replace complex expressions, why not? Still better than manual search and replace which is more prone to human errors
Thanks to This post by Road fire software which was a great resource and inspiration for this post