ozd
ozd

Reputation: 1284

Convert asynchronous block callback to synchronous return value

UIWebView's - stringByEvaluatingJavaScript(from:) is synchronously returning a String? value.

The WKWebView equivalent evaluateJavaScript(_:completionHandler:) works with a complitionHandler.

I'm working with legacy code and I have stringByEvaluatingJavaScript(from:) affecting thousands of lines in the code and can be found everywhere and in a lot of functions that return values (not using blocks).

very simple example -

@discardableResult func js(_ script: String) -> String? {
        let callback = self.stringByEvaluatingJavaScript(from: script)
        if callback!.isEmpty { return nil }
        return callback
    }

now that I'm changing to evaluateJavaScript(_:completionHandler:) I will need the js(script:) method to be handled with a block, and methods that use that method needs to change and so on...

I think that in order to fix this without changing all my code I need evaluateJavaScript(_:completionHandler:) to synchronously return String?.

Does anybody know how this can be achieved? or maybe have a different solution?

Upvotes: 0

Views: 165

Answers (1)

ozd
ozd

Reputation: 1284

Found it -

Add this to your WKWebview extension to wrap evaluateJavaScript(_:completionHandler:) in a synchronous warp like in UIWebview -

open func stringByEvaluatingJavaScript(from script: String) -> String?
    {
        var finished = false
        var stringResult: String? = nil

        evaluateJavaScript(script) { (result, error) in

            if error == nil, let result = result
            {
                stringResult = String(describing: result)
            }

            finished = true
        }

        while !finished
        {
            RunLoop.current.run(mode: .default, before: Date.distantFuture)
        }

        return stringResult
    }

I'm not sure what types can be returned in the result so watch out for that. other then that works great.

Upvotes: 1

Related Questions