Function doesnt wait download Picture with Alamofire in Swift - ios

I try to download picture with Alamofire and this image must be append to custom class. But I didnt do that. Image is downloaded with async and i didnt fix this issue. Where is my mistake ?
Image always return nil
// MARK: CUSTOM FUNCTION
import UIKit
import Alamofire
import AlamofireImage
class DownloadPhotoWAlamofire {
func download(imageUid:String , completion : #escaping (UIImage) -> () ) {
let url = "\(Config.fileService)file?uid=\(String(describing: imageUid))"
print("URL \(url)")
Alamofire.request(url, method: .get ).responseImage { response in
print("Image Response \(response)")
let image = response.result.value
completion(image!)
}
}
}
// MARK : IN VIEWCONTROL
guard let objectElement = o as? [String:Any] else {return}
let managerName = objectElement["managerName"] as? String
let managerAvatarUid = objectElement["managerProfilePictureFileUid"] as? String
let assistantsOfDiariesUid = objectElement["assistantsOfDiariesUid"] as? String
var image:UIImage? = nil
if managerAvatarUid != nil {
DownloadPhotoWAlamofire().download(imageUid: managerAvatarUid!, completion: { (imageD) in
image = imageD
})
}
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
self.managerList.append(e)

Change it to :
if managerAvatarUid != nil {
DownloadPhotoWAlamofire().download(imageUid: managerAvatarUid!, completion: { (imageD) in
let image = imageD
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
self.managerList.append(e)
})
}
As downloading is asynchronous call and you this line is executing before image is downloaded :
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
at that time image is nil. So call it only when you have image downloaded and it should work.
Also don't unwrap optional with "!", do a if let/ guard let. So change Almofire code to :
Alamofire.request(url, method: .get ).responseImage { response in
print("Image Response \(response)")
if let image = response.result.value {
print("image downloaded: \(image)")
completion(image)
} else {
print("Image is nil")
}
}

You set the image directly after starting the asynchronous download, when it of course is still nil. The point of using a callback as you do in your code is that you only can use a requested resource once the callback gets called; so you should move the code that's using the image into the callback block.

Related

iOS, Swift : Download multiple file serially and showing single progress bar for all file as a one progress

I am downloading the zip file using the Alamofire in my ios(swift4) application. I am able to download the file serially using Alamofire.
But I also want to show the one progress bar for the all the downloaded files. Means If I have 4 zip files and when all the file is downloaded then the progress should be 100%.
Till I have tried below code, which gives a progress value for each of the url and progress is shown as one file downloaded showing progress as 100% then again it start from 0 for second url and when second url is downloaded then progress is shown 100% complete.
Please guide me for this. I want to get progress value as 100% when all the files are downloaded using Alamofire.
Can it be possible with Alamofire?
CODE:
func serialZipFileDownload(downloadPath: String){
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let name = self.offlineDownloadFileName?[urlCount]
let currentVideoURL = documentsURL.appendingPathComponent(name ?? "Default.zip")
let str = downloadPath
let urlString = str.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url = URL(string: urlString ?? "")
if super.isConnectedToNetwork() == true {
let manager = Alamofire.SessionManager.default
let headers = ["Accept-Encoding" : ""]
manager.request(url ?? "", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).downloadProgress { (progress) in
print(progress.fractionCompleted)
DispatchQueue.main.async{
self.progressDownload.setProgress((Float(progress.fractionCompleted)), animated: true)
let per = round((Float(progress.fractionCompleted)) * 100)
self.lblDownloadPercent.text = "\(Int(per))%"
}
}.responseData { (response) in
switch (response.result){
case .success(_) :
print(response)
print(response.result.value!)
print(response.result.description)
if let data = response.result.value {
do {
try data.write(to: currentVideoURL)
self.showToast(message: "File downloaded successfully")
}
catch {
print("Something went wrong!")
}
}
case .failure(let error) :
print(response)
if error._code == NSURLErrorNetworkConnectionLost {
DispatchQueue.main.async {
super.showPopup(title: msgStruct.networkTitle, message: msgStruct.noInternet)
}
}
else if error._code == NSURLErrorTimedOut {
DispatchQueue.main.async {
super.showPopup(title: msgStruct.networkTitle, message: msgStruct.noInternet)
}
}
else if error._code == NSURLErrorDownloadDecodingFailedMidStream {
print("error",error.localizedDescription)
}
break
}
}
}
else{
super.showPopup(title: msgStruct.networkTitle, message: msgStruct.noInternet)
}
You can do it like this:
Maintain a global variable - tatalPercentage some other swift class.
static let tatalPercentage = 0
tatalPercentage = tatalPercentage + Int(per/4)
self.lblDownloadPercent.text = "\(tatalPercentage) %"

How i can see the image inside UIImageView?

I have an array filled with parsed json data including image url's. But when i try to see that images inside uiimageview, it doesn't the show. What should i do
I printed the url. This is my url inside array.
This is my array
var feedResult = [Result]()
It shows the name inside collectionview but i couldn't see the images. I used named like everybody does. But what is missing?
let info = feedResult[indexPath.row]
cell.appLabel.text = info.artistName
cell.customCollectionImage.image = UIImage(named: info.artWorkUrl)
You have to download the image Data using the url you got, only then you will use the downloaded data, like so:
imageView.image = UIImage(data: downloadedData)
Here is a quick subclass of UIImageView that does the downloading:
class URLImageView: UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Update-1 Use the download function using UIImageView extension, without subclassing, like so:
extension UIImageView {
func download(url urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (downloadedData, _, error) in
guard error == nil && downloadedData != nil else { return }
DispatchQueue.main.async{
self.image = UIImage(data: downloadedData!)
}
}
task.resume()
}
}
Usage:
cell.customCollectionImage.download(url: info.artWorkUrl)
By using UIImage(named: info.artWorkUrl) you are not accessing the image in your array but the images in your Assets.xcassets (assets that you add manually in your project).
You need to download the image from the artWorkUrl and then directly use the downloaded image like this:
cell.customCollectionImage.image = UIImage(data: yourImageData)
Where yourImageData is what you have downloaded from the server with the artWorkUrl.

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 }
}

Downloading UIImage via AlamofireImage? [duplicate]

This question already has answers here:
How can I get the Data from NSURLSession.sharedSession().dataTaskWithRequest
(2 answers)
Closed 5 years ago.
I have a URL and want to download the image via a return function, however I cant get it to cooperate properly, here is my func:
func getTabImage(url: URL) -> UIImage {
Alamofire.request(url)
.responseImage { response in
if let image = response.result.value {
return image
} else {
print("Failed to get image")
}
}
}
I pass in the URL, and want a UIImage returned from the alamofire response.
But i get
Unexpected non-void return value in void function
for the return statement.
How can i achieve this correctly?
You can use the below function for downloading the image:
func getImage(_ url:String,handler: #escaping (UIImage?)->Void) {
print(url)
Alamofire.request(url, method: .get).responseImage { response in
if let data = response.result.value {
handler(data)
} else {
handler(nil)
}
}
}
Uses
getImage("http://") { (image) in
if image != nil {
print(image)
}
}
Or
If you want to set the image on UIImageView use extension of AlamofireImage.
if let imageURL = URL(string: "http://"), let placeholder = UIImage(named: "default") {
imageView.af_setImage(withURL: imageURL, placeholderImage: placeholder) //set image automatically when download compelete.
}

Firebase reference is 'variable not available' when downloading picture in Swift

Title says everything. I'm just unable to download an image from Firebase Storage dir. Here is the snippet of the code which calls the function for setting data and it also calls the function which tries to download the picture:
for element in Dict {
if let itemDict = element.value as? [String:AnyObject]{
let name = itemDict["name"] as! String
let price = itemDict["price"] as! Float
let imageObject = itemDict["image"] as! NSDictionary
let hash = imageObject["hash"] as! String
let storageDir = imageObject["storageDir"] as! String
let image:UIImage = self.downloadImageProductFromFirebase(append: hash)!
let product = Product(name: name, image: image, imageName:hash, price: price, storageDir : storageDir)
self.productList.append(product)
}
}
print(Dict)
self.myTable.reloadData()
And here is the code which tries to download the image:
func downloadImageProductFromFirebase(append:String) -> UIImage?{
let gsReference = Storage.storage().reference(forURL: "gs://fridgeapp-3e2c6.appspot.com/productImages/productImages/" + append)
var image : UIImage?
gsReference.downloadURL(completion: { (url, error) in
if error != nil {
print(error.debugDescription)
return
}
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error.debugDescription)
return
}
guard let imageData = UIImage(data: data!) else { return }
DispatchQueue.main.async {
image = imageData
}
}).resume()
})
return image
}
But, for some reason, it crashes just when calling this last function, saying that "fatal error: unexpectedly found nil while unwrapping an Optional value". I tried to use the debugger, and I found out that Firebase reference to Storage variable says "variable not available".
Could someone of you guys help me with this? I think I read the Firebase doc about a hundred times, and still can't get the point.
Thank you!
Downloading an image from a remote server is an asynchronous task, that means that the result is not immediately available. This is the reason that gsReference.downloadURL accepts a completion callback as an argument, and has no return value.
Since your function (downloadImageProductFromFirebase) is simply a wrapper to gsReference.downloadURL, it should also accept a completion callback as an argument, and should not have a return value (i.e. remove the -> UIImage?).
When you call self.downloadImageProductFromFirebase pass in a closure that receives the image, finds the index of the corresponding product in productList, and sets itself as the cell's image (assuming you're showing the image in the cell).
See this answer for how to asynchronously set cell images.

Resources