WKWebView 评估 JavaScript 返回值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26778955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WKWebView evaluate JavaScript return value
提问by redak105
I need to change a function to evaluate JavaScript from UIWebView to WKWebView. I need to return result of evaluating in this function.
我需要更改一个函数来评估 JavaScript 从 UIWebView 到 WKWebView。我需要在这个函数中返回评估结果。
Now, I am calling:
现在,我打电话给:
[wkWebView evaluateJavaScript:call completionHandler:^(NSString *result, NSError *error)
{
NSLog(@"Error %@",error);
NSLog(@"Result %@",result);
}];
But I need get result like return value, like in UIWebView.
Can you suggest a solution?
但我需要得到像返回值这样的结果,就像在UIWebView. 你能提出一个解决方案吗?
采纳答案by UnknownStack
Update: This is not working on iOS 12+ anymore.
更新:这不再适用于 iOS 12+。
I solved this problem by waiting for result until result value is returned.
我通过等待结果直到返回结果值来解决这个问题。
I used NSRunLoop for waiting, but I'm not sure it's best way or not...
我使用 NSRunLoop 等待,但我不确定它是否是最好的方法......
Here is the category extension source code that I'm using now:
这是我现在使用的类别扩展源代码:
@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
Example code:
示例代码:
NSString *userAgent = [_webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
NSLog(@"userAgent: %@", userAgent);
回答by Brams
This solution also works if the javascript's code raise NSError:
如果 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;
}
回答by mort3m
I just stumbled about the same problem and wrote a little Swift (3.0) WKWebView extension for it, thought I might share it:
我只是偶然发现了同样的问题,并为它编写了一个 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)
}
}
}
回答by Nicolas Mandica
Base on @mort3m's answer, here is a WKWebView extension working with Swift 5.
基于@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)
}
}
}
回答by Steve Yost
I've found that the value of final statement in your injected javascript is the return value passed as the id argument to the completion function, if there are no exceptions. So, for example:
我发现注入的 javascript 中的 final 语句的值是作为 id 参数传递给完成函数的返回值,如果没有异常的话。因此,例如:
[self.webview evaluateJavaScript:@"var foo = 1; foo + 1;" completionHandler:^(id result, NSError *error) {
if (error == nil)
{
if (result != nil)
{
NSInteger integerResult = [result integerValue]; // 2
NSLog(@"result: %d", integerResult);
}
}
else
{
NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
}
}];
回答by Vlad E. Borovtsov
It's possible to use dispatch semaphore. It works on iOS12+
可以使用调度信号量。它适用于iOS12+
Example:
例子:
__block NSString *resultString = nil;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[self evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
if (error == nil) {
if (result != nil) {
resultString = [NSString stringWithFormat:@"%@", result];
dispatch_semaphore_signal(sem);
}
} else {
NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
}
finished = YES;
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
//process resultString here.

