UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to use raw strings in Swift 5

What are the pound signs around strings for?

Paul Hudson       @twostraws

Raw strings in Swift 5 give us the ability to write more natural strings, particularly when using backslashes and quote marks. In some instances, such as regular expressions, the difference is dramatic, as you’ll see.

I’ve written extensively about all the new features in Swift 5, and even have a whole website dedicated to tracking what’s new in Swift, but in this article I want to go further: I want to talk about how to use raw strings in Swift 5, but also give some detailed examples of why they are useful so you can see for yourself their usefulness.

So, if you've ever thought to yourself "what are those hashtag strings in Swift?" hopefully this article will help!

Tip: Raw strings are completely optional – while it’s important you at least know what they are so you can recognize them in the wild, you don’t need to use them in your own code if you don’t want to.

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

What are raw strings?

Swift 5 gives us the ability to specify a custom string delimiter using the hash symbol, #, sometimes called a hashtag or a pound sign. When you use # with a string it affects the way Swift understands special characters in the string: \ no longer acts as an escape character, so \n literally means a backslash then an “n” rather than a line break, and \(variable) will be included as those characters rather than using string interpolation.

So, these two strings are identical:

let regularString = "\\Hello \\World"
let rawString = #"\Hello \World"#

Notice how in the second example the string starts and ends with a # symbol, which is what marks it as being a raw string.

That same # symbol can now be used inside strings, to mark special characters. For example, if you want to use string interpolation, you should now use \#(variableName) rather than just \(variableName), like this:

let name = "Taylor"
let greeting = #"Hello, \#(name)!"#
print(greeting)

You can also use them with multi-line strings, like this:

let message = #"""
This is rendered as text: \(example).
This uses string interpolation: \#(example).
"""#

Working with the delimiter

Although it’s a feature that theoretically ought never to be needed, it’s possible to add more hash symbols around your string to make more unique string delimiters.

For example, all of these create the same string:

let zero = "This is a string"
let one = #"This is a string"#
let two = ##"This is a string"##
let three = ###"This is a string"###
let four = ####"This is a string"####

The reason this exists is so that strings end only when you want them to, so in the unlikely event that you you need to write ”# in a string you won’t hit problems.

I should stress that this is extremely unlikely. For example, you’d need to write a string like My dog said "woof"#gooddog to hit the problem – I didn’t leave a space after the quote in “woof”, and used a Twitter-style hashtag directly after. Using a single-delimited raw string Swift would see that as a string terminator, so you’d need to write this instead:

let str = ##"My dog said "woof"#gooddog"##

Why are raw strings useful?

The Swift Evolution proposal for raw strings lists three examples of where raw strings are a good idea. Specifically, code that:

  1. Is obscured by escaping. Escaping actively harms code review and validation.
  2. Is already escaped. Escaped material should not be pre-interpreted by the compiler.
  3. Requires easy transport between source and code in both directions, whether for testing or just updating source.

The first two are the ones that are most likely to affect you: adding escaping to strings that already have escaping usually makes code much harder to read.

As an example, let’s take a look at regular expressions. Imagine we have a string like this:

let message = #"String interpolation looks like this: \(age)."#

That uses raw strings to let us show how string interpolation looks rather than actually using it – the string “(age)” will appear in the text, rather than being replaced by the value of a variable called age.

If we want to create a regular expression to find all string interpolations, we’d start with \([^)]). That means “backslash, open parenthesis, one or more characters that aren’t a closing parenthesis, then a closing parenthesis. (If you don’t already use regexes, you might find my book Beyond Code very useful!)

However, we can’t use that in Swift – this isn’t valid:

let regex = try NSRegularExpression(pattern: "\([^)])")

Swift sees the \ as an escape character, and assumes we’re trying to use string interpolation in our regex. So, we need to double escape the backslash, like this:

let regex = try NSRegularExpression(pattern: "\\([^)]+)")

But now there’s a second problem: when that string reaches the regex system it will be read as \([^)]), so the regex system will assume we’re escaping the opening parenthesis as opposed to typing a literal backslash, so we need to add another escape for the regex system:

let regex = try NSRegularExpression(pattern: "\\\([^)]+)")

…and again Swift will complain because it thinks we’re escaping the backslash and escaping the parenthesis, so we need a fourth backslash:

let regex = try NSRegularExpression(pattern: "\\\\([^)]+)")

Yes, that’s now four backslashes: one we actually want to match, one to escape that in Swift, one to escape it in the regex engine, and one to escape the regex engine escaping one in Swift.

And that regex still isn’t going to work.

You see, we also need to escape the opening and closing parenthesis we want to match, which means the full regular expression is this:

let regex = try NSRegularExpression(pattern: "\\\\\\([^)]+\\)")

Remember, we add one to escape ( in the regex engine, and another one in Swift to escape the regex engine quote.

I hope you can now see the sad truth of the xkcd cartoon about backslashes:

If we use raw strings instead, we still need to escape characters for the regex engine: to match “\” we must write “\”, and to match “(“ we must write “(“. However, at least we no longer need to add extra escape characters for Swift.

So, we end up with half the number of backslashes:

let regex = try NSRegularExpression(pattern: #"\\\([^)]+\)"#)

That regex pattern has no escaping unique to Swift, so you can try it out on sites like https://regex101.com without modification.

Where next?

To find out more about all the new features in Swift 5, you should read my article: What’s new in Swift 5.0?

You might find it useful to read the Swift Evolution proposal that brought about raw strings: SE-0200 – Enhancing String Literals Delimiters to Support Raw Text.

Finally, I can highly recommend Erica Sadun’s article on this same topic. Erica was instrumental in shaping this proposal, and has a lot of great advice on how to use raw strings effectively.

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.