Get nth character of a string in Swift

character, collections, string, subscript, swift

Solution

Attention: Please see Leo Dabus' answer for a proper implementation for Swift 4 and Swift 5.

Swift 4 or later

The `Substring` type was introduced in Swift 4 to make substrings faster and more efficient by sharing storage with the original string, so that's what the subscript functions should return.

Try it out here

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

To convert the `Substring` into a `String`, you can simply do `String(string[0..2])`, but you should only do that if you plan to keep the substring around. Otherwise, it's more efficient to keep it a `Substring`.

It would be great if someone could figure out a good way to merge these two extensions into one. I tried extending `StringProtocol` without success, because the `index` method does not exist there. Note: This answer has been already edited, it is properly implemented and now works for substrings as well. Just make sure to use a valid range to avoid crashing when subscripting your StringProtocol type. For subscripting with a range that won't crash with out of range values you can use this implementation

Why is this not built-in?

The error message says "see the documentation comment for discussion". Apple provides the following explanation in the file UnavailableStringAPIs.swift:

Subscripting strings with integers is not available.

The concept of "the `i`th character in a string" has different interpretations in different libraries and system components. The correct interpretation should be selected according to the use case and the APIs involved, so `String` cannot be subscripted with an integer.

Swift provides several different ways to access the character data stored inside strings.

`String.utf8` is a collection of UTF-8 code units in the string. Use this API when converting the string to UTF-8. Most POSIX APIs process strings in terms of UTF-8 code units.

`String.utf16` is a collection of UTF-16 code units in string. Most Cocoa and Cocoa touch APIs process strings in terms of UTF-16 code units. For example, instances of `NSRange` used with `NSAttributedString` and `NSRegularExpression` store substring offsets and lengths in terms of UTF-16 code units.

`String.unicodeScalars` is a collection of Unicode scalars. Use this API when you are performing low-level manipulation of character data.

`String.characters` is a collection of extended grapheme clusters, which are an approximation of user-perceived characters.

Note that when processing strings that contain human-readable text, character-by-character processing should be avoided to the largest extent possible. Use high-level locale-sensitive Unicode algorithms instead, for example, `String.localizedStandardCompare()`, `String.localizedLowercaseString`, `String.localizedStandardRangeOfString()` etc.

Problem

How can I get the nth character of a string? I tried bracket(`[]`) accessor with no luck. ``` var string = "Hello, world!" var firstChar = string[0] // Throws error ``` ERROR: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion

Original source

Related problems