0

I'm trying to download some HTML from a website, parse it, and display it in a grid. The HTML isn't formatted very well, so I've already written an init() method that parses that HTML as a String.

My init() works fine in my unit tests.

What I'm having trouble with, is, getting the HTML as a string via Alamofire. All the examples I can find seem to imply that I need to do something close to this...


import SwiftUI
import Alamofire
func getHtml()-> String
{
    let x = AF.request("https://httpbin.org/get")
        .validate()
        .response
    if let y = x {
        debugPrint(y)
        return y.something()
    }
    return "<!-- the html could not be retrieved -->"
}

struct ContentView: View {
    var body: some View {
        Text(getHtml())
            .padding()
    }
}

My challenge is that AF.request().response seems to always be nil. Which I find strange because all the Alamofire examples I've seen online do not seem to even check for that. It's as if older of Alamofire returned an HTTPResponse, not an HTTPResponse?

Is there a simple way I can retrieve the (non-JSON) output of the website I'm looking at, and hand it to my constructor? With or without Alamofire?

Thanks!

This is with Alamofire 5.6.3 if it matters

Things I've tried:

  • I've tried with / without the validate()
  • On the theory response is asyrchonous, I tried adding a braindead while loop: while(response != null) { sleep(1) }
Brian Shelden
  • 73
  • 1
  • 6
  • 1
    Never **`while`** and **`sleep`** to make an asynchronous API synchronous. Please learn to understand how asynchronous data processing works. And with `async/await` the syntax is even *quasi-synchronous* – vadian Mar 05 '23 at 05:59

1 Answers1

0

You cannot return after you call an asynchronous function to get to the results.

You need to wait for the results, then use them. There are many ways to do this, including completion handlers. You will have to look up how to use asynchronous functions in Swift.

Here is some code that shows an additional alternative to Alamofire, using Swift async/await framework, to get the html string from the asynchronous function getHtml().

Note that, you get some JSON data from the url you show, which could easily be decoded into a model if needed.

struct ContentView: View {
    @State var htmlString = "no data"
    
    var body: some View {
        Text(htmlString)
        //        .task {
        //            await getHtml()
        //        }
        // Alamofire
            .onAppear {
                getHtml() { html, error in
                    if error == nil && html != nil {
                        print("\n---> html: \n \(html) \n")
                        htmlString = html!
                    }
                }
            }
    }
    
    func getHtml() async {
        if let url = URL(string: "https://httpbin.org/get") {
            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                htmlString = String(data: data, encoding: .utf8)!
                print("\n---> htmlString: \n \(htmlString) \n")
            } catch {
                print(error)
            }
        }
    }
    
    // using Alamofire
    func getHtml(completion: @escaping  (String?, Error?) -> Void)  {
        AF.request("https://httpbin.org/get").response { response in
            switch response.result {
            case .success(_):
                let theString = String(data: response.data!, encoding: .utf8)!
                completion(theString, nil)
            case .failure(let AFError):
                let error = AFError
                print(error)
                completion(nil, error)
            }
        }
    }
    
}