更新弃用的 webkit objc 项目以在 javascript 内启用本机代码 运行

Update deprecated webkit objc items to enable native code running within javascript

我正在使用基于遗留 webkit 的应用程序在 macOS 原生应用程序上生成表单(cocoa 用 objective-c 编写的应用程序)

在 javascript 加载到视图之前调用以下回调, 并允许在即将加载的 javascript 中使用当前 class 代码 (objc)。

- (void)webView:(WebView *)webView windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject {
    [windowScriptObject setValue:self forKey:@"app"];
}

不幸的是,它早就被弃用了,我想使用 WKWebView 的 webView 对象的更新替代品。然而,上面的回调是来自 WebFrameLoadDelegate 的委托方法,它也被弃用了。也许有人知道如何使用 WKWebView 在 javascript 中注入我们的本机代码?

谢谢

这就是您使用 WKWebView 的方式。 YourWebView 是 UIView 或 ViewController class.

@interface YourWebView () <WKNavigationDelegate, WKScriptMessageHandler>

- (void)injectWSKitScriptInUserContentController:(WKUserContentController*)userContentController;

@end    

-init-initWithFrame:

中的实现中
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
self.autoresizesSubviews = YES;
self.wantsLayer = YES;

WKWebViewConfiguration* conf = [[WKWebViewConfiguration alloc] init];
conf.suppressesIncrementalRendering = NO;
conf.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll;

WKUserContentController* userContentController = [[WKUserContentController alloc] init];
[self injectWSKitScriptInUserContentController:userContentController];
[userContentController addScriptMessageHandler:self name:@"yourscript"];
conf.userContentController = userContentController;

#ifdef DEBUG
    NSLog(@"Developer Extras Enabled");
    [conf.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
#endif

WKWebView *webView = [[WKWebView alloc] initWithFrame:frame configuration:conf];
webView.navigationDelegate = self;

添加webView查看或任何你需要它的地方,并定义一个注入js的方法。

-(void)injectWSKitScriptInUserContentController:(WKUserContentController*)userContentController {
    NSBundle* bundle = [NSBundle bundleForClass:[YourWebView class]];
    NSString* scriptLocation = [bundle pathForResource:@"yourscript" ofType:@"js"];
    NSString* scriptSource = [NSString stringWithContentsOfFile:scriptLocation encoding:NSUTF8StringEncoding error:nil];
    WKUserScript* userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
    [userContentController addUserScript:userScript];
}

然后遵循关于在 YourWebView 中实现的其他内容的协议 例如与您的 webview 交互,例如前进、重新加载、后退等。

最后,您需要添加一个 javascript 文件作为您应用程序的起点。 "yourscript.js"如上。

(function() {
"use strict";
    
    var Events = {
        listeners: { },

        gc: function() {
          var events = Object.keys(this.listeners)
          for (var i = events.length - 1; i >= 0; i -= 1) {
            var eventName = events[i],
                listeners = this.listeners[eventName]
            if (listeners.length === 0) {
              delete this.listeners[eventName]
            }
          }
        },
        
        once: function(name, listener) {
          if (name in this.listeners) {
            this.listeners[name].push({ oneshot: true, listener: listener })
            return
          }
        
          this.listeners[name] = [
            { oneshot: true, listener: listener },
          ]
        },
        
        on: function(name, listener) {
          if (name in this.listeners) {
            this.listeners[name].push({ listener: listener })
            return
          }
        
          this.listeners[name] = [ { listener: listener } ]
        },
        
        off: function(name, listener) {
          if ( ! (name in this.listeners)) {
            return
          }
        
          var listeners = this.listeners[name]
          for (var i = listeners.length - 1; i >= 0; i -= 1) {
            if (listeners[i].listener === listener) {
              listeners.splice(i, 1)
              return
            }
          }
        },
        
        trigger: function(name, arg) {
          if ( ! (name in this.listeners)) {
            return
          }
        
          var event = { stopIteration: false, data: arg }
        
          var listeners = this.listeners[name]
          for (var i = 0; i < listeners.length; i += 1) {
            var listener = listeners[i]
            try {
              listener.listener(event)
            } catch (e) { }
            if (listener.oneshot) {
              listeners.splice(i, 1)
              i -= 1
            }
          }
        
          this.gc()
        },
    }

    var ETimeout = new Error('WSKit: configuration timeout'),
        _config = { resolve: null, reject: null, resolved: false }
    
    window.WSKit = {
        configuration: new Promise(function(resolve, reject) {
            _config.resolve = resolve
            _config.reject = reject
        }),

        addEventListener: function(name, listener, config) {
            config = config || { }
            
            if (config.oneshot) {
              Events.once(name, listener)
            } else {
              Events.on(name, listener)
            }
        },
        removeEventListener: function(name, listener) {
          Events.off(name, listener)
        },
        dispatchEvent: function(name, arg) {
          Events.trigger(name, arg)
        },
    }

    setTimeout(function() {
        if ( ! _config.resolved) {
            _config.reject(ETimeout)
        }
    }, 5000)
    
    WSKit.addEventListener('configure', function(ev) {
        _config.resolve(ev.data)
    })
 
    window.webkit.messageHandlers.webscreen.postMessage('obtainconfiguration')
})();

这应该很管用