OCMapper
OCMapper copied to clipboard
[[ObjectMapper sharedInstance] dictionaryFromObject:object] doesn't work if the object comes from a framework
I know that MESRecipePreviewRecipeIngredientViewModel
can be converted into a dictionary normally in a standard iOS app project. The moment I moved it to another private pod framework, I saw this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (MESRecipePreviewRecipeIngredientViewModel)'
*** First throw call stack:
(
0 CoreFoundation 0x0000000103ccac65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000103963bb7 objc_exception_throw + 45
2 CoreFoundation 0x0000000103ccab9d +[NSException raise:format:] + 205
3 Foundation 0x00000001035fafd0 _writeJSONValue + 689
4 Foundation 0x00000001035ff34d ___writeJSONArray_block_invoke + 130
5 CoreFoundation 0x0000000103c14026 __53-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]_block_invoke + 70
6 CoreFoundation 0x0000000103c13f5c -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 284
7 Foundation 0x00000001035ff262 _writeJSONArray + 264
8 Foundation 0x00000001035faf3c _writeJSONValue + 541
9 Foundation 0x00000001035ff46f ___writeJSONObject_block_invoke + 220
10 CoreFoundation 0x0000000103c3ecd5 __65-[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 85
11 CoreFoundation 0x0000000103c3ebec -[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:] + 236
12 Foundation 0x00000001035ff080 _writeJSONObject + 376
13 Foundation 0x00000001035faea6 _writeJSONValue + 391
14 Foundation 0x00000001035ff46f ___writeJSONObject_block_invoke + 220
15 CoreFoundation 0x0000000103c3ecd5 __65-[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 85
16 CoreFoundation 0x0000000103c3ebec -[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:] + 236
17 Foundation 0x00000001035ff080 _writeJSONObject + 376
18 Foundation 0x00000001035faea6 _writeJSONValue + 391
19 Foundation 0x00000001035ff46f ___writeJSONObject_block_invoke + 220
20 CoreFoundation 0x0000000103c3ecd5 __65-[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 85
21 CoreFoundation 0x0000000103c3ebec -[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:] + 236
22 Foundation 0x00000001035ff080 _writeJSONObject + 376
23 Foundation 0x00000001035faea6 _writeJSONValue + 391
24 Foundation 0x00000001035ff34d ___writeJSONArray_block_invoke + 130
25 CoreFoundation 0x0000000103c14026 __53-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]_block_invoke + 70
26 CoreFoundation 0x0000000103c13f5c -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 284
27 Foundation 0x00000001035ff262 _writeJSONArray + 264
28 Foundation 0x00000001035faf3c _writeJSONValue + 541
29 Foundation 0x00000001035ff34d ___writeJSONArray_block_invoke + 130
30 CoreFoundation 0x0000000103c14026 __53-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]_block_invoke + 70
31 CoreFoundation 0x0000000103c13f5c -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 284
32 Foundation 0x00000001035ff262 _writeJSONArray + 264
33 Foundation 0x00000001035faf3c _writeJSONValue + 541
34 Foundation 0x00000001035ff46f ___writeJSONObject_block_invoke + 220
35 CoreFoundation 0x0000000103c3ecd5 __65-[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:]_block_invoke + 85
36 CoreFoundation 0x0000000103c3ebec -[__NSDictionaryI enumerateKeysAndObjectsWithOptions:usingBlock:] + 236
37 Foundation 0x00000001035ff080 _writeJSONObject + 376
38 Foundation 0x00000001035faea6 _writeJSONValue + 391
39 Foundation 0x00000001035ff34d ___writeJSONArray_block_invoke + 130
40 CoreFoundation 0x0000000103c14026 __53-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]_block_invoke + 70
41 CoreFoundation 0x0000000103c13f5c -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 284
42 Foundation 0x00000001035ff262 _writeJSONArray + 264
43 Foundation 0x00000001035faf3c _writeJSONValue + 541
44 Foundation 0x00000001035facea -[_NSJSONWriter dataWithRootObject:options:error:] + 137
45 Foundation 0x00000001035fd76b +[NSJSONSerialization dataWithJSONObject:options:error:] + 345
46 React 0x0000000101d2b2ee RCTJSONStringify + 94
47 React 0x0000000101c99152 __70-[RCTContextExecutor executeJSCall:method
I.e. I was trying to use OCMapper
to convert my ViewModel
class into a dictionary that React Native can consume. RN is barfing because the object didn't get converted at all
what do you mean by doesn't work? What's the behavior?
Would it work if you update the code according to this PR? https://github.com/aryaxt/OCMapper/pull/34/files
@aryaxt Hi aryaxt, thanks for the super quick reply.
Yes I noticed the cause is definitely the bundle checking.
...
// For example when we are mapping an array of string, we shouldn't try to map the string objects inside the array
if ([NSBundle mainBundle] != [NSBundle bundleForClass:object.class] && [object class] != [NSArray class])
{
return object;
}
I think the pull request might go some way to fixing it... I'll need to try that.
@aryaxt I checked out the version from the pull request but it's not helped.
I don't understand why bundle checking is necessary at all. It really shouldn't matter whether the class that I'm trying to convert belongs in one bundle or another — it's just a class, right :S?
Plus, I'm using AppCode, not Xcode. When I debugged the comparison between mainBundlePath
and classBundlePath
, I found their values were WILDLY different:
mainBundlePath = {__NSCFString * | 0x7ff013d6eb20} "/Users/myusername/Library/Developer/CoreSimulator/Devices/4CCEBA75-D7CE-408F-8135-97927736A940/data/Containers/Bundle/Application/854F84EA-647C-4B99-A94B-E5E94186714B/MakeEatSeeRNUI_Example.app"
classBundlePath = {__NSCFString * | 0x7ff016005020} "/Users/myusername/Library/Caches/AppCode32/DerivedData/MakeEatSeeRNUI-8ccb4a77/Build/Products/Debug-iphonesimulator/MakeEatSeePresenters.framework"
So yeah, there's just no way this comparison could possibly succeed. This causes -dictionaryFromObject:object
to fail and return object
. Not very useful at all.
yeah the code to detect project-specific classes need to be reworked, right now it only works if the models are in the main bundle
To understand the reason behind that logic, you can pull my code, comment out the code, and run the unit tests. Fell free to open a PR if you find a solution. I'll look into it myself as well
Hmmm. The relevant test in question is this:
- (void)testShouldMapArrayOfStringFromObjectToDictionary
{
User *user = [[User alloc] init];
user.randomKeywords = @[@"keyword1", @2].mutableCopy;
NSDictionary *dictionary = [self.mapper dictionaryFromObject:user];
NSArray *array = [dictionary objectForKey:@"randomKeywords"];
XCTAssertTrue(array.count == 2);
XCTAssertTrue([array[0] isEqualToString:@"keyword1"]);
XCTAssertTrue([array[1] isEqualToNumber:@2]);
}
:/ the implementation appears to make quite a lot of assumptions about what classes belong in bundles and which don't. I've got a wild idea: how about check for the NS
prefix from the class name instead?
Let me think about the NS prefix, maybe it could be in additions to bundle checking. Bundle checking is very reliable as long as your models are in the main bundle
@aryaxt Yeah I gave just checking for the NS
prefix a quick try, but one of the tests still failed.
@fatuhoku Did you manage to find a solution? I haven't had time to look into this
@aryaxt Yes — I just used HRCoder + AutoCoding to serialise the JSON out for React Native consumption instead! I'm not sure how they solve the cross-bundle issue. Probably worth checking the HRCoder
code.