Extracting a character or sub-string in the Swift programming language may seem less intuitive for programmers familiar with Java or C. One interesting difference is the CharacterView to access the length (count) of a string such as aString.characters.count. Swift String includes subscript notation by String.Index instead of an Integer, and the substring takes a Range or ClosedRange as argument. Let’s go over some code to see it in action.

updated for Swift 5

Swift 5: sub-string
import UIKit
let aString = "A classic string, \"Hello World!\""
// need to use String.Index
let fifthChar = aString[aString.index(aString.startIndex, offsetBy: 5)] // s
// substring deprecated
//let suffix = aString.substring(from: aString.index(aString.startIndex, offsetBy: 18))
let suffix = aString.firstIndex(of: "\"")!
let start = aString.index(aString.startIndex, offsetBy: 19)
var end = aString.index(aString.startIndex, offsetBy: 23)
// get a sub-string with a ClosedRange
let range = start...end
let substring = aString[range] // Hello
// replace by text matching is easy enough
let byeStr = aString.replacingOccurrences(of: substring, with: "Goodbye,").replacingOccurrences(of: "classic", with: "dire")
// replace by Range
end = aString.index(aString.startIndex, offsetBy: 24)
let bString = aString.replacingCharacters(in: Range(uncheckedBounds: (lower: start, upper: end)), with: "Hi,")

Accessing a character within a string appears a bit verbose. It helps to think of a String in Swift as not an array of characters, but as Unicode scalars, accessible with String.unicodeScalars. String.Index keeps track of the character indices. As you can see from the example above where I updated the end variable, ClosedRange includes end boundry and Range does not.

Of course, one could extend String to allow using Integer subscript. Keep in mind that in some situations (Unicode or character?) this may add ambiguity. Swift wants us to be explicit.

Swift 5: sub-string
// extend String to enable sub-script with Int to get Character or sub-string
extension String
{
subscript (i: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: i)]
}
// for convenience we should include String return
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
let start = self.index(self.startIndex, offsetBy: r.lowerBound)
let end = self.index(self.startIndex, offsetBy: r.upperBound)
return String(self[start...end])
}
}
// Now we can use integer subscripts with our extension
let ch: Character = aString[15] // g
let subStr1 = aString[19..<23] // Hello