highcharts-ios
highcharts-ios copied to clipboard
Crash: Invalid number value (NaN) in JSON write
We are seeing this issue quite a few times on user devices, but cannot reproduce the issue on our devices. It looks like the wrapper prepares series data without checking if one value might be NSNull, NaN, or nil. Might be the same issue or related to https://github.com/highcharts/highcharts-ios/issues/91
Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x1ab03dc30 __exceptionPreprocess
1 libobjc.A.dylib 0x1aad580c8 objc_exception_throw
2 Foundation 0x1ab4b8a68 _writeJSONNumber
3 Foundation 0x1ab4b97e0 ___writeJSONObject_block_invoke
4 Foundation 0x1ab4b99b0 ___writeJSONObject_block_invoke_2
5 CoreFoundation 0x1ab08db20 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
6 CoreFoundation 0x1aaf0dd50 -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
7 Foundation 0x1ab4b8cd0 _writeJSONObject
8 Foundation 0x1ab4b9b00 ___writeJSONArray_block_invoke
9 CoreFoundation 0x1ab00f7c4 __NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__
10 CoreFoundation 0x1aaf0d424 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
11 Foundation 0x1ab4b919c _writeJSONArray
12 Foundation 0x1ab4b97e0 ___writeJSONObject_block_invoke
13 Foundation 0x1ab4b99b0 ___writeJSONObject_block_invoke_2
14 CoreFoundation 0x1ab08db20 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
15 CoreFoundation 0x1aaf0dd50 -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
16 Foundation 0x1ab4b8cd0 _writeJSONObject
17 Foundation 0x1ab4b9b00 ___writeJSONArray_block_invoke
18 CoreFoundation 0x1ab00f7c4 __NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__
19 CoreFoundation 0x1aaf0d424 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
20 Foundation 0x1ab4b919c _writeJSONArray
21 Foundation 0x1ab4b97e0 ___writeJSONObject_block_invoke
22 Foundation 0x1ab4b99b0 ___writeJSONObject_block_invoke_2
23 CoreFoundation 0x1ab08db20 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
24 CoreFoundation 0x1aaf0f938 -[__NSFrozenDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
25 Foundation 0x1ab4b8cd0 _writeJSONObject
26 Foundation 0x1ab361a34 -[_NSJSONWriter dataWithRootObject:options:error:]
27 Foundation 0x1ab3616cc +[NSJSONSerialization dataWithJSONObject:options:error:]
28 Highcharts 0x100a31a28 _hidden#4343_
29 Highcharts 0x100a7bc38 _hidden#5709_
30 Highcharts 0x100a013d4 _hidden#3459_
31 Highcharts 0x100a01838 _hidden#3463_
32 Foundation 0x1ab433ac4 __NSFireTimer
33 CoreFoundation 0x1aafb98d4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
34 CoreFoundation 0x1aafb960c __CFRunLoopDoTimer
35 CoreFoundation 0x1aafb8c80 __CFRunLoopDoTimers
36 CoreFoundation 0x1aafb3b28 __CFRunLoopRun
37 CoreFoundation 0x1aafb3098 CFRunLoopRunSpecific
38 GraphicsServices 0x1b511d534 GSEventRunModal
39 UIKitCore 0x1af0d37ac UIApplicationMain

Hello @skuske,
thank you for the reporting.
It's hard to say something without reproducing the issue. Do you think the problem is in series.data
?
series.data
may contain NSNull
and nil
values. Please see an example below:
let series = HISeries()
var c: Double?
series.data = [NSNull(), 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, c]
So, there is only one thing to check is a NaN
. Can your data contain the NaN
value?
Hello @ihnatmoisieiev
yes, the series data might contain NaN
. We retrieve data from a remote server, and sometimes missing data is null
, and I think it results in NaN
if converted to NSNumber
before checking if it is null
.
We have now integrated a check for null
values within the raw data and replace them with 0
before converting them to NSNumber
. That probably fixes it, but it might be a good idea to make the Highcharts wrapper check any incoming data for NaN
, too.
@skuske the main Highcharts library also doesn't have that functionality. So, you should check it by yourself while preparing the data.
@ihnatmoisieiev
Yes, we are doing that now. Thanks! :o)
@ihnatmoisieiev
I am reopening this issue as we need additional information on how to get this fixed.
Although we have implemented a lot of checks, we still see those NaN errors - actually we do check every data array for NSNull or NaN values and replace or remove them from the array, but the problem is still there:
Fatal Exception: NSInvalidArgumentException
Invalid number value (NaN) in JSON write
_hidden#4343_
Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x1a6143ab0 __exceptionPreprocess
1 libobjc.A.dylib 0x1a5e5d028 objc_exception_throw
2 Foundation 0x1a65bea28 _writeJSONNumber
3 Foundation 0x1a65bf7a0 ___writeJSONObject_block_invoke
4 Foundation 0x1a65bf970 ___writeJSONObject_block_invoke_2
5 CoreFoundation 0x1a61939e0 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
6 CoreFoundation 0x1a6013ca0 -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
7 Foundation 0x1a65bec90 _writeJSONObject
8 Foundation 0x1a65bfac0 ___writeJSONArray_block_invoke
9 CoreFoundation 0x1a61157b0 __NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__
10 CoreFoundation 0x1a6013374 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
11 Foundation 0x1a65bf15c _writeJSONArray
12 Foundation 0x1a65bf7a0 ___writeJSONObject_block_invoke
13 Foundation 0x1a65bf970 ___writeJSONObject_block_invoke_2
14 CoreFoundation 0x1a61939e0 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
15 CoreFoundation 0x1a6013ca0 -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
16 Foundation 0x1a65bec90 _writeJSONObject
17 Foundation 0x1a65bfac0 ___writeJSONArray_block_invoke
18 CoreFoundation 0x1a61157b0 __NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__
19 CoreFoundation 0x1a6013374 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
20 Foundation 0x1a65bf15c _writeJSONArray
21 Foundation 0x1a65bf7a0 ___writeJSONObject_block_invoke
22 Foundation 0x1a65bf970 ___writeJSONObject_block_invoke_2
23 CoreFoundation 0x1a61939e0 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__
24 CoreFoundation 0x1a6015888 -[__NSFrozenDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:]
25 Foundation 0x1a65bec90 _writeJSONObject
26 Foundation 0x1a64677f4 -[_NSJSONWriter dataWithRootObject:options:error:]
27 Foundation 0x1a646748c +[NSJSONSerialization dataWithJSONObject:options:error:]
28 Highcharts 0x100b71a28 _hidden#4343_
29 Highcharts 0x100bbbc38 _hidden#5709_
30 Highcharts 0x100b413d4 _hidden#3459_
31 Highcharts 0x100b41838 _hidden#3463_
32 Foundation 0x1a6539acc __NSFireTimer
33 CoreFoundation 0x1a60bf8c0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
34 CoreFoundation 0x1a60bf5f8 __CFRunLoopDoTimer
35 CoreFoundation 0x1a60bec6c __CFRunLoopDoTimers
36 CoreFoundation 0x1a60b9b14 __CFRunLoopRun
37 CoreFoundation 0x1a60b9084 CFRunLoopRunSpecific
38 GraphicsServices 0x1b0307534 GSEventRunModal
39 UIKitCore 0x1aa229670 UIApplicationMain
40 myapp 0x1003ace70 main + 120 (main.m:120)
41 libdyld.dylib 0x1a5f38e18 start
We doubt that this is actually coming from a series.data as we filtered out all NaN and al NSNull values before passing the data to Highcharts. However, since the stack trace does not give any hint, we need some additional information where exactly Highcharts crashes here.
What is actually happening in _hidden#4343
. Can someone shed some light on this, please?
Our NaN check looks like this:
[numValue isEqualToNumber:[NSDecimalNumber notANumber]]
(see here)
where numValue
is a NSNumber.
Many thanks.
@ihnatmoisieiev
In addition to my previous reply: further above you say
the main Highcharts library also doesn't have that functionality.
I checked the issues of the JS Highcharts library, and yes: it checks for null or nil, but not for NaN (https://github.com/highcharts/highcharts/issues/3571).
Why don't you make the iOS wrapper check for NaN? The crash occurs in writeJSONNumber
and it would be so easy to just check if the value is NaN before writeJSONNumber
is actually called. That would just fix it.
@ihnatmoisieiev
We have now even implemented an
isnan
check on every (!!!) value of the data array, but the problem still exists. Our data is free from any NaN
, NSNull
, or nil
value, but Highcharts still crashes. Not on our devices, but on our user's.
PLEASE (!) include an isnan
check before writeJSONObject
is called. I have no idea what we can do here to fix that issue as we have run out of options now. We have implemented so many checks, but the problem still exists.
And please reply. Many thanks.
@skuske
thanks for paying attention.
As you can see, writeJSONNumber
is a method from the Foundation
framework and we can't do anything with it.
Please, prepare some example data to reproduce the issue, because it is impossible at the moment.
As I mentioned here https://github.com/highcharts/highcharts-ios/issues/248#issuecomment-544372527, the main HC framework doesn't have this check.
I also will discuss what we can do for this topic and let you know.
@ihnatmoisieiev
But the Highcharts wrapper calls the writeJSONNumber
method. And before calling it with a specific value, it should just check if the value is NaN
or not... If it is NaN
, it should replace it with 0
or just skip the value...
I cannot provide a sample as the issue never appeared on our devices, but lots of users do see the issue, and then it disappears again. Therefore a simple check for NaN
by the wrapper would just be the logical consequence and the only possible fix for that.
@ihnatmoisieiev
In addition to my previous reply:
we do check the series.data array for NaN
and NSNull
or nil
values, and we do remove them all if they appear within the array. However, the issue is still there, and since we do not know where exactly the Highcharts wrapper calls writeJSONNumber
and for what specific property of the chart it calls it, there's just nothing we can do against the issue.
If the wrapper calls writeJSONNumber
just for the series data, then the issue would still be the data array. I think the wrapper calls it for all chart options all together and then it sends the data to the Highcharts core. And somewhere within the chart options there must be a NaN
value causing writeJSONNumber
to crash.
Even though Highcharts JS does not check for NaN
values: what is the reason that the iOS wrapper does not do that at least?
Performance can not be the real reason, because what's the advantage of a slightly better (but hardly noticeable) performance when it crashes on the other hand, just because you skip a NaN
check because of performance?
An empty chart (because of incomplete options) is still better than a crashing app. Don't you think so, too?
Hi @skuske, We are actually working on the new v8 version which should be available before Christmas. Let wait until new release and then get back to tests.
@sebastianbochan
That sounds somewhat promising ... :o)
@skuske does the issue still appear for you? Can I close it?
I am re-opening this issue because we see it happening again, although I am not sure if it's related to the latest release.
Anyway, I did some investigation and found out that dataWithJSONObject
only works if
The top level object is an NSArray or NSDictionary, unless you set the NSJSONWritingFragmentsAllowed option. All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull. All dictionary keys are instances of NSString. Numbers are neither NaN or infinity.
To avoid a crash, you can pass the object to be converted to JSON to isValidJSONObject
- see https://developer.apple.com/documentation/foundation/nsjsonserialization/1418461-isvalidjsonobject
I would suggest you include that check to the wrapper to avoid such crashes:
