How can I know the progress of image download?(Swift, UIKit) - ios

Question
I am trying to download a large image from the web with the code below. I want to show how much download has been done using the progress bar. Currently, it is only possible to know whether the download was successful or not.
func downloadBigImg() -> UIImage? {
let imgURL = URL(string: "https://picsum.photos/1024")!
guard let data = try? Data(contentsOf: imgURL) else { return nil }
let image = UIImage(data: data)
return image
}
What I did
Using asynchronous operation, the progress bar was raised during the download of the image. But that Progress Bar is a fake. It's not really a progress. It just adds a very small number which isn't related to the actual progress. When the image download is completed, it will be changed to 100%.

Related

UIImage appears 2-3 seconds late on viewController swift

I have a UIImageView that I use for users profile pic. But, the image appears late on screen like 2 to 3 seconds. Here is how I download it:
#IBOutlet weak var ProfilePic: UIImageView!
override func awakeFromNib() {
FriendSystem.system.USER_REF.child(user.uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild("imageUrl"){
self.firebaseStorage.child("users").child(FriendSystem.system.CURRENT_USER_ID).getData(maxSize: 10*1024*1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
self.ProfilePic.image = userPhoto
})
}
})
}
How can I solve this?
The code you're running runs on a different Thread, and according to Apple, all UI updates must run on the Main Thread. Here's a link that explains why all UI updates must be on the Main Thread
So in your code you just have to use DispatchQueue like this
DispatchQueue.main.async {
// please don't use implicit unwrapping of variables `data!` cause it may cause crashes, use if let statements instead
if let data = data {
let userPhoto = UIImage(data: data)
self.ProfilePic.image = userPhoto
}
}
On a different note:
Please read on Swift coding standards in this link
Well i actually solved it and I will post it here if newbies like me encounter the same problem. I used Firebase and SDWebImage. Here is the link This is from Firebase's official website.
// Reference to an image file in Firebase Storage
let reference = storageRef.child("images/stars.jpg")
// UIImageView in your ViewController
let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)

Swift 3 DispatchQueue.global (download images from Firebase and UI frozen)

I have a problema with this method:
func DownloadImages(uid: String, indice: Int) {
DispatchQueue.global(qos: .background).async {
let refBBDD = FIRDatabase.database().reference().child("users").child(uid)
refBBDD.observeSingleEvent(of: .value, with: { snapshot in
let snapshotValue = snapshot.value as? NSDictionary
let profileImageUrl = snapshotValue?.value(forKey: "profileImageUrl") as! String
let storage = FIRStorage.storage()
var reference: FIRStorageReference!
if(profileImageUrl == "") {
return
}
print("before")
reference = storage.reference(forURL: profileImageUrl)
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data)
print("image yet dowload ")
self.citas[indice].image = image
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadRows(at: [IndexPath(row: indice, section: 0)], with: .none)
//self.tableView.reloadData()
print("image loaded")
})
}
print("after")
})
}
}
I want to download images in background mode. I want follow using app, but the UI has frozen until methods not entry in reloadRows.
Is it possible run in true background mode and can i follow using the app??
Trace program:
before
after
before
after
...
before
after
before
after
image yet dowload --> here start UI frozen
image loaded
image yet dowload
image yet dowload
...
image yet dowload
image yet dowload
image loaded
image loaded
...
image loaded
image loaded
image yet dowload ------> here UI is not frozen
image loaded
The problem is caused by this line: let data = NSData(contentsOf: url!).
That constructor of NSData should only be used to read the contents of a local URL (a file path on the iOS device), since it is a synchronous method and hence if you call it to download a file from a URL, it will be blocking the UI for a long time.
I have never used Firebase, but looking at the documentation, it seems to me that you are using the wrong method to download that file. You should be using func getData(maxSize size: Int64, completion: #escaping (Data?, Error?) -> Void) -> StorageDownloadTask instead of func downloadURL(completion: #escaping (URL?, Error?) -> Void), since as the documentation states, the latter only "retrieves a long lived download URL with a revokable token", but doesn't download the contents of the URL itself.
Moreover, you shouldn't be force unwrapping values in the completion handler of a network request. Network requests can often fail for reasons other than a programming error, but if you don't handle those errors gracefully, your program will crash.
Your problem is in this line let data = NSData(contentsOf: url!) and let's now see what apple says about this method below
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.
So it clearly states that this method will block your User Interface.

iOS 10 Notification Content Extension: using NSURLSession?

I'm trying to create a new Notification Content Extension in iOS 10 for local notifications where the notification view controller responsible for the content extension downloads an image from the network and renders it in a UIImageView. I have the Notification Content Extension target set up with an appropriate Info.plist and the content extension works beautifully for simple things like rendering a label with some content, for example the sample code that comes in the template:
func didReceive(_ notification: UNNotification) {
self.label.text = notification.request.content.body
}
However, when I try to introduce NSURLSession (or URLSession in Swift 3) into the mix, the notification content totally fails to load - not even the label gets set anymore:
func didReceive(_ notification: UNNotification) {
self.label.text = notification.request.content.body
let session = URLSession.shared()
let url = URL(string: "https://chart.googleapis.com/chart?cht=p3&chs=250x100&chd=t:60,40&chl=Hello|World")!
let task = session.downloadTask(with: url) { (fileURL, response, error) in
if let path = fileURL?.path {
DispatchQueue.main.async {
self.imageView.image = UIImage(contentsOfFile:path)
}
}
}
task.resume()
}
Is the usage of NSURLSession in the notification content extension not allowed? Is my extension possibly being killed before the download completes? If so, how could I ensure that it's not killed so I can download and render the image?
At the point when func didReceive(_ notification: UNNotification) is called in the Content extension, any modifications to the content such as downloading images should have already occurred.
It seems that you use a Notification Service Extension to do the downloading of any additional content. The Notification Content extension is only responsible for providing a custom user interface if you need one.
In your Service extension you download the image using the url in the notification payload, and set it as an attachment on the UNNotification object. If you don't need any custom UI, the system will automatically display visual media attachments like a video or an image. If that suits your needs, you don't actually need a Notification Content Extension at all.
Pusher provide a great tutorial on setting up a Notification Service Extension for handling media attachments in push notifications on iOS 10 right here.
It's actually possible to download images in a Notification Content Extension. However, your code contains two problems:
The URL is not valid.
The fileURL returned by the downloadTask methode is deleted as soon as the function runs out of scope, which is already the case when you try to access the fileURLfrom another thread. Instead it's better to catch the fileURL content in a data variable and use that to produce an image in the main thread
Slightly corrected code:
guard let url = URL(string: "https://betamagic.nl/images/coredatalab_hero_01.jpg") else {
return
}
let task = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
if let fileURL = fileURL,
let data = try? Data(contentsOf: fileURL) {
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}
}
task.resume()
Disable app transport security in Info.plist for you extension.
Tip: move file from tmp folder to cache for save

UIImage Gif loading delegate?

I am using this guy's Gif class to create a Gif UIImage. https://github.com/mayoff/uiimage-from-animated-gif
Load it with this :
let fileUrl = NSURL(string: "some url" as! String)
let Gif = UIImage.animatedImageWithAnimatedGIFURL(fileUrl!)
imageview.image=Gif
The thing is that it takes time, a lot of time, like 7 seconds till you see the image.
I would like to preload it in some way, or at least get a delegate when finish loading.
What are my options here ?
You can use Grand Central Dispatch (or GCD) to perform this task in the background.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// This will happen in the background
let fileUrl = NSURL(string: "some url" as! String)
let gif = UIImage.animatedImageWithAnimatedGIFURL(fileUrl!)
dispatch_async(dispatch_get_main_queue()) {
// Always update UI on the main thread
imageview.image = gif
}
}
This will still take however long it takes to load the gif but it won't lock up the UI so the user can do things while the gif loads.

Show thumbnail image while larger image is downloading asynchronously in Swift

I am using Haneke to download and show images asynchronously in CollectionView.
I want to show a smaller thumbnail type of image while the larger image is being downloaded for each cell.
Here's the code:
let imageUrl = NSURL(string: "imgUrlString")
let miniImageUrl = NSURL(string: "miniImgUrlString")
func onSuccess(image: UIImage?) {
cell!.imageView.hnk_setImageFromURL(imageUrl!)
}
cell!.imageView.hnk_setImageFromURL(miniImageUrl!, placeholder: nil, format: nil, failure: onFailure, success: onSuccess)
Also there is preloader image set on ImageView of Cell just after dequeing it.
cell?.imageView.image = UIImage(named: "preloader")
The Problem
The onSuccess handler is called, but the mini image is never visible. The image changes from "preloader" to "image", while I want the mini image to load soon and then change to original image.
What am I missing?

Resources