UITextView Runtime Error Occurrence (attempt to create NSTextRange from nil location)

I encountered a runtime error in a project I'm working on, where I tapped a specific sentence to move the cursor position and then tried to insert a newline. When I pressed backspace to cancel the newline, the app crashed due to a runtime error.

Believing that the issue might not be with our project, I attempted the same actions in Apple's default Notes app and encountered a crash there as well.

This appears to be a UIKit bug... Can it be resolved? I will attach the video I tested this with and the text used for testing below.

Test Text:

https://shorturl.at/uISV7

Test Video:

https://github.com/leeari95/leeari95/assets/75905803/1665fd8e-73ea-4be0-9c02-abc682f8552b

Thank you for the reproduction sample! I do have the same crash reports for my app, but was unable to reproduce and thus debug it. With your sample I've set with the following workaround:

import UIKit

extension NSTextStorage {
  // Newline characters must not be followed by variation selector.
  // This invalid sequence sometimes appears in UITextView when adding newline
  // in certain combinations of characters.
  // For instance, if you insert this string:
  // [[string from https://shorturl.at/uISV7, blocked by Apple for some reason]]
  // then place cursor before first ❤️ and press return,
  // you get a string in which there's a substring "\nu{FE0F}"
  // for which UITextView by a series of calls tries to get NSTextLocation
  // which leads to objc exception being thrown.
  // This method fixed this by moving newline to where it belongs:
  // back to the first location that isn't variation selector
  // (presumably it's the symbol to which variation selector actually belongs).
  @objc fileprivate func fixInvalidVariationSelectorsAndProcessEditing() {
    struct InvalidNewline {
      var location: Int
      var desiredLocation: Int
    }

    var invalidLocations: [InvalidNewline] = []
    let utf16 = Array(string.utf16)
    let newline: UInt16 = 0x000A
    for index in utf16.indices.dropLast() {
      if utf16[index] == newline, utf16[index + 1].isVariationSelector {
        var properIndex = index - 1
        while properIndex > 0, utf16[properIndex].isVariationSelector == true {
          properIndex -= 1
        }

        invalidLocations.append(.init(location: index, desiredLocation: properIndex))
      }
    }

    invalidLocations.forEach {
      mutableString.deleteCharacters(in: NSRange(location: $0.location, length: 1))
      mutableString.insert("\n", at: $0.desiredLocation)
    }

    // Call original method which is swizzed by this
    fixInvalidVariationSelectorsAndProcessEditing()
  }

  static func fixInvalidVariationSelectors() {
    method_exchangeImplementations(
      class_getInstanceMethod(Self.self, #selector(fixInvalidVariationSelectorsAndProcessEditing))!,
      class_getInstanceMethod(Self.self, #selector(processEditing))!
    )
  }
}

extension UInt16 {
  fileprivate var isVariationSelector: Bool {
    Unicode.Scalar(self)?.properties.isVariationSelector == true
  }
}

I've tried my best to describe the origin of the crash and how it's fixed in the comments above the method.

There's still a minor issue though: after adding a newline in the buggy spot, trying to delete it using backspace leads to ❤️ being removed instead of newline (while the cursor is visually located at the left side of ❤️). I assume that there should be similar way to work around this bug too, but I didn't bother.

UITextView Runtime Error Occurrence (attempt to create NSTextRange from nil location)
 
 
Q