Search code examples
htmlswiftcrashuitextviewnsattributedstring

iOS Swift UITextView NSAttributedString from HTML crashes when text in <table><tr><td> is too long


The code reads in (large) HTML files as String, performs some adaptations to the String, converts it to a NSAttributedString/NSMutableAttributedString and performs some concatenation/mutation operations on them again and finally displays the results in a UITextViews.

Problem

This works fine for most of the HTML files. But (recently) for some large(r) files it crashes with the following error:

Thread 1: "*** -[NSBigMutableString getCharacters:range:]: Range {7825, 1} out of bounds; string length 7811"

Relevant parts of the code:

let htmlText = try? String(contentsOfFile: filePath, encoding: String.Encoding.utf8)
...
htmlText.html(fontSize: fontSizeBody, textAlign: .natural, mainTextColor: Theme.Color.darkLight)
...
[Some NSMutableAttributedString concatenations/operations]
...
let attributedText = NSAttributedString(attributedString: mutableAttributedText)
print(attributedText) // => No problem here (the expected output is shown)
self.textView.attributedText = attributedText // => Crash, but just for large(r) files

If I delete some lines of the large(r) HTML file the code works as intended.

The question is: How to solve this issue?

More log output:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSBigMutableString getCharacters:range:]: Range {7825, 1} out of bounds; string length 7811'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010bd37604 __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x0000000109a84a45 objc_exception_throw + 48
    2   Foundation                          0x000000010a5acb8a -[NSBigMutableString replaceCharactersInRange:withString:] + 0
    3   UIFoundation                        0x0000000119d7e3e4 _NSFastFillAllLayoutHolesForGlyphRange + 923
    4   UIFoundation                        0x0000000119dd652a -[NSLayoutManager enumerateLineFragmentsForGlyphRange:usingBlock:] + 72
    5   UIKitCore                           0x000000011ce8d8a6 __38-[UITextView _rectForScrollToVisible:]_block_invoke + 141
    6   UIFoundation                        0x0000000119dd6c08 -[NSLayoutManager(TextLocking) coordinateAccess:] + 47
    7   UIKitCore                           0x000000011ce8d5dc -[UITextView _rectForScrollToVisible:] + 294
    8   UIKitCore                           0x000000011ce88a5c -[UITextView _updateFrameOfTrailingWhitespace:] + 115
    9   UIKitCore                           0x000000011ce88cb2 -[UITextView _updateContentSize] + 451
    10  UIKitCore                           0x000000011ce889d6 -[UITextView _textContainerSizeDidChange:] + 27
    11  CoreFoundation                      0x000000010bc7609d __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    12  CoreFoundation                      0x000000010bc76063 ___CFXRegistrationPost_block_invoke + 49
    13  CoreFoundation                      0x000000010bc755b0 _CFXRegistrationPost + 496
    14  CoreFoundation                      0x000000010bc74ee6 _CFXNotificationPost + 822
    15  Foundation                          0x000000010a66df4c -[NSNotificationCenter postNotificationName:object:userInfo:] + 82
    16  UIKitCore                           0x000000011ce999b4 __76-[_UITextContainerView _setFrameOrBounds:oldRect:isFrameRect:settingAction:]_block_invoke.71 + 366
    17  UIKitCore                           0x000000011ce997c0 -[_UITextContainerView _setFrameOrBounds:oldRect:isFrameRect:settingAction:] + 733
    18  UIKitCore                           0x000000011ce99c2a -[_UITextContainerView setFrame:] + 161
    19  UIKitCore                           0x000000011ce99e62 -[_UITextContainerView setConstrainedFrameSize:] + 488
    20  UIFoundation                        0x0000000119d7b459 -[NSLayoutManager(NSPrivate) _resizeTextViewForTextContainer:] + 1084
    21  UIFoundation                        0x0000000119d7aede -[NSLayoutManager(NSPrivate) _recalculateUsageForTextContainerAtIndex:] + 1908
    22  UIFoundation                        0x0000000119d7af4f -[NSLayoutManager(NSPrivate) _validatedStoredUsageForTextContainerAtIndex:] + 82
    23  UIFoundation                        0x0000000119dd1f70 -[NSLayoutManager usedRectForTextContainer:] + 118
    24  UIKitCore                           0x000000011ce991f9 -[_UITextContainerView textContainerOrigin] + 158
    25  UIFoundation                        0x0000000119e031e6 -[NSTextContainer textContainerOrigin] + 161
    26  UIKitCore                           0x000000011ce70743 -[UITextInputController caretRectForPosition:] + 610
    27  UIKitCore                           0x000000011ce90629 -[UITextView caretRectForPosition:] + 73
    28  UIKitCore                           0x000000011ce0bafb -[UITextSelection caretRect] + 162
    29  UIKitCore                           0x000000011ce8867d -[UITextView _scrollToCaretIfNeeded] + 352
    30  UIKitCore                           0x000000011ce91453 -[UITextView textInputDidChangeSelection:] + 90
    31  UIKitCore                           0x000000011ce66513 -[UITextInputController _sendDelegateChangeNotificationsForText:selection:] + 80
    32  UIKitCore                           0x000000011ce67e56 __46-[UITextInputController setSelectedTextRange:]_block_invoke.538 + 292
    33  UIKitCore                           0x000000011ce68227 __52-[UITextInputController _coordinateSelectionChange:]_block_invoke + 108
    34  UIFoundation                        0x0000000119e07541 -[NSTextStorage coordinateReading:] + 42
    35  UIKitCore                           0x000000011ce68191 -[UITextInputController _coordinateSelectionChange:] + 150
    36  UIKitCore                           0x000000011ce67b5e -[UITextInputController setSelectedTextRange:] + 535
    37  UIKitCore                           0x000000011ce67fe6 -[UITextInputController setSelectedRange:] + 92
    38  UIKitCore                           0x000000011ce91fa0 -[UITextView setSelectedRange:] + 67
    39  UIKitCore                           0x000000011ce87613 -[UITextView setAttributedText:] + 1006
  ...
)

UPDATE

I found out that the part in the HTML file that causes the crash is a table tag with very long String in one of its td tags:

  <table>
    <tr>
      <td>
        adsfsdfadsf:
      </td>
      <td>
          hkavdhsghasgdvfhjgasvdfhgasdvhjfgvashjgdfvjhagsvdfhjavsdhjfvajhsdgfhjagsvfjhagsvjdhfvajhsdgfvjhasgvdfjhgahkavdhsghasgdvfhjgasvdfhgasdvhjfgvashjgdfvjhagsvdfhjavsdhjfvajhsdgfhjagsvfjhagsvjdhfvajhsdgfvjhasgvdfjhga
      </td>
    </tr>
  </table>

Solution

  • I found out that the part in the HTML file that causes the crash is a table tag with very long String in one of its td tags:

     <table>
        <tr>
          <td>
            adsfsdfadsf:
          </td>
          <td>
              hkavdhsghasgdvfhjgasvdfhgasdvhjfgvashjgdfvjhagsvdfhjavsdhjfvajhsdgfhjagsvfjhagsvjdhfvajhsdgfvjhasgvdfjhgahkavdhsghasgdvfhjgasvdfhgasdvhjfgvashjgdfvjhagsvdfhjavsdhjfvajhsdgfhjagsvfjhagsvjdhfvajhsdgfvjhasgvdfjhga
          </td>
        </tr>
      </table>
    

    The issue seems to appear whenever the text long text can't be displayed in one line. While this worked fine in the past, the problem occurs in newer iOS versions.

    To avoid this error:

    • omit using HTML tables
    • or use HTML delimiters in the text
    • or make the HTML table scrollable vertically