Getting the length
A Swift string reports its length through the count property. It counts characters, not bytes:
var message = "Hello world"
print(message.count)
message += "!"
print(message.count)
11
12
It is a property, so there are no parentheses. The value follows the string: after += "!" the count is one higher. The empty string "" has a count of 0.
Because count counts what a reader sees as one character, an accented letter or an emoji counts as 1 even though it takes several bytes to store:
let text = "é🙂"
print(text.count)
print(text.utf8.count)
2
6
Swift has to scan the string to find character boundaries, so count takes time proportional to the length. If you need it several times, store it in a constant.
Reaching the end of a string
Strings are numbered from 0, so the last character sits at offset count - 1. Swift has no integer subscript, so you move from endIndex, which points one past the last character, by a negative offset:
let message = "Hello world"
let lastIdx = message.index(message.endIndex, offsetBy: -1)
print(message[lastIdx])
let secondLastIdx = message.index(message.endIndex, offsetBy: -2)
print(message[secondLastIdx])
d
l
The nth character from the end is message[message.index(message.endIndex, offsetBy: -n)].
The last property
The simplest way to read the last character is the last property. It returns an optional, Character?, because an empty string has no last character:
let word = "hello"
print(word.last as Any)
print(word.last!)
let empty = ""
print(empty.last as Any)
Optional("o")
o
nil
The ! force-unwraps the optional. It is fine when you know the string isn't empty, as in this lesson, but it crashes the program if the value is nil. When the string might be empty, use if let or a default with ?? instead.
A safe way to read it is if let, which runs its block only when there is a value:
let empty = ""
if let ch = empty.last {
print("last is", ch)
} else {
print("empty string")
}
empty string
The length is also how you guard an index. Before you move to an offset, check it against count: an offset is safe only when it is at least 0 and less than message.count. Skipping that check on a string that might be short is the usual cause of a crash.
The last n characters
The suffix(n) method returns the last n characters:
let message = "Hello world"
print(message.suffix(4))
print(message.suffix(7))
print(message.suffix(100))
orld
o world
Hello world
If n is larger than the string, you get the whole string, with no crash. The result is a Substring, which prints like a string. Use String(message.suffix(4)) when you need to store it as a String.
The exercise
The string is "I love pancakes". Three steps, each printing one line:
message.countgives the length. The string has 15 characters, spaces included.message.last!gives the last character,s.message.suffix(5)gives the last five characters,cakes.
let message = "I love pancakes"
print(message.count)
print(message.last!)
print(message.suffix(5))
15
s
cakes
Common mistakes
- Using
countas an index. The last valid offset iscount - 1. OffsettingstartIndexbycountlands onendIndex, which isn't a character, and reading it crashes the program. - Writing
count(). It is a property, not a method. - Forgetting the optional.
lastis aCharacter?. Without!orif let,printshowsOptional("s")rather thans. - Assuming bytes. The length of
"é"is 1 in characters but 2 inutf8.count. Usecountunless you really need bytes.
Key takeaways
string.countis the number of characters.string.lastis an optional,nilfor an empty string.string.suffix(n)returns the lastncharacters as aSubstring, and never crashes ifnis too big.- The last valid offset in a string is
count - 1.
