Logo for tanaschita.com

Working with Strings in Swift

Learn how to use Swift's String type and its companions Character, Index and Substring.

05 Jun 2021 · 5 min read

Representing text values, Strings are one of the most common data structures we use when coding. In this post, we will learn how to work with Swift's String type and its companions Character, Index and Substring.

Let's directly jump into an example.

var fruit = "apple" // fruit is of type String
fruit.isEmpty // returns false
fruit.count // returns 5
fruit.uppercased() // returns "APPLE"
fruit.first // returns the first Character - a
fruit.last // return the last Character - e
fruit.append("!") // changes "apple" to "apple!"

Above, we can see some of the String properties and methods in action that are mostly self-explanatory.

Under the hood, Swift's String type is modeled as a collection of characters represented by the Character type. Strings behave like other collections in many aspects. For example, we can iterate over their characters just like how we would iterate over an array:

for character in "hello 🐌" {
print(character) // prints "h", "e", "l", "l", "o", "🐌"
}

Every Character type represents a human-readable character which can consist of one or more Unicode characters. Unicode is an international standard for encoding, representing and processing text. It enables us to represent almost any character from any language in a standardised form.

let emoji: String = "🐌"
emoji.count // returns 1
emoji.utf8.count // returns 4

As we can see above, the number of Swift characters and the actual number of UTF-8 characters aren’t always equal.

String interpolation

String interpolation provides a way to construct a new String from a mix of constants or variables. Each variable we insert into the string literal has to be wrapped in a pair of parentheses, prefixed by a backslash.

let age = 10
let description = "\(age) years old"
print(description) // prints "10 years old"

Swift' String interpolation is able to handle a variety of different data types automatically.

String indices & substrings

To access certain parts of a string or to modify it, Swift provides the Swift.Index type which represents the position of each Character in a String.

Each string comes with a startIndex and an endIndex. Using those we can derive any other index like this:

let hello = "hello world"
let index = hello.index(hello.startIndex, offsetBy: 6) // index with an offset of 6 characters
hello[index] // returns Character w
hello.prefix(upTo: index) // returns Substring "hello "

The above prefix(upTo:) method returns a Substring and not a String. Why? Strings are value types e.g. they get copied every time we pass or reassign them. This has the advantage that no one else is going to change it without our knowledge, but an efficiency disadvantage.

The Substring type is a more efficient string type that enables us to retrieve and pass substrings around without copying. Substrings are meant for short-term usage.

Conclusion

Swift provides a modern powerful API when working with Strings. Some parts however, for example the indices may seem complicated to use.

To simplify the indices usage, we could create some extensions to abstract away the implementation details. For example, the prefix method extension could look like this:

extension String {
func prefix(upTo: Int) -> String {
let toIndex = self.index(startIndex, offsetBy: upTo)
return String(self[..<toIndex])
}
}

The extension allows us to directly use the Int type instead of the Index type. Instead of creating an index, we can now pass in an integer hello.prefix(upTo: 5) to get a certain prefix of a string.

Related tags

Written by