iOS JavaScript 桥接器

Posted

技术标签:

【中文标题】iOS JavaScript 桥接器【英文标题】:iOS JavaScript bridge 【发布时间】:2012-03-17 10:21:16 【问题描述】:

我正在开发一个应用程序,我将同时使用 UIWebView 中的 html5 和原生 ios 框架。我知道我可以实现 javascript 和 Objective-C 之间的通信。是否有任何库可以简化这种通信的实现?我知道有几个库可以在 HTML5 和 javascript 中创建原生 iOS 应用程序(例如 AppMobi、PhoneGap),但我不确定是否有一个库可以帮助创建使用大量 JavaScript 的原生 iOS 应用程序。我需要:

    从 Objective-C 执行 JS 方法 从 JS 执行 Objective-C 方法 从 Objective-C 监听原生 JS 事件(例如 DOM 就绪事件)

【问题讨论】:

您可以使用 WKWebView: 从 javascript window.webkit.messageHandlers.NAME.postMessage(message) 调用,然后使用 [WKUserContentController addScriptMessageHandler:name:] 处理它以从 JS 调用 Objective-C 【参考方案1】:

有一些库,但我没有在大型项目中使用这些库,因此您可能想尝试一下:

WebViewJavascriptBridge:https://github.com/marcuswestin/WebViewJavascriptBridge GAJavaScript:https://github.com/newyankeecodeshop/GAJavaScript

但是,我认为这很简单,您可以自己尝试一下。当我需要这样做时,我个人正是这样做的。您还可以创建一个适合您需要的简单库。

1。从 Objective-C 执行 JS 方法

这实际上只是一行代码。

NSString *returnvalue = [webView stringByEvaluatingJavaScriptFromString:@"your javascript code string here"];

更多详情见官方UIWebView Documentation。

2。从 JS 执行 Objective-C 方法

不幸的是,这稍微复杂一些,因为在 Mac OSX 上没有相同的 windowScriptObject 属性(和类)允许两者之间完全通信。

但是,您可以轻松地从 javascript 自定义 URL 调用,例如:

window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value

然后用这个从 Objective-C 中截取它:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType 
   NSURL *URL = [request URL]; 
   if ([[URL scheme] isEqualToString:@"yourscheme"]) 
       // parse the rest of the URL object and execute functions
    

这并不像应有的那样干净(或使用 windowScriptObject),但它可以工作。

3。监听来自 Objective-C 的原生 JS 事件(例如 DOM 就绪事件)

从上面的解释可以看出,如果你想这样做,你必须创建一些 JavaScript 代码,将它附加到你想要监控的事件上,并调用正确的 window.location 调用然后被拦截。

再一次,虽然不是应有的干净,但它确实有效。

【讨论】:

提示:不要在你的方案中使用下划线或类似符号。 并在你应该返回的时候返回一个值:) 另一个提示:在 iFrame 中加载 URL 以防止闪烁 @Folleto 嗨,你说“但我没有在大项目中使用这些”,为什么不呢?你在这些库上发现了几个问题???提前致谢。 没有问题。我根本没有使用它们,所以我无法评论在更大的项目中使用它们。我想澄清一下。 :)【参考方案2】:

不推荐在接受的答案中从 JS 调用目标 c 的建议方法。问题的一个示例:如果您立即连续拨打两个电话,其中一个会被忽略(您不能太快更改位置)。

我推荐以下替代方法:

function execute(url) 

  var iframe = document.createElement("IFRAME");
  iframe.setAttribute("src", url);
  document.documentElement.appendChild(iframe);
  iframe.parentNode.removeChild(iframe);
  iframe = null;

您反复调用execute 函数,由于每次调用都在自己的 iframe 中执行,因此在快速调用时不应忽略它们。

感谢this guy。

【讨论】:

您还可以在 JavaScript 中使用 iOS 可以获取的队列,以避免丢失来自 .src 更改太快的消息。上面链接的实现,github.com/marcuswestin/WebViewJavascriptBridge,使用队列方法。【参考方案3】:

更新:这在 iOS 8 中有所改变。我的回答适用于以前的版本。

另一种可能会被应用商店拒绝的方法是使用 WebScriptObject。

这些 API 在 OSX 上是公开的,但不在 iOS 上。

您需要定义内部类的接口。

@interface WebScriptObject: NSObject
@end

@interface WebView
- (WebScriptObject *)windowScriptObject;
@end

@interface UIWebDocumentView: UIView
- (WebView *)webView;
@end

您需要定义将用作 WebScriptObject 的对象

@interface WebScriptBridge: NSObject
- (void)someEvent: (uint64_t)foo :(NSString *)bar;
- (void)testfoo;
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (WebScriptBridge*)getWebScriptBridge;
@end

static WebScriptBridge *gWebScriptBridge = nil;

@implementation WebScriptBridge
- (void)someEvent: (uint64_t)foo :(NSString *)bar

    NSLog(bar);


-(void)testfoo 
    NSLog(@"testfoo!");


+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;

    return NO;


+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;

    return NO;


+ (NSString *)webScriptNameForSelector:(SEL)sel

    // Naming rules can be found at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/Protocols/WebScripting_Protocol/Reference/Reference.html
    if (sel == @selector(testfoo)) return @"testfoo";
    if (sel == @selector(someEvent::)) return @"someEvent";

    return nil;

+ (WebScriptBridge*)getWebScriptBridge 
    if (gWebScriptBridge == nil)
        gWebScriptBridge = [WebScriptBridge new];

    return gWebScriptBridge;

@end

现在为你的 UIWebView 设置一个实例

if ([uiWebView.subviews count] > 0) 
    UIView *scrollView = uiWebView.subviews[0];

    for (UIView *childView in scrollView.subviews) 
        if ([childView isKindOfClass:[UIWebDocumentView class]]) 
            UIWebDocumentView *documentView = (UIWebDocumentView *)childView;
            WebScriptObject *wso = documentView.webView.windowScriptObject;

            [wso setValue:[WebScriptBridge getWebScriptBridge] forKey:@"yourBridge"];
        
    

现在您可以在 javascript 中调用:

yourBridge.someEvent(100, "hello");
yourBridge.testfoo();

【讨论】:

我的应用在应用商店被拒绝。我收到的问题是“应用程序包含或继承自 AppName 中的非公共类:UIWebDocumentView”【参考方案4】:

In iOS8 you can look at WKWebView instead of UIWebView。这有以下类: WKScriptMessageHandler:提供一种从网页中运行的 JavaScript 接收消息的方法。

【讨论】:

当然可以,但是该方法没有连续执行..这是苹果提供的框架中的一个错误 WKWebView 有很多问题,包括没有正确处理 cookie。只是对任何来到这里认为它会解决你所有问题的人的警告 - 在采用它之前先谷歌一下。【参考方案5】:

这在 iOS7 上是可能的,结帐http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

【讨论】:

并非如此:JavaScriptCore 不与 UIWebViews 交互。这是一个很棒的工具,但针对不同类别的问题。 ;) 是的:JSContext* context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];但目前 JavaScriptCore 漏洞百出,无法真正发挥作用。【参考方案6】:

您最好的选择是 Appcelerators Titanium 产品。他们已经使用 webkit 使用的 V8 引擎 JavascriptCore 引擎构建了一个 Obj-C javascript 桥。它也是开源的,因此您可以下载它并随意修改 Obj-C。

【讨论】:

@hova,不是 Obj-C V8 桥。 V8 仅适用于 android【参考方案7】:

看看 KirinJS 项目:Kirin JS,它允许将 Javascript 用于应用程序逻辑和适合其运行平台的原生 UI。

【讨论】:

还有人为麒麟做贡献吗?过去几个月我没有看到任何活动...【参考方案8】:

我创建了一个类似于 WebViewJavascriptBridge 的库,但它更像 JQuery,更易于设置且更易于使用。不依赖 jQuery(尽管值得称赞的是,如果我在写这篇文章之前就知道 WebViewJavascriptBridge 存在,我可能只是在深入研究之前稍微退缩了)。让我知道你的想法! jockeyjs

【讨论】:

【参考方案9】:

如果您在 iOS 8 上使用 WKWebView,请查看XWebView,它可以自动将原生界面暴露给 javascript。

【讨论】:

以上是关于iOS JavaScript 桥接器的主要内容,如果未能解决你的问题,请参考以下文章

webviewjavascriptbridge之js与ios桥接篇 在h5里加啥代码

WebView中文件的iOS Javascript资源路径?

AHB2APB桥接器设计——同步桥设计的介绍

模仿windows任务管理器性能图

javascript与jquery基础

热更新机制