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にクローズされました