'I can't make extension convenience init for UIImage

I want to create UIImage(urlString: String?). There is no error when I run this code but it is not working.

extension UIImage {
    convenience init?(urlString: String?) { 
        var imageData = Data()
        guard let urlString = urlString else { return nil}
        guard let url = URL(string: urlString) else { return nil}
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }
            guard let response = response as? HTTPURLResponse else {
                print("Empty image response")
                return
            }
            print("HTTP image response code: \(response.statusCode)")
            guard let data = data else {
                print("Empty image data")
                return
            }
            imageData = data
        }.resume()
        self.init(data: imageData)
    }
}


Solution 1:[1]

My solution for you:

  • Use Data(contentsOf:URL) to get Data from an URL, instead of using URLSession.dataTask
   extension UIImage {  
    convenience init?(urlString: String) {
        self.init()
        guard let url = URL(string: urlString), let data = try? Data(contentsOf: url) else { return }
        self.init(data: data)
    }
}

You have to call self.init() because it required self.init() to be called before return. Because this is a Failable Initializer, if your URL is valid then you get an image from that URL, otherwise, you get nil.

In case you want to use a default image when your URL is invalid, just replace init() with init(named:):

   extension UIImage {  
    convenience init?(urlString: String) {
        self.init(named: "YOUR_IMAGE_NAME")
        guard let url = URL(string: urlString), let data = try? Data(contentsOf: url) else { return }
        self.init(data: data)
    }
}

Solution 2:[2]

UIImage initializer init(data:) is a synchronous method. Your self.init(data: imageData) method is called before the async method dataTask finish its request and execute its completion handler.

What I suggest is to create an instance method extending URL and add a completion handler to it:

extension URL {
    func asyncImage(completion: @escaping (UIImage?) -> Void) {
        URLSession.shared.dataTask(with: self) { data, _, _ in
            guard let data = data, let image = UIImage(data: data) else {
                completion(nil)
                return
            }
            completion(image)
        }.resume()
    }
}

Usage:

yourURL.asyncImage { image in 
    guard let image = image else { return }
    // use image here
}

Another option is to extend UIImageView as shown in this post

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2