Loading/Downloading Image from Url on Swift

Loading/Downloading image from URL on Swift

Xcode 8 or later • Swift 3 or later

Synchronously:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
imageView.contentMode = .scaleAspectFit
imageView.image = image
}

Asynchronously:

Create a method with a completion handler to get the image data from your url

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Create a method to download the image (start the task)

func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async() { [weak self] in
self?.imageView.image = UIImage(data: data)
}
}
}

Usage:

override func viewDidLoad() {
super.viewDidLoad()
print("Begin of code")
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
downloadImage(from: url)
print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
}

Extension:

extension UIImageView {
func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}

Usage:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

Swift: Display Image from URL

There's a few things with your code as it stands:

  1. You are using a lot of casting, which is not needed.
  2. You are treating your URL as a local file URL, which is not the case.
  3. You are never downloading the URL to be used by your image.

The first thing we are going to do is to declare your variable as let, as we are not going to modify it later.

let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")! // We can force unwrap because we are 100% certain the constructor will not return nil in this case.

Then we need to download the contents of that URL. We can do this with the URLSession object. When the completion handler is called, we will have a UIImage downloaded from the web.

// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)

// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}

Finally you need to call resume on the download task, otherwise your task will never start:

downloadPicTask.resume().

All this code may look a bit intimidating at first, but the URLSession APIs are block based so they can work asynchronously - If you block your UI thread for a few seconds, the OS will kill your app.

Your full code should look like this:

let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")!

// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)

// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}

downloadPicTask.resume()

How to display Image from a url in SwiftUI

iOS 15 update:

you can use asyncImage in this way:
AsyncImage(url: URL(string: "https://your_image_url_address"))

more info on Apple developers document:
AsyncImage

Using ObservableObject (Before iOS 15)

first you need to fetch image from url :

class ImageLoader: ObservableObject {
var didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}

init(urlString:String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
}
}
task.resume()
}
}

you can put this as a part of your Webservice class function too.

then in your ContentView struct you can set @State image in this way :

struct ImageView: View {
@ObservedObject var imageLoader:ImageLoader
@State var image:UIImage = UIImage()

init(withURL url:String) {
imageLoader = ImageLoader(urlString:url)
}

var body: some View {

Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:100, height:100)
.onReceive(imageLoader.didChange) { data in
self.image = UIImage(data: data) ?? UIImage()
}
}
}

Also, this tutorial is a good reference if you need more

Display image from URL, Swift 4.2

First of all I'm pretty sure that in half a year you will find Objective-C very complicated and difficult. /p>

Second of all even your ObjC code is discouraged. Don't load data from a remote URL with synchronous Data(contentsOf method. Regardless of the language use an asynchronous way like (NS)URLSession.

And don't use Foundation collection types NSArray and NSDictionary in Swift. Basically don't use NS... classes at all if there is a native Swift counterpart.

In Swift 4 you can easily decode the JSON with the Decodable protocol directly into a (Swift) struct,

the URL string can be even decoded as URL.

Create a struct

struct Item: Decodable {
// let copyright, date, explanation: String
// let hdurl: String
// let mediaType, serviceVersion, title: String
let url: URL
}

Uncomment the lines if you need more than the URL.

And load the data with two data tasks.

let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")! 

let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
// this line is only needed if all JSON keys are decoded
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Item.self, from: data!)
let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
if let imageError = imageError { print(imageError); return }
DispatchQueue.main.async {
let apodImage = UIImage(data: imageData!)
let apodView = UIImageView(image: apodImage)
// do something with the image view
}
}
imageTask.resume()
} catch { print(error) }
}
task.resume()

swift Image from URL download to local storage

You shouldn't use Data(contentsOf:) initializer to fetch non local resources. If you are trying to save it to disk there is no need to download it to memory. You can use URLSession downloadTask method to download your file asynchronously direct to disk:


import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

if let url = URL(string: "https://i.stack.imgur.com/xnZXF.jpg") {
URLSession.shared.downloadTask(with: url) { location, response, error in
guard let location = location else {
print("download error:", error ?? "")
return
}
// move the downloaded file from the temporary location url to your app documents directory
do {
try FileManager.default.moveItem(at: location, to: documents.appendingPathComponent(response?.suggestedFilename ?? url.lastPathComponent))
} catch {
print(error)
}
}.resume()
}

How do I know in swift 5 if the image was actually downloaded from an url?

You can simply check like this before setting the image in your code:

DispatchQueue.main.async {
if let image = UIImage(data: data) { //Image is available in the url
imageView.image = image
self?.scrollView.addSubview(imageView)
}else {
print("Image not available in this url")
}
}

Keep Coding........... :)

How to download image with URL Swift 3?

There's an excellent example of how to do this in Leo Dabus's answer here. I'll include the relevant bits to your question below.

Using code from that post, I've found one of the easier and cleaner ways is to add an extension to UIImageView:

extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { () -> Void in
self.image = image
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
}

Then you can just grab the image from your view controller using:

if let url = URL.init(string: urlString) {
imageView.downloadedFrom(url: url)
}

Downloading jpg image from url with URLSession

In general, it's a good idea to do asynchronous tasks like this in a ObservableObject rather than the View itself.

You're already doing the downloading -- all you need to do now is save the data:

class Downloader : ObservableObject {
func downloadImage() {
let imageUrlStr = "https://media.wired.com/photos/5f2d7c2191d87e6680b80936/16:9/w_2400,h_1350,c_limit/Science_climatedesk_453801484.jpg".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let task = URLSession.shared.dataTask(with: URLRequest(url: URL(string: imageUrlStr)!), completionHandler: {(data, response, error) -> Void in

guard let data = data else {
print("No image data")
return
}

do {
try data.write(to: self.getDocumentsDirectory().appendingPathComponent("image.jpg"))
print("Image saved to: ",self.getDocumentsDirectory())
} catch {
print(error)
}

})
// Start the download.
task.resume()
}

private func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}

struct ContentView : View {
@StateObject private var downloader = Downloader()

var body : some View {
Button("Download") {
downloader.downloadImage()
}
}
}

If you run this in the simulator, you can see the output to the console with the location of the directory. If you open that in Finder, you'll see your image has been saved there.

Note that to see the directory and file in the Files app, you'll need to make sure that you've set UIFileSharingEnabled to YES in your Info.plist



Related Topics



Leave a reply



Submit