Lester
Lester

Reputation: 731

How to prevent an app crash or freeze due to a slow connection when retrieving a remote photo in Swift?

I want to display avatar image in my table view cell by the url string. and it will crash when the phone is not connect to the internet, so I added Reachability swift to it. but now I face another problem, when the user leaving the wifi zone or the internet connection is not stable, the app will freeze, I'm not able to tap anything until I walk back the strong wifi zone. why?

let imageData:NSData = try! NSData(contentsOf: imageUrl)

this code will crash so I try add do & catch but still not working. is it because the app can't connect to the url string and get the data so that the app will be freeze? how to prevent an app crash or freeze due to a slow connection when retrieving a remote photo?

if Reachability.shared.isConnectedToNetwork(){
    if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
        let imageUrlString = crewAva
        let imageUrl:URL = URL(string: imageUrlString)!

        DispatchQueue.main.async(execute:  {
            do{
                let imageData:NSData = try NSData(contentsOf: imageUrl)
                let image = UIImage(data: imageData as Data)
                self.avaImg.image = image
            }
            catch{
                print("error")
            }
        })
    }
}else{
    print("Not reachable")
}

Upvotes: 0

Views: 485

Answers (3)

Lester
Lester

Reputation: 731

Solution

func getDataFromUrl(url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        completion(data, response, error)
        }.resume()
}

func downloadImage(url: URL) {
    getDataFromUrl(url: url) { data, response, error in
        guard let data = data, error == nil else { return }
        print(response?.suggestedFilename ?? url.lastPathComponent)
        DispatchQueue.main.async() {
            self.avaImg.image = UIImage(data: data)
        }
    }
}

override func viewWillAppear(_ animated: Bool) {
    if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
        let imageUrlString = crewAva
        let url = URL(string: imageUrlString)!
        downloadImage(url: url)
    }
}

Upvotes: 1

vadian
vadian

Reputation: 285082

From the NSData documentation:

init?(contentsOf url: URL)

Important

Don't use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated. Instead, for non-file URLs, consider using the dataTask(with:completionHandler:) method of the NSURLSession class. See URL Session Programming Guide for details.

Upvotes: 3

PoolHallJunkie
PoolHallJunkie

Reputation: 1363

Try only updating the UI on the main thread.

if Reachability.shared.isConnectedToNetwork(){
    if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
        let imageUrlString = crewAva
        let imageUrl:URL = URL(string: imageUrlString)!
        let imageData:NSData = try NSData(contentsOf: imageUrl)
        let image = UIImage(data: imageData as Data)
        DispatchQueue.main.async(execute:  {
            do{
                self.avaImg.image = image
            }
            catch{
                print("error")
            }
        })
    }
}else{
    print("Not reachable")
}

Upvotes: 0

Related Questions