Downloaded image doesn't always show up immediately - ios

For some reason my photoimg will not update consistantly , sometimes it does sometimes it doesn't.
I'm pretty sure it has something to do with async calls but I've been stuck trying to figure out the root reason why its not updating. So this is in my mainVC and for a user to upload/update image they go to the settingsVC and when they segue back sometimes it shows to the updated image, other times still shows the old image , other times showing nothing . But oddly if I click on my settings and dismiss it then the image will show updated.
So I think my issue lies where I'm calling my method and my async queue.
func fetchProfileImage() {
Dataservice.dataService.USERS_REF_CURRENT_PROFILE_IMAGE.downloadURL { (url, error) in
if error != nil {
}
else {
let url = url?.downloadURL
URLSession.shared.dataTask(with: url!, completionHandler: { (data, resonse, error) in
if error != nil {
print("Fetching did not download \(error.debugDescription)")
}
if let data = data {
print("Fetching Image did download data")
DispatchQueue.main.async {
self.profilePhoto.image = UIImage(data: data)
}
}
}).resume()
}
}
}

Why not just use the built in download mechanism, which always presents callbacks on the main thread*:
let image: UIImage!
let ref = FIRStorage.storage().reference(forURL: Dataservice.dataService.USERS_REF_CURRENT_PROFILE_IMAGE)
ref.data(withMaxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for your profile image is returned
image = UIImage(data: data!)
}
}
*unless you explicitly change the thread by providing your own queue ;)

after you perform async, you need to back to main thread to set the download image
DispatchQueue.main.async {
DispatchQueue.main.sync {
self.profilePhoto.image = UIImage(data: data)
}
}

Related

Completion Handler to trigger collectionView Reload once Images are downloaded - Xcode [SWIFT]

I'm using a code I got off here to download some images and present them in a collection view.
Heres the code:
func downloadImage (url: URL, completion: () -> Void){
let session = URLSession(configuration: .default)
let downloadPicTask = session.dataTask(with: url) { (data, response, error) in
if let e = error {
print("Error downloading image \(e)")
} else {
if let res = response as? HTTPURLResponse {
print("Downloaded image with response code \(res.statusCode)")
if let imageData = data {
let image = UIImage(data: imageData)
self.globalImages.append(image!)
print("what does this say?-->", self.globalImages.count)
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
completion()
downloadPicTask.resume()
}
And I'm calling the download image in view did load where URL is the URL. (this URL works and I can download image).
downloadImage(url: url) { () -> () in
collectionView.ReloadData()
}
The completion handler I've tried calls reloadData() way too soon. I'm wanting it to be called when the image is finished downloading? so then the image can be displayed as soon as it's downloaded.
What is wrong with this code?
Please help!
You would call the completion handler as soon as you have the image. So, in your code:
if let imageData = data {
let image = UIImage(data: imageData)
self.globalImages.append(image!)
print("what does this say?-->", self.globalImages.count)
// call the completion handler here
Be aware, though, that you are likely to have other issues caused by data sharing across multiple threads, and also that your idea of storing the downloaded images successively in an array (globalImages) is not likely to work correctly.

Add an image as an accessory in your UITableView in Swift 3

In my project, I show a UITableView, which currently has text describing a show's name and genre loading from a remote JSON file.
That all works. What I want next is to use the URL from the JSON file and load a thumbnail next to each show.
Using a tutorial, I have added a function to download the remote image with a print to test if it's successful.
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let show_name = shows_obj["show"] as? String
let show_genre = shows_obj["genre"] as? String
let show_image = shows_obj["thumbnail"] as? String
TableData.append(show_name! + " | " + show_genre!)
let testPictureURL = URL(string: show_image!)!
let session = URLSession(configuration: .default)
// Here's the download task where I'm grabbing the image
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
if let res = response as? HTTPURLResponse {
print("Downloaded picture with response code \(res.statusCode)")
if let imageData = data {
// Now I know I have data, so I think I can use UIImage to convert it into an image
let image = UIImage(data: imageData)
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()
}
There are three items in the JSON array, and I get three printed statements that the picture was download: but the image does not appear.
My theory: since this is a table, maybe I have to add this as an accessory, but there isn't an image accessory subclass.
I am new to Swift -- do you have any ideas about how I should append this uploaded image to the table.
This is probably being caused by the asynchronous behavior of URLSession so when the requested image returns the view is already loaded.
To solve that, you can use a callback, for instance:
func myFunction(completion: (returnedImage -> UIIMage) -> Void){
//...
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
//...
let image = UIImage(data: imageData)
completion(returnedImage: image)
//...
}
downloadPicTask.resume()
}
}
By using a callback, let's say that you have a method called myFunction(completion:), so now when you call the method you can handle whatever comes back from completion:
myFunction { (image) in
DispatchQueue.main.async { cell.imageView.image = image }
}

Firebase Object Not Found

Guys i was making a simple application using Firebase and i was uploading an image and also downloading it sometime later. Uploading works perfectly well but when i try to download an error occurs that says. Object profile_pics not found. I have created a folder named profile_pics and i store the image inside that folder. The image is there but gives error while downloading. Here is the screen shot.
Here is what is in the console.
Error -: Object profile_pics does not exist.
Here is the code to download the image.
let storage = FIRStorage.storage().reference(forURL: "gs://dd-dd-dd.appspot.com/profile_pics/\(profileURL)")
print("Path Full \(storage.fullPath)")
storage.data(withMaxSize: 11 * 1024 * 1024, completion: {
(data, error) in
if let error = error{
print("Error -: \(error.localizedDescription)")
}
else{
let image = UIImage(data: data!)
cell.profileImageView.image = image
}
})
What could be the possible error? If you please guide me through this.
I recover it this way.
let url = URL(string: "\(ImageUrlSavedinDatabase)")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.sync() {
self.profilepicimageview.image = UIImage(data: data)
self.actIndicator.stopAnimating()
}
}
task.resume()
Does that work?
Niall

Firebase storage - PutData and download not being called when expected

I'm making an app that involves an image view, the first time the user logs in they upload a photo to be their photo, like so:
let storage = FIRStorage.storage()
let storageRef = storage.referenceForURL("gs:xxxxxxxxxxxxxxxx")
let picRef = storageRef.child("images/" + "myname" + ".jpg")
let data = UIImagePNGRepresentation(pickedImage)
forceUpload(profPicRef, data: data!)
let uploadTask = picRef.putData(data, metadata: nil) { metadata, error in
if (error != nil) {
print(error)
} else {
print("uploaded")
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata!.downloadURL
}
}
}
and then the next time they come back to this view controller, in ViewDidAppear I have this code that should download the picture and put it into the image View:
picRef.dataWithMaxSize(1 * 4096 * 4096) { (data, error) -> Void in
print("in download")
if (error != nil) {
print(error)
} else {
retPic = UIImage(data: data!)!
and then I set retPic to be the image view's image. The problem is, these methods are seemingly called at random. WHen I first go into this view controller, no picture appears in the image view and "in download" isn't printed. Also when I upload a picture, it isn't always uploaded and "uploaded" doesn't always print. After switching back and forth into this view controller again it eventually seems to load at random. I am wondering why these putData and dataWithMaxSize functions are called at seemingly random times, and how to force them to occur when I want them too. I had this issue with snapshots too, and moving them into ViewWillAppear seemed to work, but not here.
Thanks!

How to download and view images from the new Firebase Storage?

I am able to upload images to Firebase Storage but I am having trouble downloading them. This is my code to download images:
let storage = FIRStorage.storage()
let localURL : NSURL! = NSURL(string: "file:///Documents/co.png")
// i also tried let localURL : NSURL! = NSURL.fileURLWithPath("file:///Documents/co.png")
func download() {
let storageRef = storage.referenceForURL("gs://project-5547819591027666607.appspot.com")
let imageRef = storageRef.child("co.png")
let downloadTask = imageRef.writeToFile(localURL) { (URL, error) -> Void in
if (error != nil) {
print(error?.localizedDescription)
}
else {
self.imageView.image = UIImage(data: data!)
}
}
}
I am receiving - Optional("An unknown error occurred, please check the server response.")
Also once I get them downloaded How would I view that image?
For trying to see if the image was downloaded I created a UIImageView and set an outlet for it in storyboard called "imageView" then set the downloaded image to the UIImageView.
self.imageView.image = UIImage(data: data!)
Try
first getting reference to the image you want to download using
let reference = FIRStorage.storage().reference("uploads/sample.jpg")
If you know the size of image is low - like 1-2 mb max . download the image in memory
reference.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print(error)
} else {
let myImage: UIImage! = UIImage(data: data!)
}
}
This will be the quickest and easy way to download directly from Firebase Storage.
However there are cases when you want the progress blocks and certain other things like caching. In such cases you could use any third party like Alamofire to download the image from the url you get from Firebase Storage.
To get the url do something like this
reference.downloadURLWithCompletion { (URL, error) -> Void in
if (error != nil) {
// Handle any errors
} else {
print(URL)
// download image using NSURLSession or Alamofire
}
}

Resources