简体   繁体   English

调用 Objective-C 函数时将值返回给 JavaScript

[英]Returning values to JavaScript when calling an Objective-C function

I've been using WebViewJavascriptBridge to bridge Objective-C and JavaScript in an iOS application.我一直在使用WebViewJavascriptBridge在 iOS 应用程序中连接 Objective-C 和 JavaScript。 It works great but it lacks support for returning values to the calling JavaScript code from the called native Objective-C function.它工作得很好,但它不支持从被调用的本机 Objective-C 函数返回值给调用 JavaScript 代码。

I'm pretty sure that Cordova (PhoneGap) does that in some way with callbacks but I've been having a hard time trying to understand how the underlying mechanics work.我很确定 Cordova (PhoneGap) 在某种程度上通过回调做到了这一点,但我一直很难理解底层机制是如何工作的。

Is there anyone who's had the same problem and that managed to find a solution?有没有人遇到同样的问题并设法找到解决方案?

Now, I've never used WebViewJavascriptBridge, but I have done this in objective-c using a simple delegate, so maybe this will help you:现在,我从未使用过 WebViewJavascriptBridge,但我已经在 Objective-C 中使用简单的委托完成了这项工作,所以也许这会对您有所帮助:

MyScript.js MyScript.js

// requestFromObjc
// functionName (required):
//      the name of the function to pass along to objc
// returnedResult (not used by caller):
//      the result given by objc to be passed along
// callback (not used by caller):
//      sent by objc, name of the function to execute
function requestFromObjc(functionName, objcResult, callback)
{    
    if (!objcResult)
    {
        window.location = "myapp://objcRequest?function=" + functionName + "&callback=" + arguments.callee.name + "&callbackFunc=" + arguments.callee.caller.name;
    }
    else
    {
        window[callback](objcResult);
    }
}

function buttonClick(objcResult)
{    
    if (!objcResult)
    {
        // ask for the color from objc
        requestFromObjc("buttonColor&someParam=1");
    }
    else
    {
        // do something with result (in this case, set the background color of my div)
        var div = document.getElementById("someDiv");

        div.style.background = objcResult;
    }
}

MyPage.html我的页面.html

<html>
    <head>
        <script type="text/javascript" src="MyScript.js"></script>
    </head>
    <body>
        <div id="someDiv">
            This is my div! Do not make fun of it, though.
        </div>

        <button onClick="buttonClick(undefined)">
            Click Me!
        </button>
    </body>
</html>

ViewController.m视图控制器.m

-(NSString *) javaScriptResultForRequest:(NSDictionary *) params
{
    if ([params[@"function"] isEqualToString:@"buttonColor"])
    {
        NSArray *colors = @[ @"red", @"yellow", @"blue", @"green", @"purple" ];
        return colors[arc4random_uniform(colors.count)];
    }
    else
    {
        return @"undefined";
    }
}

-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] scheme] isEqualToString:@"myapp"])
    {
        // parse the URL here
        NSURL *URL = [request URL];

        if ([URL.host isEqualToString:@"objcRequest"])
        {
            NSMutableDictionary *queryParams = [NSMutableDictionary dictionary];
            NSArray *split = [URL.query componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"&="]];

            for (int i = 0; i < split.count; i += 2)
            {
                queryParams[split[i]] = split[i + 1];
            }

            NSString *result = [self javaScriptResultForRequest:queryParams];
            NSString *jsRequest = [NSString stringWithFormat:@"%@(\"%@\", \"%@\", \"%@\")", queryParams[@"callback"], queryParams[@"function"], result, queryParams[@"callbackFunc"]];

            // now we send this to the target
            [self.webView stringByEvaluatingJavaScriptFromString:jsRequest];
            return NO;
        }
    }

    return YES;
}

Obviously this is much slower than trying to do the equivalent function in pure JS, because of all the loops it has to jump through.显然这比尝试在纯 JS 中执行等效功能要慢得多,因为它必须跳过所有循环。 However, if there is something you need to use in your JS code that's in your ObjC code already, this may be for you.但是,如果您需要在您的 JS 代码中使用已经在您的 ObjC 代码中的东西,那么这可能适合您。

If you mean, "return" in the sense that your JS caller will see the results as returned from the call, I don't know how to do it.如果您的意思是“返回”,因为您的 JS 调用者将看到调用返回的结果,我不知道该怎么做。 I suspect it would take a level of thread manipulation not available in JS.我怀疑这需要 JS 中没有的线程操作级别。 Instead, you can recode your JS side logic to create a result handler function.相反,您可以重新编码 JS 端逻辑以创建结果处理函数。 In pseudo code:在伪代码中:

res = CallObjC(args);
work with res...

Becomes变成

CallObjC(args, function(res) { work with res...});

Admittedly a bit awkward but get used to it - it is a very common pattern.诚然有点尴尬,但习惯了 - 这是一种非常常见的模式。 Internally, the JS bridge code creates a mapping of the request to the callback function.在内部,JS 桥接代码创建了请求到回调函数的映射。 When the ObjC code has the result, it uses WebView's stringByEvaluatingJavaScriptFromString to call the JS bridge code that locates and invokes the callback.当 ObjC 代码有结果时,它使用 WebView 的 stringByEvaluatingJavaScriptFromString 调用定位并调用回调的 JS 桥代码。

@Richard - Be a bit careful with the solution you posted. @Richard - 请注意您发布的解决方案。 Setting window.location to invoke shouldStartLoadWithRequest can result in both lost functionality in the webview and also lost messages to ObjectiveC.将 window.location 设置为调用 shouldStartLoadWithRequest 会导致 webview 中的功能丢失以及 ObjectiveC 的消息丢失。 Current practice is to use an iframe and have some kind of message queue that can buffer up messages.当前的做法是使用 iframe 并拥有某种可以缓冲消息的消息队列。

WebViewJavascriptBridge have no maintain for a long time. WebViewJavascriptBridge很久没有维护了。

Maybe you should change to use this library.也许你应该改用这个库。

SDBridgeOC SDBridgeOC

 self.bridge.consolePipeBlock = ^(id  _Nonnull water) {
        NSLog(@"Next line is javascript console.log------>>>>");
        NSLog(@"%@",water);
    };

This can easy to get javascript console.log.这可以很容易地得到javascript console.log。

Also have Swift language Version.也有 Swift 语言版本。

SDBridgeSwift SDBridgeSwift

bridge.consolePipeClosure = { water in
         guard let jsConsoleLog = water else {
             print("Javascript console.log give native is nil!")
              return
         }
         print("Next line is Javascript console.log----->>>>>>>")
         print(jsConsoleLog)
      }

Also have h5 demo for your partner.还为您的合作伙伴提供 h5 演示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM