WKWebView evaluate JavaScript return value WKWebView evaluate JavaScript return value javascript javascript

WKWebView evaluate JavaScript return value


Update: This is not working on iOS 12+ anymore.


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...

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);


This solution also works if the javascript's code raise 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;}


I just stumbled about the same problem and wrote a little Swift (3.0) WKWebView extension for it, thought I might share it:

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)        }    }}