These string operations in Swift can drive you crazy if you are used to a simple one from C#. So, I’ve been looking into it and I’ve put together a little bit here. I tested it with Swift 5.1
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. let str = "Hello, playground" print(str[str.startIndex]) // H //print(str[str.endIndex]) // error: after last character let rangeStartToEnd = str.startIndex..<str.endIndex print("Range: " + str [rangeStartToEnd]) // "Hello, playground" let rangeWithoutGround = str.startIndex..<str.index(of: "ground")! print("Range: " + str[rangeWithoutGround]) // "Hello, playground" }
before refers to the index of the character directly before the specified index.
// character let index = str.index(before: str.endIndex) str[index] // d // range let range = str.startIndex..<str.index(before: str.endIndex) str[range] // Hello, playgroun
The OffsetBy value may be positive or negative and starts from the specified index. Although it is of type String.IndexDistance, you can pass an Int value.
// character let index = str.index(str.startIndex, offsetBy: 7) str[index] // p // range let start = str.index(str.startIndex, offsetBy: 7) let end = str.index(str.endIndex, offsetBy: -6) let range = start..<end str[range] // play
The limitedBy is useful to ensure that the offset does not cause the index to exceed the limits. It is a limit index. Since it is possible for the offset to exceed the limit, this method returns an optional. It returns zero if the index is outside the limits.
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) { str[index] // p }
If the offset had been 77 instead of 7, the if statement would have been skipped.
It is possible to hide this complexity behind an Int index extension, but I hesitate to do so. It’s good to be reminded of what actually happens.
Translated with www.DeepL.com/Translator (free version)
The following Extenson must be added under your class in the code. This extension offers you the possibility to determine the index of a whole string within a string. In my example “ground”.String operations are thus addressed via indices and ranges. The index is therefore not a simple integer variable.
extension StringProtocol { func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? { range(of: string, options: options)?.lowerBound } func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? { range(of: string, options: options)?.upperBound } func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] { var indices: [Index] = [] var startIndex = self.startIndex while startIndex < endIndex, let range = self[startIndex...] .range(of: string, options: options) { indices.append(range.lowerBound) startIndex = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex } return indices } func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] { var result: [Range<Index>] = [] var startIndex = self.startIndex while startIndex < endIndex, let range = self[startIndex...] .range(of: string, options: options) { result.append(range) startIndex = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex } return result } }
About the author