Search code examples
swiftsdwebimage

How to use SDWebImage to download and save CGImage


I'm trying to use SDWebImage to download and image from an external url and return it. I do not want to set it on a view. This is the code I'm using, but it's not working. I'm returning nil. But I know the url I'm passing in works because I can see it in the browser. What am I doing wrong?

func downloadImage() -> CGImage {
   var myImage: CGImage?
   let myUrl = URL(string: "my-url-here.com")

   SDWebImageDownloader.shared.downloadImage(with: myUrl, completed: { (image, data, error, true) in
       print("Completed")
       if image != nil {
           myImage = image?.cgImage
       }
   })

   return myImage!
}

I also tried this version, also with no luck:

func downloadImage() -> CGImage {
    var myImage: CGImage?
    let myUrl = URL(string: "my-url-here.com")

    SDWebImageManager.shared.loadImage(with: myUrl, options: .continueInBackground, progress: { (received, expected, nil) in
        print(received, expected)
    }, completed: { (downloadedImage, data, error, SDImageCacheType, true, imageUrlString) in
        DispatchQueue.main.async {
            if downloadedImage != nil {
                myImage = downloadedImage?.cgImage
            }
        }
    })

    return myImage!
}

Solution

  • SDWebImage is an asynchronous library. You can’t just return the results. Generally one would use an @escaping closure to supply the results to the caller. E.g.

    func downloadImage(completion: @escaping(CGImage?) -> Void) {
        let url = URL(string: "https://my-url-here.com")!
    
        SDWebImageDownloader.shared.downloadImage(with: url) { image, _, _, _ in
            completion(image?.cgImage)
        }
    }
    

    And you’d use it like:

    downloadImage { image in
        guard let image = image else { return }
    
        // use image here
    }
    
    // but not here
    

    But let’s step back and look at the whole pattern. You say you want to “save” the result. If you’re talking about saving it to persistent storage, you would not want to use CGImage (or UIImage or whatever) at all. That’s computationally inefficient (converting asset to image and then back to Data so you can save it), space inefficient (you have to load the whole asset into memory at the same time), and likely introduces problems (e.g. if you download a JPG, convert to CGImage and then try to recreate a JPG, the resulting asset will be slightly different, bigger, and/or with new JPG artifacts). If you’re just pre-downloading assets, just use a simple networking library like Alamofire or URLSession.