Dealing with strings in Swift
One of the first weird things you might notice when you start to code in Swift is the new way to manipulate strings. There isn’t an [NSString class]
anymore… oh, my!
So, how can we deal with strings? First thing first: what is a string in Swift? As the Apple says:
A string is a series of characters, such as
"hello, world"
or"albatross"
. Swift strings are represented by theString
type. The contents of aString
can be accessed in various ways, including as a collection ofCharacter
values.
String as a type!! A type like in many other programming languages? Ok, I can live with this :stuck_out_tongue_winking_eye:.
Anyway, Apple has bridged Swift’s String
type with Foundation’s NSString
class. That means if you import the Foundation framework in your Swift project you can use the entire NSString
API on any String
value you create. Also, you can use the String
type in calls to any API that requires NSString
parameters.
Regardless, managing strings in Swift still seems strange at first. As an example, you can consider the case when you need to know a string length. In old Objective-C you type something like:
NSString *myString = @"Swift rocks!";
int myStringLen = [string length];
But in Swift, you don’t have a method length
for the new String
type. As I quote above the new String
type ‘can be accessed as a collection of Character
values’. Then we can get the string length like this:
let myString = "Swift rocks!"
let myStringLen = string.characters.count
Cool, right? Let’s go deep in this ‘characters’ thing.
Working with Characters
Like in other programming languages, we can use subscripting syntax to access every single character in a String
value. To do this, we must use the String.Index
type associated with our String
value.
Why not using a simple integer as the index? Well, as you possibly know, Swift allows us to use Unicode characters in our strings. Actually, Swift’s String
and Character
types are fully Unicode-compliant. For example, these two strings are equivalent:
// String literal
let myString = "The octocat! :octocat:"
// String from a characters array
let stringFromCharacters : [Character] = [ "T", "h", "e", " ", "o", "c", "t", "o", "c", "a", "t", "!", ":octocat:" ]
In fact, “Swift’s native String
type is built from Unicode scalar values. A Unicode scalar is a unique 21-bit number for a character or modifier”. This means that different Unicode characters can require a different quantity of memory to store, so in order to determine which Character
is at a given position, you need to iterate over each Unicode scalar from the start or end of that String
. For this reason, Swift strings cannot be indexed by integer values.
That’s why String.Index
has a bunch of useful properties and methods:
let myString = "The octocat! :octocat:"
let firstChar = myString[myString.startIndex]
// T
let lastChar = myString[myString.endIndex.predecessor()]
// :octocat:
let secondChar = myString[myString.startIndex.successor()]
// h
let tenthChar = myString[myString.startIndex.advancedBy(9)]
// a
myString[myString.endIndex] // error!!
myString[myString.endIndex.successor()] // error!!
Obviously, if you try to access a Character
at an index beyond the string’s range limits you get a runtime error, like in the last two sentences.
And the characters
property has an indices
property that can be used to create a Range
of all of the characters indexes in a string:
for index in myString.characters.indices {
print("\(myString[index]) ", terminator: "")
}
// prints "T h e o c t o c a t ! :octocat:"
When you understand how Swift’s strings works you can make fun code. As an example I’ve made a new Swift String extension with a bunch of useful methods to deal with strings:
let charsToTrim: [Character] = ["\n", "\r", "\t", " "]
extension String {
// Simple length shortcut:.
// Usage: "This is a string".length
var length: Int {
return characters.count
}
// TRIMMING strings
// LEFT trim
// Usage: " <- these spaces will be trimmed!".trimLeft()
func trimLeft() -> String {
if self.isEmpty {
return ""
}
var index = startIndex
while index < endIndex {
let char = self[index]
if (charsToTrim.indexOf(char) == nil) {
break
}
index = index.successor()
}
return self[index..<endIndex]
}
// RIGHT trim
// Usage: "Final spaces will be trimmed! ".trimLeft()
func trimRight() -> String {
if self.isEmpty {
return ""
}
var index = endIndex
while index >= startIndex {
index = index.predecessor()
let char = self[index]
if (charsToTrim.indexOf(char) == nil) {
break
}
}
return self[startIndex...index]
}
// BOTH SIDES trim
// Usage: " All side spaces will be trimmed! ".trim()
func trim() -> String {
return self.trimLeft().self.trimRight()
}
The trim functions can be implemented using [NSString class]
bridged through the Foundation framework. For example you can use stringByTrimmingCharactersInSet()
to trim a string.
Hope this helps someone. “Tor Dif smusma je” 🖖.