What are the pound signs around strings for?
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.
SPONSORED Alex is the iOS & Mac developer’s ultimate AI assistant. It integrates with Xcode, offering a best-in-class Swift coding agent. Generate modern SwiftUI from images. Fast-apply suggestions from Claude 3.5 Sonnet, o3-mini, and DeepSeek R1. Autofix Swift 6 errors and warnings. And so much more. Start your 7-day free trial today!
Sponsor Hacking with Swift and reach the world's largest Swift community!
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).
"""#
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"##
The Swift Evolution proposal for raw strings lists three examples of where raw strings are a good idea. Specifically, code that:
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.
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.
SPONSORED Alex is the iOS & Mac developer’s ultimate AI assistant. It integrates with Xcode, offering a best-in-class Swift coding agent. Generate modern SwiftUI from images. Fast-apply suggestions from Claude 3.5 Sonnet, o3-mini, and DeepSeek R1. Autofix Swift 6 errors and warnings. And so much more. Start your 7-day free trial today!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.