what happens when i invoke the swift completion handler?

I got this to work after reading some of the comments here and doing more research/experimentation. Here’s what I changed:

In the URL extension, I left it pretty much the same as I find it more readable this way. I did push the timeoutInterval to a parameter:

// Extensions/URL.swift


import Foundation

extension URL {
    func isReachable(timeoutInterval: Double, completion: @escaping (Bool) -> Void) {
        var request = URLRequest(url: self)
        request.httpMethod = "HEAD"
        request.timeoutInterval = timeoutInterval
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            if error != nil {
                DispatchQueue.main.async {
                    completion(false)
                }
                return
            }
            if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
                DispatchQueue.main.async {
                    completion(httpResp.statusCode == 200)
                }
                return
            } else {
                DispatchQueue.main.async {
                    completion(false)
                }
                return
            }
        }.resume()
    }
}

I modified my BookViewModel to make two of the properties to @Published and used the URL extension there:

// View Models/BookViewModel.swift

import Foundation

class BookViewModel: ObservableObject {
    @Published var book: Book
    @Published var imageURLIsReachable: Bool
    @Published var imageURL: URL?
    
    init(book: Book) {
        self.book = book
        self.imageURL = nil
        self.imageURLIsReachable = false
        if let url = book.image_url {
            self.imageURL = URL(string: url)
            self.imageURL!.isReachable(timeoutInterval: 1.0) { result in
                self.imageURLIsReachable = result
            }
        }
    }
    
    // Rest of properties...
}

Now my BookThumbnailView can properly display the conditional views:

// Views/BookThumbnailView.swift

import SwiftUI
import Foundation
import KingfisherSwiftUI

struct BookThumbnailView: View {
    @ObservedObject var viewModel: BookViewModel
        
    private var book: Book {
        viewModel.book
    }
    
    @ViewBuilder
    var body: some View {
        if let imageURL = self.viewModel.imageURL {
            if self.viewModel.imageURLIsReachable {
                KFImage(imageURL)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(maxWidth: 70)
                        .cornerRadius(8)
            } else {
                ErrorBookThumbnailView()
            }
        } else {
            DefaultBookThumbnailView()
        }
    }
}

Whew, that was quite the learning experience. Thanks to everyone who commented with suggestions and provided hints on where to look!

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top