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?
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.
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).
Seems like a bug, possibly related to how Swift strings handle characters differently than NSString.
I would file a radar.
DispatchQueue.main.async { let text = yourHtmlText.htmlDecode() }
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
}
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 months ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSTextContentStorage: Inconsistent element cache state. Elements for range {0, 36} are not cached while trying to insert'
terminating with uncaught exception of type NSException
The app crashes with the above error.
I'm extracting the attributed string from the html, and then assigning it to textView. Here's the code which I'm using whenever there is a call to my method updateView.
func displayTextView(with text: String) {
text.attributedStringFromHTML{ attrText in
self.textView.attributedText = attrText
}
}
public func attributedStringFromHTML(completionBlock: #escaping (NSAttributedString?)->()) {
DispatchQueue.main.async {
if let attributedString = try? NSAttributedString(data: customUtfData, options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue,],
documentAttributes: nil) {
completionBlock(attributedString)
} else {
print("Unable to create attributed string from html string: \(self)")
completionBlock(nil)
}
}
}
PS: Here customUtfData, is customised to replace unknown tags and is working fine.
App, is crashing intermittently. It's working fine for most of the cases.
Can someone guide me here?
Thanks!!
I've got a released app using Realm and there are some crash logs showing that there is sometimes a failure to create the realm with a configuration resulting in a EXC_BREAKPOINT (SIGTRAP) crash. (there's 9 crash files for a few hundred app installations so its not something which is happening frequently)
#objc class Database : NSObject
{
let configuration = Realm.Configuration(encryptionKey: Database.getKey() as Data)
var transactionRealm:Realm? = nil
override init()
{
let realm = try! Realm(configuration: configuration) // Crash here
<snip>
}
// This getKey() method is taken from the Realm website
class func getKey() -> NSData {
let keychainIdentifier = "Realm.EncryptionKey.AppKey"
let keychainIdentifierData = keychainIdentifier.data(using: String.Encoding.utf8, allowLossyConversion: false)!
// First check in the keychain for an existing key
var query: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
kSecAttrKeySizeInBits: 512 as AnyObject,
kSecReturnData: true as AnyObject
]
// To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
// See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
return dataTypeRef as! NSData
}
// No pre-existing key from this application, so generate a new one
let keyData = NSMutableData(length: 64)!
let result = SecRandomCopyBytes(kSecRandomDefault, 64, keyData.mutableBytes.bindMemory(to: UInt8.self, capacity: 64))
assert(result == 0, "REALM - Failed to get random bytes")
// Store the key in the keychain
query = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
kSecAttrKeySizeInBits: 512 as AnyObject,
kSecValueData: keyData,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
]
status = SecItemAdd(query as CFDictionary, nil)
assert(status == errSecSuccess, "REALM - Failed to insert the new key in the keychain")
return keyData
}
Here's the relevant portion of the crash file:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000103c0f30c
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [0]
Triggered by Thread: 0
Thread 0 name:
Thread 0 Crashed:
0 libswiftCore.dylib 0x0000000103c0f30c 0x103ab4000 + 1422092
1 libswiftCore.dylib 0x0000000103c0f30c 0x103ab4000 + 1422092
2 libswiftCore.dylib 0x0000000103b13d2c 0x103ab4000 + 392492
3 libswiftCore.dylib 0x0000000103b13bf4 0x103ab4000 + 392180
4 My app 0x000000010334ff14 _TFC14Caller_Name_ID8DatabasecfT_S0_ + 1648 (Database.swift:21)
5 My app 0x0000000103330624 -[Model createDatabase] + 200 (Model.m:271)
Line 21 of the Database.swift file is the one indicated in the code above in the Init() method.
If the reason for the Realm crash is because getKey() fails, then why would that be failing? If thats not the cause, that what are other reasons for the failure?
And is there anything the code can do (such as retry to create the Realm object) if there is a failure?
The init method
I'm going to get this out of the way first: It's unlikely that this has anything to do with your crash in this instance, but when you override any init method, you should always call super's version of that init method unless there is no super class or no available superclass implementation to call. In Objective-C you assign the results of [super init] to self, however, this pattern was not adopted by swift and it is unclear, from Apple's documentation, what happens when you call super.init() from a direct NSObject subclass in Swift. I'm running out of steam at this hour, thought I did spend some time looking at the apple/swift GitHub repository Apple/Swift Github among other place and was unable to find any really satisfying information about calling NSObject init from a Swift subclass. I would definitely encourage you to continue the search if you really want to know more and not just take my word for it. Until then, it is highly unlikely that it will cause issues if you call NSObject's init() from a Swift subclass and it just might save your butt at some point too! ;)
The crash
If the reason for the Realm crash is because getKey() fails, then why would that be failing?
I'm not sure why getKey() might be failing, but it's unlikely this is the cause of your crash. I believe default values for properties are available by the time code inside init() is called in which case your app would crash before it reached that call inside init(). I will do a little more research on this tomorrow.
If thats not the cause, that what are other reasons for the failure?
I've looked at the Realm API you are using there and it's unclear why the call to Realm(configuration:) is failing although clearly that's expected to be possible since the call can throw. However, the documentation doesn't state the conditions under which it will throw.
And is there anything the code can do (such as retry to create the Realm object) if there is a failure?
Yes! Fortunately, the "try" mechanism has other variation than just "try!". Actually, in this case, the reason the app appears to be crashing is that you are using try! which in production code should only be used in situations where you know the call is extremely unlikely to ever fail such as retrieving a resource your app ships with from inside it's app package. Per Apple's documentation:
Sometimes you know a throwing function or method won’t, in fact, throw an error at runtime. On those occasions, you can write try! before the expression to disable error propagation and wrap the call in a runtime assertion that no error will be thrown. If an error actually is thrown, you’ll get a runtime error. Swift Error Handling Documentation
When it comes to impacting the user experience, I always like to err on the side of caution so I would be highly surprised to find even one occurrence of try! in any of my code (even Swift playground toys and experiments).
In order to fail gracefully and retry or take another course of action your code either needs to use try? which will convert the thrown error to an optional as in:
if let realm = try? Realm(configuration: configuration) {
// Do something with realm and ignore the error
}
Or you can use a full try-catch mechanism like so:
let realm: Realm? = nil
do {
realm = try Realm(configuration: configuration)
// do something with realm
} catch let e {
realm = nil
// do something with the error
Swift.print(e)
}
Or alternatively:
do {
let realm = try Realm(configuration: configuration)
// do something with realm
} catch let e {
// do something with the error
Swift.print(e)
}
So this may not be your golden ticket to never having this Realm call fail, but I hope it provides some help towards making your code a little more robust. I apologize for any errors, it's getting late for me here. Good luck and cheers! :)
My iOS app is crashing on iOS 8.2 and working perfectly on iOS 9 and above.
When I checked the crash log, the following function causes the crash (Written in the NSMutableAttributedString extension)
func changeFont(font: UIFont, range : NSRange, keepFontSize: Bool = false)
{
let traits = font.fontDescriptor().symbolicTraits
let descriptor = baseDescriptor.fontDescriptorWithSymbolicTraits(traits)
let newFont = UIFont(descriptor: descriptor, size: keepFontSize ? descriptor.pointSize : baseDescriptor.pointSize)
self.removeAttribute(NSFontAttributeName, range: range)
self.addAttribute(NSFontAttributeName, value: newFont, range: range) // <- Crash happens here
}
Crash Log
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'NSConcreteMutableAttributedString addAttribute:value:range:: nil
value'
When I debugged and logged the variables used in that function, the descriptor & newFont both shown as <uninitialized>
(lldb) po traits
▿ UIFontDescriptorSymbolicTraits
- rawValue : 268435458
(lldb) po baseDescriptor
UICTFontDescriptor <0x7fc075b574c0> = {
NSFontNameAttribute = CenturyGothic;
NSFontSizeAttribute = 14;
}
(lldb) po descriptor
<uninitialized>
(lldb) po keepFontSize
true
(lldb) po newFont
<uninitialized>
I can't use if let or newFont != nil check here, because both will throw an error saying "UIFont won't be nil".
How can I fix this crash ? (Or How can I check an object is initialized or not ?) I'm really stuck on this issue, since majority of my users still use iOS 8.
I am developing an iOS App that fetches videos from the Photo gallery and it always worked. I just tested the app on a different device (this is not the first on which I test) and it crashes while I use the retrieved data and I don't understand why...
Here is my code :
self.videosAssets = PHAsset.fetchAssetsWithMediaType(.Video, options: nil)
if self.videosAssets != nil {
for i in 0..<self.videosAssets!.count {
if let video = self.videosAssets!.objectAtIndex(i) as? PHAsset {
self.videos.append(Video(asset: video))
}
}
}
It fetches 221 videos but it crashes when i == 59.
Here is the error I get :
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
self.videosAssets = PHAsset.fetchAssetsWithMediaType(.Video, options: nil)
if let videoAssets = self.videosAssets {
videoAssets.forEach { video in
if video as? PHAsset { self.videos.append(Video(asset: video)) }
}
}
After testing the Video constructor, it was the problem. For some reason, when I call let resources = PHAssetResource.assetResourcesForAsset(asset), it returns me an empty array and this is where the app was crashing.
Sorry for this useless post, maybe it will help someone...
Facing an issue with xcode.
I'm trying to develop an app that gives me the weather info. The build succeeds, but everytime I click on the console to check the output (to search, copy etc) xcode crashes.
The following text comes from the reporting tool to Apple,
Application Specific Information:
ProductBuildVersion: 7C1002
UNCAUGHT EXCEPTION (NSRangeException): -[__NSCFString characterAtIndex:]: Range or index out of bounds
UserInfo: (null)
Hints: None
Here's the code I'm running,
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string:"http://www.weather-forecast.com/locations/Hyderabad/forecasts/latest")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) -> Void in
if let webContent = data {
let decodedContent = NSString(data: webContent, encoding: NSUTF8StringEncoding)
//print(decodedContent)
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
print(weatherSiteSourceArray)
// if weatherSiteSourceArray?.count > 0 {
//
// let weatherInfo = weatherSiteSourceArray![1]
// print(weatherInfo)
// }
}
}
task.resume()
// Do any additional setup after loading the view, typically from a nib.
}
though the report tool informs me UNCAUGHT EXCEPTION (NSRangeException): -[__NSCFString characterAtIndex:]: Range or index out of bounds.
I am unable to figure out where this is happening.
From what i have learned you can print an array in Swift using the print(items: Any...) method.
help on this would be greatly appreciated !
You are checking is there any element in your array and then you are trying to read second element. There are chances that your array is having only one item in it and hence when you are choosing weatherSiteSourceArray![1], it fails as accessing element 2 is out of bound of array.
if weatherSiteSourceArray?.count > 0 {
let weatherInfo = weatherSiteSourceArray![1]
Either check for
if weatherSiteSourceArray?.count > 1
or use first element i.e.
let weatherInfo = weatherSiteSourceArray![0]
I have no idea of swift and assume it uses zero based index for array.
A good place to start would be to figure out exactly which line is causing the crash. Try commenting out these three lines:
let decodedContent = NSString(data: webContent, encoding: NSUTF8StringEncoding)
//print(decodedContent)
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
print(weatherSiteSourceArray)
And if you code doesn't crash after that, then try commenting in you code one line at the time until you have the line crashing.
Another thing:
Looking at your code I'm guessing that maybe this line:
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
might be the one causing your problems. Its a bit risky and brittle to try separating your content based on an HTML string like that. The second that string changes (and it will :-)), you've lost.
A better way would be to receive the data in pure JSON or XML if they have an API providing that.
Just a thought :-)