Strings in Swift Explained

Written by Reinder de Vries on September 22 2020 in App Development, Swift

Strings in Swift Explained

You use strings in Swift to represent text. They’re strings of characters, use the String value type, and are stored with the intriguing Unicode/UTF-8 data format. Let’s find out more about strings in Swift!

In this tutorial, we’ll get into:

  • How you use String and string literals in Swift
  • String operations like interpolation, concatenation and addition
  • The consequences of Unicode and UTF-8, and string lengths
  • Working with indices to get specific characters from a string
  • Getting substrings from a string with range operators
  • (And a lot of examples…)

Ready? Let’s go.

  1. What’s a String?
  2. Strings and Characters in Swift
  3. String Operations and Interpolation
  4. Working with String Indices
  5. Getting Substrings with Swift
  6. Further Reading

What’s a String?

A string in Swift is just a bunch of characters in a row, like "Hello world!". Strings in Swift are represented by the String type, which is a struct.

They’re called strings because they “string” characters together (like a shashlyk!). You use strings a lot in day-to-day iOS development, ranging from user-facing bits of text to dictionary keys and identifiers in your code.

Here’s an example of a string in Swift:

let name = "Arthur Dent"
print(name)

In the above example, the "Arthur Dent" code is a string literal. It’s a literal representation of a string. You can recognize it by its double quotes "···". You never use single quotes ' in Swift.

The type of the name constant in the above code is String. This is a struct, which means that strings in Swift are value types.

You can create multi-line strings in Swift, which is useful if you want to write larger bits of text on multiple lines. Check this out:

let line = """
Trinity: Neo, no one has ever done anything like this.
Neo: I know, that's why it's going to work.
"""

The above multi-line string is wrapped in 3 double quotes """. It’s also good to know that a multi-line string preserves any line breaks you’ve added to the string. When you do print(line) for the above code, you really get 2 separate lines of text. In a single-line string, you just get one line.

You can’t use the double quote " without escaping it with a backslash in a single-line string literal, i.e. "Hello \"world!\"" for Hello “world!”. You can, however, use double quotes in a multi-line string without escaping. Don’t worry, these things become clear as you bump into them in day-to-day app building!

Learn how to build iOS apps

Get started with iOS 14 and Swift 5

Sign up for my iOS development course, and learn how to build great iOS 14 apps with Swift 5 and Xcode 12.

Strings and Characters in Swift

A string is technically a series of characters. They’re readable by us humans, but a computer couldn’t care less if you wrote "\u{59}\u{65}\u{61}\u{68}\u{21}" instead of "Yeah!".

That string with the \u{···} code in it – that’s Unicode. It’s a standard data format for text, which is basically a huge lookup table of characters and “code points”. A font file, for example, converts a code point like \u{61} to the pixel shape of an “a”. That’s how bits and bytes are converted to shapes us humans recognize as words and sentences.

Strings in Swift are encoded with UTF-8. An encoding like UTF-8 determines how a data point like Unicode’s \u{61} is stored in bytes. You can compare that to the vocabulary and words you use (Unicode), and left-to-right or right-to-left (encoding). With Unicode and UTF-8 we run into a problem, which has important consequences for working with strings in Swift.

Strings in Swift use multi-byte encoding, and something called extended grapheme clusters, which means that any character can be represented using a variable number of bytes. Multiple code points can be combined into one character. This means that, in ones-and-zeroes, every character can have a different length in bytes.

Here’s an example:

  • a is 01100001 (1 byte)
  • 👍 is 11110000 10011111 10010001 10001101 (4 bytes)

What happens if we turn a more complex string, consisting of many characters, into binary? How do we know where each character ends, and the next one starts? You can see how the code 01100001 might mean “a”, or maybe it’s part of the bytes of a longer, complex emoji…

Fortunately for us iOS developers, this problem has already been solved. iOS uses UTF-8 behind the scenes to take care of variable-width encoding. It has a consequence though: it’s impossible to calculate the length of a string without looking at each character individually.

With a fixed-width encoding, you can just divide the entire string by the word-length of a character to get the amount of characters in the string. This isn’t possible with variable-width encoding, because each character can have a different length.

Here’s how that affects strings in Swift:

  1. You can’t access a string in Swift like an array, i.e. with an integer subscript like name[5] = "a"
  2. You need to use string indices to access characters in a string, which represent a fixed position within that string
  3. You can use emojis in your Swift source code – “Why can you use emojis in Swift?” is a great reminder of the consequences of multi-byte UTF-8 encoding

You can get the length of a string in Swift with the count property, like this:

let name = "Ford Prefect"
print(name.count) // Output: 12

The count property is technically an O(n) operation, which may surprise you. Due to the nature of variable-width encoding, you can only get the length of a string in Swift by iterating over each individual character.

You can also use the isEmpty property to quickly check if a string is empty or not. This property is logically equal to count == 0.

let name = "Ford Prefect"
print(name.isEmpty) // Output: false

Let’s move on!

Pro tip: The above is, of course, a quick explanation of complex topics, so we all can get on with our lives. That said, spend some time looking up these concepts, like variable-width encoding. They’re fundamental, intriguing challenges of Information Theory, going from Morse code all the way to emojis!

Fun fact: String literals are stored in the app binary that Xcode produces when you compile your app. You can read them with a hex or binary editor. These strings end up in computer memory during runtime, they’re literally read from the app binary – same as instructions like if. You should never store sensitive passwords or keys as literals in your app’s source code.

String Operations and Interpolation

You can use a few useful operators with strings in Swift, most notably string interpolation. Here’s how that works:

var answer = "42"
let dontpanic = "The Answer to Life, the Universe and Everything is \(answer)"
print(dontpanic)

In the above code, the \(···) code within the literal uses string interpolation. It’ll insert the value of variable answer into the string literal, which effectively includes it into the final string.

The syntax is simple: include \( ) inside the string’s double quotes and write (almost) anything you want inside the parentheses.

You can do this with any value that can be represented as a string, like integer values. You can also use any expression, i.e. functions, operators, etc. to create a complete string. String interpolation isn’t limited to print(), you can use it anywhere you use strings.

let number = 8
print("\(number) > 3 = \(number > 3) and \(number) / 3 is \(Double(number) / 3.0)")

You can also use a few operators with strings in Swift. Let’s take a look at a few examples. First, you can concatenate two strings by using the + operator:

let a = "Hello"
let b = "world!"
let c = a + " " + b
print(c) // Hello world!

In the above code, we’re “adding” one string to another by using constants a and b. Keep in mind that the + operator only works on values of the same type, i.e. two strings. Code like 42 + "42" won’t work, because the types don’t match.

You can also append strings to each other. Check this out:

var passcode = "Tinker Tailor "
passcode += "Soldier Spy"
print(passcode) // Tinker Tailor Soldier Spy

In the above code, we’re using the addition assignment operator += to append a string to passcode. It’s the same as coding passcode = passcode + "···".

Here’s a quick one-liner to concatenate or “join” an array of strings:

let items = ["Tinker", "Tailor", "Soldier", "Spy"]
let passcode = items.joined(separator: " ")
print(passcode)

It’s worth noting that the separator is added between the array items, when they’re joined into one string. If you’d use, say, a for loop to do the same, you’d run into trouble with the last (or first) item’s separator.

Riffing on string concatenation some more, you could also use the reduce(_:_:) function. Like this:

let items = ["Tinker", "Tailor", "Soldier", "Spy"]
let passcode = items.reduce("", +)
print(passcode) // TinkerTailorSoldierSpy

Awesome! Let’s move on.

Working with String Indices

We’ve already discussed how UTF-8 and Unicode affect strings in Swift. Their variable-width encoding means you can’t access an arbitrary character in a string by an integer index. The subscript below won’t work:

let name = "Ford Prefect"
print(name[5]) // Expected output: "P", but doesn't compile :-(

You’ll need an index! Swift uses indices to designate the locations of individual characters in a string. Let’s take a look at an example:

let name = "Ford Prefect"
let index = name.index(name.startIndex, offsetBy: 5)
print(name[index])

See what happens? We’re getting an index from the name string with the index(_:offsetBy:) function. This function takes a starting index, which is a property of name, and offsets that by 5. This gives us an index value for the name[···] subscript, which prints "P".

The good news is that string indices are almost the same as integer values for specific positions of characters in a string, as you’d intuitively expect. The bad news is that you can’t just punch in an integer and get the character back. In the simplest scenario, you need a startIndex and a number of steps forward from that position.

You can simply loop over every character in a string, like this:

let name = "Ford Prefect"

for c in name {
    print(c) // F, etc.
}

Accessing an arbitrary character is more useful, though. You’ve got a few more index-producing functions in your arsenal:

  • string.startIndex and string.endIndex are properties that return the indices for the start and end of the string. They’re good positions to start with. (See note below on endIndex.)
  • string.index(before:) and string.index(after:) produce indices to the left and right of another index, i.e. s.index(after: s.startIndex) is the second character of a string. The last character is s.index(before: s.endIndex).
  • string.firstIndex(of:) searches for a specific substring, and returns its index. Perfect if you know what you’re looking for, but don’t know where in the string.

The return type of a subscript like name[index] is Character, and not String. You can convert the character to String with the String(···) initializer. Like this:

let name = "Ford Prefect"
let index = name.index(name.startIndex, offsetBy: 5)
let char = String(name[index])
print(type(of: char)) // Output: String

String indices are challenging to grasp at first, but once you wrap your head around them they become second nature. Play around with getting indices from strings, and working with characters, and you’ll get a feel for it soon enough.

Want to practice with string indices? Check out this tutorial: Play With Code: Palindromes In Swift

Quick Tip: The string.endIndex property of a string is “past the end”, which means it’s essentially one greater than the last valid character of a string. The endIndex is equal to the startIndex offset by the string’s length, which should give you an idea about the off-by-one errors you could run into. The endIndex is not the last character of the string. It’s literally the end of the string – like a blinking cursor or caret – so there’s nothing there…

Getting Substrings with Swift

A common scenario in programming is needing to get a specific substring from a string, like a range of characters or a word. You can do this by combining Swift’s range operators and string indices.

Let’s take a look at an example:

let name = "Ford Prefect"
let index = name.firstIndex(of: " ")!
let firstWord = name[..<index]
print(firstWord)

In the above code, we’re finding the index of the first occurrence of a space character in name. It’s at the 4th position in the string, and now we’ve got the index too.

On the third line, we’re using the ..< one-sided half-open range operator to get every character of name up to index, not including index. That means "Ford", i.e. the first word of the string.

Check for yourself: the index we found is for the space, and we’re getting a substring up to but not including that space. So, firstWord does not contain the space character!

Substrings in Swift aren’t strings yet; they’re mere constructs called slices. They’re basically wrappers around the string, indicating which indices you want. Just like Character, you can convert them to String when you need to:

let firstWord = name[..<index]
print(type(of: firstWord)) // Output: Substring

let firstWord = String(name[..<index])
print(type(of: firstWord)) // Output: String

You can use all range operators with strings.

  1. Closed ranges with a...b, i.e. from one index to another
  2. Half-open ranges with a..<b, i.e. from one index up to but not including another
  3. One-sided ranges with a..., ...b and ..<b, i.e. everything from, to, and up to but not including

Awesome!

Learn how to build iOS apps

Get started with iOS 14 and Swift 5

Sign up for my iOS development course, and learn how to build great iOS 14 apps with Swift 5 and Xcode 12.

Further Reading

Strings in Swift look so simple, and you use them all the time. Who knew they’d contain so many clever, intriguing mechanisms? In this tutorial, we’ve discussed how you can use strings in Swift – and much more.

  • The consequences of Unicode and UTF-8, and string lengths
  • Getting substrings from a string with range operators
  • Working with indices to get specific characters from a string
  • String operations like interpolation, concatenation and addition

Want to learn more? Check out these resources:

Reinder de Vries

Hi, I'm Reinder.
I help developers play with code.

Get the Weekly

Get iOS/Swift tutorials and insights in your inbox, every Monday.
  • This field is for validation purposes and should be left unchanged.

Most Popular

Browse Topics

Swift Sandbox

Code Swift right in your browser!
Go to the Swift Sandbox

Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.

×

Build great iOS apps
Learn how in my free 7-day course

  • This field is for validation purposes and should be left unchanged.

No spam, ever. Unsubscribe anytime. Privacy Policy