38

I have found several crash reports with the reason unexpected start state. My code looks like this:

NSRange range = [content rangeOfString:@"<html>"];

if (range.location != NSNotFound) {
    NSString *htmlStr = [content substringFromIndex:range.location];

    NSAttributedString *attStr = [[NSAttributedString alloc] initWithData:[htmlStr dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} documentAttributes:nil error:nil];

    return attStr.string;
}

The crash report looks like this:

enter image description here

Ben Lachman
  • 3,083
  • 1
  • 28
  • 44
tom lider
  • 419
  • 4
  • 4

3 Answers3

8

The above crash happens when you try initializing a NSAttibutedString with html content from any other thread than the main thread.

So the solution here is to make sure the above NSAttributedString initialization is always called from the main thread.

DispatchQueue.main.async {
    let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [.documentType: NSAttributedString.DocumentType.html]
    let htmlString = try? NSAttributedString(data: htmlData, options: options, documentAttributes: nil)
}

Quoting the documentation:

The HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs). The HTML import mechanism is meant for implementing something like markdown (that is, text styles, colors, and so on), not for general HTML import.

Crt Gregoric
  • 392
  • 1
  • 5
  • 12
3

Just call your function in background thread:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    // here, call your function

    dispatch_async(dispatch_get_main_queue(), ^{
        // do updates on main thread
    });
});
Ganpat
  • 768
  • 3
  • 15
  • 30
  • 4
    Refer to documentation: ["The HTML importer should not be called from a background thread"](https://developer.apple.com/documentation/foundation/nsattributedstring/1524613-init) – shim Oct 18 '20 at 02:29
2

I've been seeing this crash report come through for my app for a while. I finally figured out that if I call [[NSAttributedString alloc] initWithData:options:documentAttributes:error:] and pass @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} for options while the app is in the background, I get the above exception about "unexpected start state".

In my case, I have a subclass of UITextView that calls a given block to update its attributed string when its trait collection changes, for light/dark theming purposes. When I suspend the app, iOS seems to want to redraw the interface with different traits or something like that, so all my custom controls get traitCollectionDidChange while I'm backgrounded.

I've fixed it by wrapping my call to that initWithData method in a @try block.

Tom Hamming
  • 10,577
  • 11
  • 71
  • 145