Você não é o único que não conseguiu encontrar a solução.
Stringnão implementa RandomAccessIndexType. Provavelmente porque eles habilitam caracteres com diferentes comprimentos de bytes. É por isso que precisamos usar string.characters.count( countou countElementsno Swift 1.x) para obter o número de caracteres. Isso também se aplica a posições. O _positioné provavelmente um índice para a matriz matéria de bytes e eles não querem expor isso. O String.Indexobjetivo é proteger-nos do acesso a bytes no meio de caracteres.
Isso significa que qualquer índice que obtiver devem ser criados a partir de String.startIndexou String.endIndex( String.Indeximplementos BidirectionalIndexType). Quaisquer outros índices podem ser criados usando métodos successorou predecessor.
Agora, para nos ajudar com os índices, há um conjunto de métodos (funções no Swift 1.x):
Swift 4.x
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 3.0
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 2.x
let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above
let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Swift 1.x
let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Trabalhar com String.Indexé complicado, mas usar um wrapper para indexar por números inteiros (consulte https://stackoverflow.com/a/25152652/669586 ) é perigoso porque oculta a ineficiência da indexação real.
Observe que a implementação da indexação Swift tem o problema de que índices / intervalos criados para uma sequência não podem ser usados com segurança para uma sequência diferente , por exemplo:
Swift 2.x
let text: String = "abc"
let text2: String = "🎾🏇🏈"
let range = text.rangeOfString("b")!
//can randomly return a bad substring or throw an exception
let substring: String = text2[range]
//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 1.x
let text: String = "abc"
let text2: String = "🎾🏇🏈"
let range = text.rangeOfString("b")
//can randomly return nil or a bad substring
let substring: String = text2[range]
//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]