0

I have a function which is a part of some WebScraping Code. Pretty "deep" in the function I have a value which I want to display in a chart. To do that I need to get the variable "elevation" out of the function parseElevation(). I have tried

func parseElevation() -> String {

This however doesn't work because the variable "elevation" is "one layer deeper.

This is the code:

func parseElevation()  {
        webView.evaluateJavaScript("document.body.innerHTML") { result, error in
            guard let html = result as? String, error == nil else {
                return
            }
            
            let leftSideOfTheValue = """
            <pre style="word-wrap: break-word; white-space: pre-wrap;">
            """
            
            let rightSideOfTheValue = """
            </pre>
            """
            
            guard let leftRange = html.range(of: leftSideOfTheValue) else {
                print("cant find left range")
                return
            }
            
            guard let righRange = html.range(of: rightSideOfTheValue) else {
                print("cant find right range")
                return
            }
            
            let rangeOfTheValue = leftRange.upperBound..<righRange.lowerBound
            
            let elevationInfo = (html[rangeOfTheValue])
            
            let last9 = elevationInfo.suffix(11)
            
            if let index = (last9.range(of: ",")?.upperBound)
            {
              //prints "value"
              let afterEqualsTo = String(last9.suffix(from: index))
                print(afterEqualsTo)
                let elevation = afterEqualsTo
                
            }
        }
        
    }

Does somebody have an idea how to solve this problem. I have always had a problem with returning values in swift.

1 Answer 1

2

You cannot return something from a closure which doesn't have a return value.

You need an asynchronous completion handler

func parseElevation(completion: @escaping (String) -> Void)  {
    webView.evaluateJavaScript("document.body.innerHTML") { result, error in
        guard let html = result as? String, error == nil else {
            return
        }
        
        let leftSideOfTheValue = """
        <pre style="word-wrap: break-word; white-space: pre-wrap;">
        """
        
        let rightSideOfTheValue = """
        </pre>
        """
        
        guard let leftRange = html.range(of: leftSideOfTheValue) else {
            print("cant find left range")
            return
        }
        
        guard let righRange = html.range(of: rightSideOfTheValue) else {
            print("cant find right range")
            return
        }
        
        let rangeOfTheValue = leftRange.upperBound..<righRange.lowerBound
        
        let elevationInfo = (html[rangeOfTheValue])
        
        let last9 = elevationInfo.suffix(11)
        
        if let index = (last9.range(of: ",")?.upperBound)
        {
            //prints "value"
            let afterEqualsTo = String(last9.suffix(from: index))
            completion(afterEqualsTo)                
        }
    } 
}

and call it

parseElevation { elevation in
    print(elevation)
}

Notes:

  • elevation in the second snippet is only available inside the closure {}.

  • Consider that the completion handler is only called if a result is evaluated. All return statements leave the closure silently.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! I have one more Question, can I assign the value of "elevation" to a varaible, something like: let value1 = parseElevation { elevation in print(elevation) }
That's impossible due to the asynchronous behavior. Execute the code you need to execute inside the closure.
@JonasHen don`t ignore the accept the answer and upvote if you think it is useful please

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.