[TIMOB-25346] iOS: App-crash when using Ti.App.fireEvent to send nested JSON-payload in 6.2.0.GA+
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Critical |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2017-10-27T18:36:05.000+0000 |
Affected Version/s | Release 6.2.2, Release 6.2.0, Release 6.2.1 |
Fix Version/s | Release 6.3.0 |
Components | iOS |
Labels | consider-6.2.3, fireevent, json, regression, tiapp |
Reporter | Anthony Chung |
Assignee | Hans Knöchel |
Created | 2017-09-26T03:29:43.000+0000 |
Updated | 2017-10-30T21:45:54.000+0000 |
Description
Tested same app in 6.1.1. was working fine.
Error in titanium 6.2.0 and 6.2.2 sdk.
Has something significantly changed in how JSON is parsed for Ti.App.fireEvent in 6.2.0 sdk going forward?
situation... note no params are provided to the event name.
Ti.App.fireEvent("my_event_name");
This event is happening as a window closes, so it is hard to replicate.
Could the previous JSON parser handle empty dictionary params but not the new updated version?
Alternatively, if there is an error in the dictionary, is it that the previous json parser failed gracefully, but the new one causes an app crash?
Tested in iOS simulator and device...
Error log
---
[INFO] : Could not parse JSON. Error: Error Domain=NSCocoaErrorDomain Code=3840 "Badly formed object around character 364." UserInfo={NSDebugDescription=Badly formed object around character 364.}
[ERROR] : The application has crashed with an uncaught exception 'NSRangeException'.
[ERROR] : Reason:
[ERROR] : *** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray
[ERROR] : Stack trace:
[ERROR] : 0 CoreFoundation 0x0000000108abdaf3 __exceptionPreprocess + 147
[ERROR] : 1 libobjc.A.dylib 0x000000010807b141 objc_exception_throw + 48
[ERROR] : 2 CoreFoundation 0x0000000108ad527d -[__NSArray0 objectAtIndex:] + 93
[ERROR] : 3 mLearn 0x0000000100fb6001 -[AppModule fireEvent:] + 97
[ERROR] : 4 mLearn 0x0000000100fb64e1 -[AppModule fireEvent:withObject:] + 113
[ERROR] : 5 mLearn 0x0000000100e7f8a1 -[AppProtocolHandler handleAppToTiRequest] + 945
[ERROR] : 6 mLearn 0x0000000100e7fe1c -[AppProtocolHandler startLoading] + 188
[ERROR] : 7 CFNetwork 0x0000000105e3e206 ___ZN16CFURLProtocol_NS28_protocolInterface_startLoadEPK20_CFCachedURLResponse_block_invoke + 303
[ERROR] : 8 libdispatch.dylib 0x000000010ac55792 _dispatch_client_callout + 8
[ERROR] : 9 libdispatch.dylib 0x000000010ac3828d _dispatch_block_invoke_direct + 358
[ERROR] : 10 CFNetwork 0x0000000105dacf88 _ZN19RunloopBlockContext13_invoke_blockEPKvPv + 24
[ERROR] : 11 CoreFoundation 0x0000000108a10be4 CFArrayApplyFunction + 68
[ERROR] : 12 CFNetwork 0x0000000105dace5e _ZN19RunloopBlockContext7performEv + 132
[ERROR] : 13 CFNetwork 0x0000000105daccf8 _ZN17MultiplexerSource7performEv + 282
[ERROR] : 14 CFNetwork 0x0000000105dacb2b _ZN17MultiplexerSource8_performEPv + 65
[ERROR] : 15 CoreFoundation 0x0000000108a63c01 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
[ERROR] : 16 CoreFoundation 0x0000000108a490cf __CFRunLoopDoSources0 + 527
[ERROR] : 17 CoreFoundation 0x0000000108a485ff __CFRunLoopRun + 911
[ERROR] : 18 CoreFoundation 0x0000000108a48016 CFRunLoopRunSpecific + 406
[ERROR] : 19 CFNetwork 0x0000000105fc3f6a _ZL27_privateRunloopEmulationSetPv + 250
[ERROR] : 20 libsystem_pthread.dylib 0x000000010afe893b _pthread_body + 180
[ERROR] : 21 libsystem_pthread.dylib 0x000000010afe8887 _pthread_body + 0
[ERROR] : 22 libsystem_pthread.dylib 0x000000010afe808d thread_start + 13
-- End simulator log ---------------------------------------------------------
Hey there, here to help! Would you be able to provide a reproducible test-case? The error is
Badly formed object around character 364.
and looks like a malformed JSON payload. If you are able to print your payload, we may be able to track it down. So the JSON-parse-error is thrown from [here](https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/AppModule.m#L205), suggesting that the payload (second parameter, not included in your example, yet, only the event-name by now) could not be serialized (from object to JSON) and then parsed (from JSON to dictionary).payload data is hard to reproduce because it changes, but it looks like this. context is elearning scorm tracks. payload below... In this context, i am handling client meta data, which sometimes can be malformed. is there a way to catch and fail gracefully, rather than crash and burn :) {"tracks":[{"element":"cmi.core.total_time","value":"00:02:27.86"}],"userdata":{"16":{"cmi.student_preference.language":"","cmi.core._children":"student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time","cmi.core.entry":"resume","cmi.objectives._children":"id,score,status","cmi.core.total_time":"00:02:13.25","cmi.core.student_name":"Anthony Chung","cmi.core.score.min":"","cmi.student_data.mastery_score":"","cmi.student_data.time_limit_action":"","cmi.core.credit":"credit","cmi.core.session_time":"0000:00:14.61","cmi.core.lesson_location":"index.html#/list/0X1CkVUYKNPDve792clZ4TnlYbPj539b/preview","cmi.objectives._count":"0","cmi._children":"core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions","cmi.core.score.max":"","cmi.suspend_data":"{\"quizItems\":{},\"progress\":{\"V9JVXnfqV_wqghlbKAyvLy7RVYheTWyN\":[1,2,3,4,5],\"iZHn5EIGvP1ygmJ4PBXaLnfau5-LECH_\":[2,3,4,6,7],\"0X1CkVUYKNPDve792clZ4TnlYbPj539b\":[1]}}","cmi.interactions._children":"id,objectives,time,type,correct_responses,weighting,student_response,result,latency","cmi.evaluation.comments._children":"content,location,time","cmi.student_preference._children":"audio,language,speed,text","cmi.core.score.raw":"","cmi.student_data.max_time_allowed":"","cmi._version":"3.4","cmi.comments":"","cmi.student_preference.text":"0","cmi.launch_data":"scormdriver/indexAPI.html","cmi.interactions._count":"0","cmi.evaluation.comments._count":"0","cmi.core.lesson_mode":"normal","cmi.core.exit":"suspend","cmi.student_preference.speed":"0","cmi.student_preference.audio":"0","cmi.core.score._children":"raw,min,max","nav.event":"","cmi.core.lesson_status":"incomplete","cmi.student_data._children":"mastery_score,max_time_allowed,time_limit_action","cmi.core.student_id":"Anthony Chung"}},"scoid":16,"attempt":1,"offline":false,"type":"app:fromWebView:scorm","name":"save_tracks"}
previous version of titanium looks it fails gracefully. perhaps new json parse mechanism?
Well, we moved from an third-party library that did the JSON-parsing to the iOS-provided API which is more performant and up-to-date. So you are sending the event from a Ti.UI.WebView to be received by your app? If you can, send me the payload AND error (e.g.
Badly formed object around character 364
, so we can track down the character that is bugging it. Because it worked before, the proposed solution should be making it backwards-compatible instead of just ignoring it. Let me know if you can provide the above combination and thanks already for the feedback! *EDIT*: Saw your last comment. So it failed in previous versions as well, but without a crash? If so, we can fix that easily. Let me know!sorry, to clarify, it works in previous versions. when i switch to 6.2.2. the app crashes with the previous type of error without showing the payload... these are the 2 payloads you can test with when i switch back to 6.1.1, put these as the params next to the fireevent name "app:fromWebView:scorm". {"tracks":[{"element":"cmi.suspend_data","value":"{\"quizItems\":{},\"progress\":{\"V9JVXnfqV_wqghlbKAyvLy7RVYheTWyN\":[1,2,3,4,5],\"iZHn5EIGvP1ygmJ4PBXaLnfau5-LECH_\":[2,3,4,6,7],\"0X1CkVUYKNPDve792clZ4TnlYbPj539b\":[1]}}"}],"userdata":{"16":{"cmi.student_preference.language":"","cmi.core._children":"student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time","cmi.core.entry":"resume","cmi.objectives._children":"id,score,status","cmi.core.total_time":"00:02:13.25","cmi.core.student_name":"Anthony Chung","cmi.core.score.min":"","cmi.student_data.mastery_score":"","cmi.student_data.time_limit_action":"","cmi.core.credit":"credit","cmi.core.session_time":"0000:00:14.61","cmi.core.lesson_location":"index.html#/list/0X1CkVUYKNPDve792clZ4TnlYbPj539b/preview","cmi.objectives._count":"0","cmi._children":"core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions","cmi.core.score.max":"","cmi.suspend_data":"{\"quizItems\":{},\"progress\":{\"V9JVXnfqV_wqghlbKAyvLy7RVYheTWyN\":[1,2,3,4,5],\"iZHn5EIGvP1ygmJ4PBXaLnfau5-LECH_\":[2,3,4,6,7],\"0X1CkVUYKNPDve792clZ4TnlYbPj539b\":[1]}}","cmi.interactions._children":"id,objectives,time,type,correct_responses,weighting,student_response,result,latency","cmi.evaluation.comments._children":"content,location,time","cmi.student_preference._children":"audio,language,speed,text","cmi.core.score.raw":"","cmi.student_data.max_time_allowed":"","cmi._version":"3.4","cmi.comments":"","cmi.student_preference.text":"0","cmi.launch_data":"scormdriver/indexAPI.html","cmi.interactions._count":"0","cmi.evaluation.comments._count":"0","cmi.core.lesson_mode":"normal","cmi.core.exit":"suspend","cmi.student_preference.speed":"0","cmi.student_preference.audio":"0","cmi.core.score._children":"raw,min,max","nav.event":"","cmi.core.lesson_status":"incomplete","cmi.student_data._children":"mastery_score,max_time_allowed,time_limit_action","cmi.core.student_id":"Anthony Chung"}},"scoid":16,"attempt":1,"offline":false,"type":"app:fromWebView:scorm","name":"save_tracks","cancelBubble":true} {"tracks":[{"element":"cmi.core.total_time","value":"00:02:27.86"}],"userdata":{"16":{"cmi.student_preference.language":"","cmi.core._children":"student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time","cmi.core.entry":"resume","cmi.objectives._children":"id,score,status","cmi.core.total_time":"00:02:13.25","cmi.core.student_name":"Anthony Chung","cmi.core.score.min":"","cmi.student_data.mastery_score":"","cmi.student_data.time_limit_action":"","cmi.core.credit":"credit","cmi.core.session_time":"0000:00:14.61","cmi.core.lesson_location":"index.html#/list/0X1CkVUYKNPDve792clZ4TnlYbPj539b/preview","cmi.objectives._count":"0","cmi._children":"core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions","cmi.core.score.max":"","cmi.suspend_data":"{\"quizItems\":{},\"progress\":{\"V9JVXnfqV_wqghlbKAyvLy7RVYheTWyN\":[1,2,3,4,5],\"iZHn5EIGvP1ygmJ4PBXaLnfau5-LECH_\":[2,3,4,6,7],\"0X1CkVUYKNPDve792clZ4TnlYbPj539b\":[1]}}","cmi.interactions._children":"id,objectives,time,type,correct_responses,weighting,student_response,result,latency","cmi.evaluation.comments._children":"content,location,time","cmi.student_preference._children":"audio,language,speed,text","cmi.core.score.raw":"","cmi.student_data.max_time_allowed":"","cmi._version":"3.4","cmi.comments":"","cmi.student_preference.text":"0","cmi.launch_data":"scormdriver/indexAPI.html","cmi.interactions._count":"0","cmi.evaluation.comments._count":"0","cmi.core.lesson_mode":"normal","cmi.core.exit":"suspend","cmi.student_preference.speed":"0","cmi.student_preference.audio":"0","cmi.core.score._children":"raw,min,max","nav.event":"","cmi.core.lesson_status":"incomplete","cmi.student_data._children":"mastery_score,max_time_allowed,time_limit_action","cmi.core.student_id":"Anthony Chung"}},"scoid":16,"attempt":1,"offline":false,"type":"app:fromWebView:scorm","name":"save_tracks","cancelBubble":true}
Okay, one proposal: Can you go to
~/Library/Application Support/Titanium/mobilesdk/osx/6.2.2.GA/iphone/Classes/TiUtils.m
and change line 2088 tooptions:NSJSONReadingAllowFragments
? The issue might be in thecmi.suspend_data
key, which holds json inside a json. I don't think we tested against that behavior using the new API, but the above option would handle it. I won't do a PR so far because the old value has it's reasons, but at least we'd know what's causing it and what can fix it. Thanks!hi. unfortunately the error is still occurring even after the change.
Ok, then I definitely need a test-project to reproduce this.
ok. i'll work on one. thanks for having a go.
I've been working on a reproducible test case but am having trouble making it make the crash. Finding it hard to reproduce.
Are you able to share your project in private? Feel free to send it to hknoechel@axway.com, thanks!
Hi. Thanks for offer. Sorry, I can't share the raw project. Your work so far helps me understand more about the TiUtils parser, thank you. I will see if I have more success reproducing a test case. It seems like as you suggested, when the json object has more complex depth and child objects, then the default parser will have more trouble. I think I might have to stringify the sub payload before it sends to be safe, and then unpack after it receives. Thanks for your help so far.
New angle. I encoded the params with javascript encode_utf8 function below and then it stopped crashing. function encode_utf8(s) { return unescape(encodeURIComponent(s)); } tiutils.m uses "value dataUsingEncoding: NSUTF8StringEncoding" +(id)jsonParse:(NSString*)value error:(NSError**)error; { return [NSJSONSerialization JSONObjectWithData: [value dataUsingEncoding: NSUTF8StringEncoding] options: NSJSONReadingMutableContainers error: error]; }
However, I may have made an error, since i encoded the object, rather than a string. so could be a false positive. The utf-8 angle is one that I hadn't thought of.
Alright. If you could provide the above requested (JSON-payload *and* error - which included the line number of the payload where it was malformed), it would definitely help. Are you already sure it's the sub-json that's causing the issue? Because I've created a test-case where I've put the ones you sent here into a JSON-file, read that, parsed it and provided it as a payload and it worked. Or are you trying to send the JSON-string as a payload? Unless we can find anything to reproduce, this issue would need to be resolved as "Cannot Reproduce" which I really want to prevent.
I've just created a reproducible test case. Am finding a place to upload test to.
Hi Hans, Here is the test case. https://github.com/anthonychung/jira-test-case-25346 Readme has notes. Look in assets for script.jslocal to see what fires the event. Cheers, Anthony
Update here: I received a test-case form [~kbrand] that reproduces the issue (thanks again):
The issue is definitely the json-payload inside the json itself, which is not properly serialized. After giving it a quick shot today, I was not unable to fix it so far and the native world does not offer an API to fix corrupt JSON except the old library we used (to serialize fragments). Although I already told Kristjan that it might be resolved as
Won't fix
, I will give it another shot tomorrow to scope the required change to fix it internally. Updating here again as soon as I have more.Hi [~hknoechel], Just checking if you saw the different test case that is different to Kristjan’s one. https://github.com/anthonychung/jira-test-case-25346 It uses a different payload, and works in 6.1.1, but not in 6.2.2. I was also wondering if it is actually utf-8 related, and had questions about how .jslocal might be related to this issue? Thanks, Anthony
PR (master): https://github.com/appcelerator/titanium_mobile/pull/9498 PR (6_3_X): https://github.com/appcelerator/titanium_mobile/pull/9499 Test-Case (crashes on 6.2.x, won't crash on 6.3.x and 7.0.0):
A side-note regarding the root-cause: The JSON-library we used before does the decoding of the event for us, which is currently not possible with the built-in iOS-API. This PR restores the old usage for this special use-case. Also to be considered for a 6.2.3.
FR Passed. Able to use nested JSON payload in fireEvent without issue. Tested using provided code as well as the webview and app suites.
[~threethirds] Have you had the chance to check the 6.3.0 RC to confirm the fix?
Hi [~hknoechel] I tried to run it using the latest .
This issue is likely caused by an incompatibility with the xcode that I need to run. Xcode8-3-3.app
[~hknoechel] This issue is confirmed to be fixed. The other crash is caused by not running the latest version of xcode.
Thanks [~hknoechel] for reverting to json parser with no crash for nested object!
And the other one is fixed for the 6.3.0.GA as well (open PR), thanks! I am wondering because we received another report about this not being fixed, so I'll ensure to test against that as well. *EDIT*: Simple reproducible case:
PR (master): Awaiting https://github.com/appcelerator/titanium_mobile/pull/9498 to be merged before PR (6_3_X): https://github.com/appcelerator/titanium_mobile/pull/9555 For QE: See test-case above. The code-change simply reverts a previous change which is okay because the old one was just a API-replacement.
PR's merged. changes are seen in the following builds: 6.3.0.v20171030133255 7.0.0.v20171030085253