Fabio Poloni
Fabio Poloni

Reputation: 8371

NSAttributedString initialization throws NSRangeException

I wrote a simple extension to decode the html entities:

extension String {
    func htmlDecode() -> String {
        if let encodedData = self.data(using: String.Encoding.unicode) {
            let attributedString = try! NSAttributedString(data: encodedData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.unicode], documentAttributes: nil)
            return attributedString.string
        }
        return self
    }
}

Now it throws an error on the line if let attributedString …:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 2]'

And self is not nil or something, just a String like this:

self = (String) "...über 25'000 Franken..."

Where is this strange NSArray-exception coming from?

Upvotes: 11

Views: 2036

Answers (5)

kiroshan KiroS
kiroshan KiroS

Reputation: 1

DispatchQueue.main.async { let text = yourHtmlText.htmlDecode() }

Upvotes: 0

Blago
Blago

Reputation: 4737

In my case, this was happening because I was trying to instantiate a NSAttributedString from within a UICollectionViewCell that was in detached state (before it was inserted in the parent UICollectionView).

Upvotes: 2

ahaese
ahaese

Reputation: 489

A shot in the dark: do you ensure that the initialization happens on the main thread?

I had exactly the same problem. I noticed that in my case the exception occurs under reproducible conditions (animations in a UICollectionViewController), but I was unable to find the actual cause. My best guess is that it's a framework bug, so I'd too suggest you file a radar.

I ended up pre-rendering my HTML formatted strings into a cache (aka array) at a time where it works, and then load the NSAttributedStrings from it on demand.

Note though that this solution may not fit your use case, since I only have to deal with a limited amount of formatted strings at a time and hence know the expense of rendering them in advance.

Upvotes: 4

Fabio Poloni
Fabio Poloni

Reputation: 8371

I just run over this error with a different error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue unsignedIntegerValue]: unrecognized selector sent to instance 0x60000024b790'

And found a serious bug in this piece of code:

I was passing String.Encoding.unicode - a Swift value - to an Objective-C method that crashed the app. After using String.Encoding.unicode.rawValue the crash disappeared:

extension String {
    func htmlDecode() -> String {
        if let encodedData = self.data(using: String.Encoding.unicode) {
            if let attributedString = try? NSAttributedString(data: encodedData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.unicode.rawValue], documentAttributes: nil) {
                return attributedString.string
            }
        }
        return self
    }
}

Upvotes: -1

Aaron Brager
Aaron Brager

Reputation: 66242

Seems like a bug, possibly related to how Swift strings handle characters differently than NSString.

I would file a radar.

Upvotes: 0

Related Questions