I have the following class. It is intended to cache a parsed DOM after fetching from a URL:
class DOMWindow {
var cachedDOM: Elements
var url: URL
init(_ url: String)throws {
self.url = URL(string: url)!
self.cachedDOM = try fetchDOM()
}
func fetchDOM()throws -> Elements {
var html: String
let task = URLSession.shared.dataTask(with: url) { data, response, error in
html = String(data: data!, encoding: .utf8)!
}
task.resume()
return try DOM.parse(html)
}
}
I am getting the errors that Variable 'html' used before initialization and Variable 'html' captured by closure before being initialized
I assume this is a detected race condition that the closure is run async and so the DOM.parse(html) will always be reached before the closure is executed. So how best should I handle this in Swift?
I think you need to add a completion handler to your fetchDOM method:
func fetchDOM(completion: @escaping (Result<Elements, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.networkError(error)))
return
}
if let data = data, let html = String(data: data, encoding: .utf8) {
do {
let parsedHTML = try DOM.parse(html)
completion(.success(parsedHTML))
} catch {
completion(.failure(.parsingFailure(error)))
}
return
}
completion(.failure(.genericFailure))
}.resume()
}
and give cachedDOM a default value or make it optional.
Though, you might also need something to let clients know if the DOM has been loaded or not. You could model it as an enum:
enum DOMState {
case notLoaded
case loading
case loaded(Elements)
case failedToLoad
}
and then have var cachedDOM: DOMState = .notLoaded as default. When you call fetchDOM, set it to .loading and then set to .loaded or .failedToLoad depending on the result.
There's various other ways to do it though. You can allow clients to call fetchDOM manually instead of calling it by default in init for example.
fetchDOM { result in
switch result {
case let .success(elements):
// do something with elements
case let .failure(error):
// do something with the error
}
}
You can read more about closures here: Closures — The Swift Programming Language (Swift 5.7). If you want to read more about completion handlers specifically, there are some resources available on the internet, such as this blog post.