WKWebView评估JavaScript返回值

red*_*105 29 javascript objective-c ios wkwebview

我需要更改一个函数来评估从UIWebView到WKWebView的JavaScript.我需要在这个函数中返回求值结果.

现在,我打电话给:

[wkWebView evaluateJavaScript:call completionHandler:^(NSString *result, NSError *error)
{
    NSLog(@"Error %@",error);
    NSLog(@"Result %@",result);
}];
Run Code Online (Sandbox Code Playgroud)

但是我需要得到像返回值这样的结果UIWebView.你能建议一个解决方案吗?

Unk*_*ack 33

这不再适用于iOS 12+.


我通过等待结果直到返回结果值来解决了这个问题.

我用NSRunLoop等待,但我不确定这是不是最好的方式......

这是我现在使用的类别扩展源代码.

@interface WKWebView(SynchronousEvaluateJavaScript)
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
@end

@implementation WKWebView(SynchronousEvaluateJavaScript)

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
{
    __block NSString *resultString = nil;
    __block BOOL finished = NO;

    [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
        if (error == nil) {
            if (result != nil) {
                resultString = [NSString stringWithFormat:@"%@", result];
            }
        } else {
            NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
        }
        finished = YES;
    }];

    while (!finished)
    {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    return resultString;
}
@end
Run Code Online (Sandbox Code Playgroud)

示例代码)

NSString *userAgent = [_webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];

NSLog(@"userAgent: %@", userAgent);
Run Code Online (Sandbox Code Playgroud)

  • 此代码的问题是,如果引发JS错误,您的本机函数将无限运行!解决方案是检查块安全布尔值而不是resultString.往下看 (3认同)
  • 这个解决方案不再工作了.现在从主线程调用回调,如果你在`while`循环中锁定主线程(就像在这个解决方案中),将永远不会调用回调处理程序.`evaluateJavaScript`等待释放主线程,但它永远不会发生,因为它锁定在循环中. (2认同)
  • 对于 iOS 12 或 iOS 13 有什么想法吗? (2认同)
  • 该解决方案在 iOS 13 中不起作用。有人有适用于 iOS 13 的可行解决方案吗? (2认同)

Bra*_*ams 20

如果javascript的代码引发NSError,此解决方案也有效:

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script {
    __block NSString *resultString = nil;
    __block BOOL finished = NO;

    [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
        if (error == nil) {
            if (result != nil) {
                resultString = [NSString stringWithFormat:@"%@", result];
            }
        } else {
            NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
        }
        finished = YES;
    }];

    while (!finished)
    {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    return resultString;
}
Run Code Online (Sandbox Code Playgroud)


mor*_*t3m 12

我只是偶然发现了同样的问题并为它写了一个小的Swift(3.0)WKWebView扩展,我想可能会分享它:

extension WKWebView {
    func evaluate(script: String, completion: (result: AnyObject?, error: NSError?) -> Void) {
        var finished = false

        evaluateJavaScript(script) { (result, error) in
            if error == nil {
                if result != nil {
                    completion(result: result, error: nil)
                }
            } else {
                completion(result: nil, error: error)
            }
            finished = true
        }

        while !finished {
            RunLoop.current().run(mode: .defaultRunLoopMode, before: Date.distantFuture)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,但是如果您仍在使用回调,这有什么意义呢? (2认同)

Nic*_*ica 6

基于@mort3m 的回答,这里是一个使用 Swift 5 的 WKWebView 扩展。

extension WKWebView {
    func evaluate(script: String, completion: @escaping (Any?, Error?) -> Void) {
        var finished = false

        evaluateJavaScript(script, completionHandler: { (result, error) in
            if error == nil {
                if result != nil {
                    completion(result, nil)
                }
            } else {
                completion(nil, error)
            }
            finished = true
        })

        while !finished {
            RunLoop.current.run(mode: RunLoop.Mode(rawValue: "NSDefaultRunLoopMode"), before: NSDate.distantFuture)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)