marcuswestin / WebViewJavascriptBridge

An iOS/OSX bridge for sending messages between Obj-C and JavaScript in UIWebViews/WebViews
http://marcuswest.in
MIT License
14.29k stars 2.98k forks source link

How to get data asynchronously and transfer to javascript #41

Closed chenfengbri closed 11 years ago

chenfengbri commented 11 years ago

I used AFNetWorking to fetch data from remote service API, when the data ready, I tried to transfer the data back to javascript, but it raise an error. Any advice is appreciate. My code look like this.

^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"data: %@", data);
            NSString *service = [data objectForKey:@"service"];
            BOOL needLogin = [[data objectForKey:@"needLogin"] boolValue];
            NSDictionary *businessParams = [data objectForKey:@"businessParams"];

            [[AHAPIClient sharedClient] consumeRemoteService:service
                                                   needLogin:needLogin
                                              withParameters:businessParams
                                                     success:^(id JSON) {
                                                         responseCallback(JSON);
                                                     }
                                                     failure:^(NSError *error) {
                                                         responseCallback(error.userInfo);
                                                     }];
}
marcuswestin commented 11 years ago

I'll need more info to be able to help. For example, you should always post the error you are getting when asking for help. Please post code which I can easily run, e.g a tar file with an example Xcode project which exhibits the error.

​Cheers, Marcus 

— Sent from Mailbox for iPad

On Tue, Jun 4, 2013 at 10:20 PM, 陈锋 notifications@github.com wrote:

I used AFNetWorking to fetch data from remote service API, when the data ready, I tried to transfer the data back to javascript, but it raise an error. Any advice is appreciate. My code look like this.

^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"data: %@", data);
            NSString *service = [data objectForKey:@"service"];
            BOOL needLogin = [[data objectForKey:@"needLogin"] boolValue];
            NSDictionary *businessParams = [data objectForKey:@"businessParams"];

            [[AHAPIClient sharedClient] consumeRemoteService:service
                                                   needLogin:needLogin
                                              withParameters:businessParams
                                                     success:^(id JSON) {
                                                         responseCallback(JSON);
                                                     }
                                                     failure:^(NSError *error) {
                                                         responseCallback(error.userInfo);
                                                     }];
}
---
Reply to this email directly or view it on GitHub:
https://github.com/marcuswestin/WebViewJavascriptBridge/issues/41
chenfengbri commented 11 years ago

Sorry, here is the exception stack and I already put the demo project at https://github.com/chenfengbri/WVJB_ASYN_DEMO.git

Thanks for your help.

(lldb) bt
* thread #1: tid = 0x1c03, 0x012f509b libobjc.A.dylib`objc_msgSend + 15, stop reason = EXC_BAD_ACCESS (code=2, address=0x8)
    frame #0: 0x012f509b libobjc.A.dylib`objc_msgSend + 15
    frame #1: 0x00e14b82 Foundation`_writeJSONValue + 85
    frame #2: 0x00e19251 Foundation`___writeJSONObject_block_invoke_0 + 272
    frame #3: 0x01dcacdf CoreFoundation`__NSDictionaryEnumerate + 991
    frame #4: 0x01dca87d CoreFoundation`-[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock:] + 45
    frame #5: 0x01dca7c5 CoreFoundation`-[NSDictionary enumerateKeysAndObjectsUsingBlock:] + 53
    frame #6: 0x00e18e2a Foundation`_writeJSONObject + 458
    frame #7: 0x00e14d5d Foundation`_writeJSONValue + 560
    frame #8: 0x00e14aed Foundation`-[_NSJSONWriter dataWithRootObject:options:error:] + 125
    frame #9: 0x00e17ecc Foundation`+[NSJSONSerialization dataWithJSONObject:options:error:] + 370
    frame #10: 0x00040f21 WVJB_ASYN_DEMO`-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045667, message=0x0758e980) _serializeMessage:] + 129 at WebViewJavascriptBridgeAbstract.m:148
    frame #11: 0x0003ffb9 WVJB_ASYN_DEMO`-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045477, message=0x0758e980) _dispatchMessage:] + 217 at WebViewJavascriptBridgeAbstract.m:74
    frame #12: 0x0003feda WVJB_ASYN_DEMO`-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045658, message=0x0758e980) _queueMessage:] + 154 at WebViewJavascriptBridgeAbstract.m:69
    frame #13: 0x00040d86 WVJB_ASYN_DEMO`__64-[WebViewJavascriptBridgeAbstract(.block_descriptor=0x072a6dc0, responseData=0x072c56f0) _flushMessageQueue]_block_invoke + 230 at WebViewJavascriptBridgeAbstract.m:116
    frame #14: 0x000035db WVJB_ASYN_DEMO`__29-[ViewController viewDidLoad]_block_invoke_3(.block_descriptor=0x080d16b0, operation=0x072c0440, responseObject=0x072c56f0) + 123 at ViewController.m:35
    frame #15: 0x000193d9 WVJB_ASYN_DEMO`__64-[AFJSONRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke93() + 41 at AFJSONRequestOperation.m:140
    frame #16: 0x017d053f libdispatch.dylib`_dispatch_call_block_and_release + 15
    frame #17: 0x017e2014 libdispatch.dylib`_dispatch_client_callout + 14
    frame #18: 0x017d27d5 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 296
    frame #19: 0x01d83af5 CoreFoundation`__CFRunLoopRun + 1925
    frame #20: 0x01d82f44 CoreFoundation`CFRunLoopRunSpecific + 276
    frame #21: 0x01d82e1b CoreFoundation`CFRunLoopRunInMode + 123
    frame #22: 0x021707e3 GraphicsServices`GSEventRunModal + 88
    frame #23: 0x02170668 GraphicsServices`GSEventRun + 104
    frame #24: 0x00227ffc UIKit`UIApplicationMain + 1211
    frame #25: 0x00002d3d WVJB_ASYN_DEMO`main(argc=1, argv=0xbffff374) + 141 at main.m:16
    frame #26: 0x00002c65 WVJB_ASYN_DEMO`start + 53
marcuswestin commented 11 years ago

That's helpful - thanks!

The error appears to happen when attempting to JSON serialize the arguments you are passing through. Make sure that you are sending serializable values. Have you tried setting a breakpoint in your success handler and stepping through the code? My guess is that id JSON is nil or something. One way to check is to try

[[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject :JSON options:0 error:nil] encoding:NSUTF8StringEncoding]

before calling responseCallback(JSON).

On Wed, Jun 5, 2013 at 1:04 AM, 陈锋 notifications@github.com wrote:

Sorry, here is the exception stack and I already put the demo project at https://github.com/chenfengbri/WVJB_ASYN_DEMO.git

Thanks for your help.

(lldb) bt* thread #1: tid = 0x1c03, 0x012f509b libobjc.A.dylibobjc_msgSend + 15, stop reason = EXC_BAD_ACCESS (code=2, address=0x8) frame #0: 0x012f509b libobjc.A.dylibobjc_msgSend + 15 frame #1: 0x00e14b82 Foundation_writeJSONValue + 85 frame #2: 0x00e19251 Foundation_writeJSONObject_block_invoke_0 + 272 frame #3: 0x01dcacdf CoreFoundation`NSDictionaryEnumerate + 991 frame #4: 0x01dca87d CoreFoundation-[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock:] + 45 frame #5: 0x01dca7c5 CoreFoundation-[NSDictionary enumerateKeysAndObjectsUsingBlock:] + 53 frame #6: 0x00e18e2a Foundation_writeJSONObject + 458 frame #7: 0x00e14d5d Foundation_writeJSONValue + 560 frame #8: 0x00e14aed Foundation-[_NSJSONWriter dataWithRootObject:options:error:] + 125 frame #9: 0x00e17ecc Foundation+[NSJSONSerialization dataWithJSONObject:options:error:] + 370 frame #10: 0x00040f21 WVJB_ASYN_DEMO-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045667, message=0x0758e980) _serializeMessage:] + 129 at WebViewJavascriptBridgeAbstract.m:148 frame #11: 0x0003ffb9 WVJB_ASYN_DEMO-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045477, message=0x0758e980) _dispatchMessage:] + 217 at WebViewJavascriptBridgeAbstract.m:74 frame #12: 0x0003feda WVJB_ASYN_DEMO-[WebViewJavascriptBridgeAbstract(self=0x082a0950, _cmd=0x00045658, message=0x0758e980) _queueMessage:] + 154 at WebViewJavascriptBridgeAbstract.m:69 frame #13: 0x00040d86 WVJB_ASYN_DEMO64-[WebViewJavascriptBridgeAbstract(.block_descriptor=0x072a6dc0, responseData=0x072c56f0) _flushMessageQueue]_block_invoke + 230 at WebViewJavascriptBridgeAbstract.m:116 frame #14: 0x000035db WVJB_ASYN_DEMO`29-[ViewController viewDidLoad]_block_invoke_3(.block_descriptor=0x080d16b0, operation=0x072c0440, responseObject=0x072c56f0) + 123 at ViewController.m:**35 frame #15: 0x000193d9 WVJB_ASYN_DEMO__64-[AFJSONRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke93() + 41 at AFJSONRequestOperation.m:140 frame #16: 0x017d053f libdispatch.dylib_dispatch_call_block_and_release + 15 frame #17: 0x017e2014 libdispatch.dylib_dispatch_client_callout + 14 frame #18: 0x017d27d5 libdispatch.dylib_dispatch_main_queue_callback_4CF + 296 frame #19: 0x01d83af5 CoreFoundation__CFRunLoopRun + 1925 frame #20: 0x01d82f44 CoreFoundationCFRunLoopRunSpecific + 276 frame #21: 0x01d82e1b CoreFoundationCFRunLoopRunInMode + 123 frame #22: 0x021707e3 GraphicsServicesGSEventRunModal + 88 frame #23: 0x02170668 GraphicsServicesGSEventRun + 104 frame #24: 0x00227ffc UIKitUIApplicationMain + 1211 frame #25: 0x00002d3d WVJB_ASYN_DEMOmain(argc=1, argv=0xbffff374) + 141 at main.m:16 frame #26: 0x00002c65 WVJB_ASYN_DEMOstart + 53

— Reply to this email directly or view it on GitHubhttps://github.com/marcuswestin/WebViewJavascriptBridge/issues/41#issuecomment-18961119 .

chenfengbri commented 11 years ago

I think the error occurs because callbackId is released when I call responseback in the success handler. When I hard code the callbackId like '123456789' both in the javascript file and WebViewJavascriptBridgeAbstract.m, it worked! The code in file WebViewJavascriptBridgeAbstract.m at line 115

if (callbackId) {
                responseCallback = ^(id responseData) {
                    NSDictionary* message = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:message];
                };
            } else {
oakho commented 11 years ago

Hey guys,

It seems there's some issues while calling WWJB responseCallback inside another block (those from AFNetworking in this case)

After some testing on the demo project I managed to make it work by changing the line :

__block NSString* callbackId = message[@"callbackId"];

Into :

NSString* callbackId = message[@"callbackId"];

That being said, I don't really have an understanding of why this fix the problem so I'm gonna dig the subject a little more before making a pull request or something.

Cheers !

marcuswestin commented 11 years ago

Thanks Antoine! -- while mobile

On Thu, Jun 6, 2013 at 5:25 AM, Antoine Lagadec notifications@github.com wrote:

Hey guys, It seems there's some issues while calling WWJB responseCallback inside another block (those from AFNetworking in this case) After some testing on the demo project I managed to make it work by changing the line : __block NSString* callbackId = message[@"callbackId"];

Into : NSString* callbackId = message[@"callbackId"]; That being said, I don't really have an understanding of why this fix the problem so I'm gonna dig the subject a little more before making a pull request or something.

Cheers !

Reply to this email directly or view it on GitHub: https://github.com/marcuswestin/WebViewJavascriptBridge/issues/41#issuecomment-19041569

chenfengbri commented 11 years ago

I checked the Apple documentation , maybe this will explain why it work after removing the __block identifier.

__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame (for example, by being enqueued somewhere for later execution). Multiple blocks in a given lexical scope can simultaneously use a shared variable.

marcuswestin commented 11 years ago

Reading the cited documentation, my understanding is that using __block strictly keeps the variable around longer (at least as long as it would have otherwise).

​I don't see how keeping a variable around would cause reference issues.

Now, the cited documentation may not be the whole story, but just looking at that paragraph it seems to me that removing __block should cause issues.

​Antoine, any news from you?

Cheers! Marcus 

-- while mobile

On Thu, Jun 6, 2013 at 8:28 PM, 陈锋 notifications@github.com wrote:

I checked the Apple documentation , maybe this will explain why it work after removing the __block identifier.

__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame (for example, by being enqueued somewhere for later execution). Multiple blocks in a given lexical scope can simultaneously use a shared variable.

Reply to this email directly or view it on GitHub: https://github.com/marcuswestin/WebViewJavascriptBridge/issues/41#issuecomment-19087165

marcuswestin commented 11 years ago

Ah, here's some more color from http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html:

4. Variables local to the enclosing lexical scope declared with the __block storage modifier are provided by reference and so are mutable.

Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope. These are discussed in more detail in “The __block Storage Type.”

I think the key here is that it is mutable. How about this theory:

If message gets discarded, it takes the @"callbackId" property with it. Thus referencing the callbackId variable gives a memory access error.

Sooo... Without a __block we get:

3. Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.
Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.

This seems more like what we want - a const variable. Will do a little more digging before committing.

marcuswestin commented 11 years ago

It looks like I added the __block reference in 4ab41bb4, and I think I had no idea what I was doing. (not that I know what I'm doing these days either, but...)

Making the change. Thanks for the patience @chenfengbri, and detective work @oakho.