Closed4
iOS 17のテキスト操作でクラッシュ

RxCocoaで受け取ったUITextViewのテキストに対し、NSRangeの範囲指定で特定の文字列を削除するようなコードでiOS 17限定でクラッシュがではじめた
コードは以下のようなもの(そもそもこのコードがよくない問題は置いておいて…)
static func removeCharacter(text: NSAttributedString?, startIndex: String.Index?, endIndex: String.Index?) -> NSAttributedString? {
guard let text, text.string.isNotEmpty, let startIndext, let endIndex else { return text }
let newText = NSMutableAttributedString(attributedString: text)
let targetIndex = text.string.index(startIndex, offsetBy: -1)
newText.replaceCharacters(in: NSRange(targetIndex..<endIndex, in: newText.string), with: "")
return newText
}

Rxが関係しているかわからないが、どうやらiOS 17の特定の条件でindexメソッドを呼び出すと、utf16
のString.Indexが強制的にutf8
になってしまうような動きをしていた。
しかも、index(_:offsetBy:)
のように変更を加える際に、utf16としてオフセットを加えた後にutf8に変換されるわけではなく、オフセットの対象はutf8に変換されたものでオフセットの値はutf16のものとなり意図したものとは別のインデックスを示すようになってしまっていた。
...
// startIndex String.Index 2[utf16]
let targetIndex = text.string.index(startIndex, offsetBy: -1)
// targetIndex String.Index 3[utf8]
newText.replaceCharacters(in: NSRange(targetIndex..<endIndex, in: newText.string), with: "")
// targetIndex..<endIndexが3..<2となりクラッシュ
...
}

iOS 16では同じコードに対し同じ操作を行っても発生しない

startIndex, endIndexそれぞれにindex(index, offsetBy: 0)
を行い、エンコーディングを揃えることで対応した
let fixedStartIndex = text.string.index(startIndex, offsetBy: 0) // utf8
let fixedEndIndex = text.string.index(endIndex, offsetBy: 0) // utf8
let targetIndex = text.string.index(fixedStartIndex, offsetBy: -1) // utf8
このスクラップは2023/09/20にクローズされました