How can I gave custom Index to String.Index working with unicode in Swift?

I want gave a custom Index to my code which that means the custom String/Character to read unicodeScalars for that String in that spacial position, for example this down code works with string.startIndex but I could not change it the index that I wanted, even the codes gave error in runtime for string.endIndex So I need help, right now this down code works just for index zero of string, for example I want this work for index = 1 that means B , how can I do this?
UPSers Login

let string: String = "ABC"
let UInt32Value: UInt32 = string.unicodeScalars[string.startIndex].value
print(UInt32Value)

You're right that String, as a Collection, refers to position using index. However, Swift makes a distinction between index and offset. What you're thinking of is an offset, which starts at zero and increases by one after every item.

Collections don't use offsets. They instead use index, which does not need to start at zero, e.g. ArraySlice, and doesn't even need to be Int, e.g. String.

To move around, you start from a valid index (startIndex, endIndex) and use the Collection's APIs, such as index(after:) to create another, valid index. In your example, you want an item at offset one, you can fetch an index after the startIndex:

let index = string.index(after: string.startIndex)
string[index] // B
string.unicodeScalars[index] // 66

You can mutate the valid index in-place:

var index = string.startIndex
string.formIndex(after: &index)
string[index] // B

There's also a variation to move by multiple offsets:

let index1 = string.index(string.startIndex, offsetBy: 1)
var index2 = string.startIndex
string.formIndex(&index2, offsetBy: 1)
string[index1] // B
string[index2] // B

Here's a little something, String also adopts the concept of Views, which means that a valid String index is also a valid index for other collections in its possession, including unicodeScalars, utf8, utf16, etc. So you can (largely) interchanges indices between different views.

1 Like

Additionally, a lot of people get bogged down in indexes and manually manipulating them, but if you want to work in offsets, the best APIs to do that today are the SubSequence-based ones: prefix(_:), suffix(_:), dropFirst(_:), and dropLast(_:). These will automatically do the most efficient thing for the Collection you're working with. (One caveat to remember, though, is that they succeed even when you try to take/drop too many elements, leaving you with a slice that may be shorter than expected.)

let string = "ABC"
let scalarValue = string.unicodeScalars.dropFirst(/*1*/).first!
print(scalarValue)

I just want to clarify that a string and a string.unicodeScalars are two distinct collections:

import Foundation

let string: String = "ÁBÇ".decomposedStringWithCanonicalMapping

// String
let thirdCluster = string.index(string.startIndex, offsetBy: 2)
print(string[thirdCluster]) // Ç

// String.UnicodeScalarView
let thirdScalar = string.unicodeScalars.index(string.unicodeScalars.startIndex, offsetBy: 2)
print(string.unicodeScalars[thirdScalar]) // B

This is important, because in @Lantua’s examples, no index would ever point at the ◌́ or the ◌̧ in the above string, and the offsets may or may not mean what you intend.

3 Likes