1

EDIT 3: Please also read my comment in the "answered" tagged answer. I think I won't use my synchronous method but change to the suggested asynchronous methods that were also given!

Ok I am struggling with some basic concepts of showing images from an URL from the internet on my app.

I use this code to show my image on an UIIamgeView in my ViewController:

func showImage() {
        let myUrlImage = URL(string: linkToTheImage)
        let image = try? Data(contentsOf: myUrlImage!)
        imageView1.image = UIImage(data: image!)
    }

Now basically I have the following question:

Is the whole image downloaded in this process? Or works the UIImageView like a "browser" in this case and doesn't download the whole picture but only "positions" the image from the URL into my UIImageView?

EDIT:

The reason I asked is, I am basically doing a quiz app and all I need in the view is an image from a URL for each question - so it's no difference if I do it asynchronous or synchronous because the user has to wait for the image anyways. I am more interested in how do I get the fastest result:

So I wanted to know if my code really downloads the picture as a whole from the URL or just "Positions" it into the UIImageView?

If in my code the picture is downloaded in its full resolution anyways, then you are right, I could download 10 pictures asynchronously when the player starts the quiz, so he hopefully doesn't have to wait after each answer as long as he would wait when I start downloading synchronously after each answer.

Edit 2: Because my Question was tagged as similar to another some more explanation: I already read about synchronous and asynchronous downloads, and I am aware of the downsides of synchronous loading.

I am more interested in a really basic question, and I get the feeling I had one basic thing really wrong: My initial thought was that if I open a link in my browser, for example this one,

https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/68dd54ca-60cf-4ef7-898b-26d7cbe48ec7/10-dithering-opt.jpg

the browser doesn't download the whole picture. But I guess this isn't the case? The whole picture is downloaded?

MakiCodes
  • 77
  • 1
  • 10

2 Answers2

4

Never use Data(contentsOf:) to display data from a remote URL. That initializer of Data is synchronous and is only meant to load local URLs into your app, not remote ones. Use URLSession.dataTask to download image data, just as you would with any other network request.

You can use below code to download an image from a remote URL asynchronously.

extension UIImage {
    static func downloadFromRemoteURL(_ url: URL, completion: @escaping (UIImage?,Error?)->()) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil, let image = UIImage(data: data) else {
                DispatchQueue.main.async{
                    completion(nil,error)
                }
                return
            }
            DispatchQueue.main.async() {
                completion(image,nil)
            }
        }.resume()
    }
}

Display the image in a UIImageView:

UIImage.downloadFromRemoteURL(yourURL, completion: { image, error in
    guard let image = image, error == nil else { print(error);return }
    imageView1.image = image
})
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Thanks a lot, this will be very helpful, but I am not sure if my question is answered... Pls have a look into my edit in the initial post, wanted to add it here into the comment, but it was too long... – MakiCodes Feb 17 '18 at 16:09
  • see my comment in the "answered" answer. I feel a bit bad because I might use your method now in my code...but the other answer really was focusing what I had questioned... Hope it is ok if I only give you an upvote... – MakiCodes Feb 17 '18 at 17:06
  • @MakiCodes it seems to me you misunderstand the meaning of the word _download_. The code you use downloads the image, whether it stores it for a longer time or not is a different question, the image itself is downloaded, even in browsers. Both in a browser and using your code, the image only gets downloaded into the RAM of your device and not into persistent storage. – Dávid Pásztor Feb 17 '18 at 18:10
  • yes I initially didn't get it right, but I think I got it now! – MakiCodes Feb 17 '18 at 21:32
1

You can do it this way. But in most cases it is better to download the image first by yourself and handle the displaying then (this is more or less what the OS is doing in the background). Also this method is more fail proof and allows you to respond to errors.

extension FileManager {

    open func secureCopyItem(at srcURL: URL, to dstURL: URL) -> Bool {
        do {
            if FileManager.default.fileExists(atPath: dstURL.path) {
                try FileManager.default.removeItem(at: dstURL)
            }
            try FileManager.default.copyItem(at: srcURL, to: dstURL)
        } catch (let error) {
            print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
            return false
        }
        return true
    }

}

func download() {
    let storagePathUrl = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("image.jpg")
    let imageUrl = "https://www.server.com/image.jpg"
    let urlRequest = URLRequest(url: URL(string: imageUrl)!)
    let task = URLSession.shared.downloadTask(with: urlRequest) { tempLocalUrl, response, error in
        guard error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            print("error")
            return
        }
        guard FileManager.default.secureCopyItem(at: tempLocalUrl!, to: storagePathUrl) else {
            print("error")
            return
        }
    }
    task.resume()
}
sundance
  • 2,930
  • 1
  • 20
  • 25
  • Thanks a lot, this will be very helpful, but I am not sure if my question is answered... Pls have a look into my edit in the initial post, wanted to add it here into the comment, but it was too long... – MakiCodes Feb 17 '18 at 16:16
  • See my comment in the "answered" answer. I feel a bit bad because I might use your method now in my code...but the other answer really was focusing what I had questioned... Hope it is ok if I only give you an upvote... – MakiCodes Feb 17 '18 at 17:06