Trouble retrieving images with Alamofire, some images load, some don't (SWIFT) - iphone

So I'm kind of stuck here. I have a mobile app that has a "feed" using a TableView and it's populated with pictures, descriptions, and likes from firebase. I use ImageShack for the image upload API, and having the imgLinks grabbed from there (with alamofire). the images are uploading find, and on my ImageShack panel, the pictures look great. So I know it's not firebase, and not imageshack's fault, but most pictures won't even load, they are just white boxes, and I can confirm that imageUrl != nil, so it's definitely a request problem with Alamofire.
Here is my Alamofire GET request code
if post.imageUrl != nil {
if img != nil {
self.deckImg.image = img
} else {
request = Alamofire.request(.GET, post.imageUrl!).validate(contentType: ["image/*"]).response(completionHandler: { request, response, data, err in
if err == nil {
let img = UIImage(data: data!)!
self.deckImg.image = img
FeedController.imageCache.setObject(img, forKey: self.post.imageUrl!)
} else {
print(err.debugDescription)
}
})
}
} else {
self.deckImg.hidden = true
}
Also, I can confirm that it's not hiding the image, because when self.deckImg.hidden = true, I have the rows adjust height, and I can also see that that's never called, because it never even hit that breakpoint. So it's simply something to do with Alamofire's get request .. what is it :(

I think I know what is the problem, as I was stuck myself for days on end.
I take it you are using a custom tableViewCell VC and that within the function that binds the data you put something like:
self.deckImg.image = nil
and then check if there is a cached image, if not download it with alamofire
after you check that the post.imageUrl != nil {}, you have to unhide the self.deckImg:
if post.imageURL != nil
{
self.deckImg.hidden = false
if img != nil
{
self.deckImg.image = img
}
else
{
request = Alamofire.request(.GET, post.imageUrl!).validate(contentType: ["image/*"]).response(completionHandler: { request, response, data, err in
if err == nil {
let img = UIImage(data: data!)!
self.deckImg.image = img
FeedController.imageCache.setObject(img, forKey: self.post.imageUrl!)
} else {
print(err.debugDescription)
}
})
}
}
else
{
self.deckImg.hidden = true
}

Related

Using Firebase Storage for Uploading Images

I can only upload a single photo to FirebaseStorage.
All the tutorials I have seen on youtube use FirebaseStorage to save user's profile pictures. However, I am trying to make a restaurant app where I should be able to keep several pictures and not only one. My code right now only allows me to keep one. As soon as I try to upload a second image, it erases the first one.
Here's my code:
#IBAction func agregarComidaFav(_ sender: Any) {
guard let imageData = imagen?.jpegData(compressionQuality: 0.4) else {
return
}
let storageRef = Storage.storage().reference(forURL: "gs://comidas-68043.appspot.com")
let storageProfileRef = storageRef.child("ComidasFavoritas")
let metadata = StorageMetadata()
metadata.contentType = "image/png"
storageRef.child("comidasFavoritas/png").putData(imageData, metadata: metadata) { (storageMetaData, error) in
if error != nil {
print(error?.localizedDescription ?? "")
return
}
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageUrl = url?.absoluteString {
print(metaImageUrl)
self.imagenURL = metaImageUrl
}
})
var ref: DocumentReference? = nil
ref = db.collection("comidasFavoritas").addDocument(data: [
"imagenURL" : imagenURL
]) { err in
if let err = err {
print("Error agregando comida: \(err)")
} else {
let id = ref!.documentID
print("Comida agregado con ID: \(id)")
}
}
}
This line:
storageRef.child("comidasFavoritas/png").putData(...)
...guarantees that each time, the data is going to be written to "comidasFavoritas/png"
I don't know anything about how you're keeping track of your data/images in your app, but you'll probably need some way to keep track of an ID for that image. Then, when you store it, you could store it like:
storageRef.child("comidasFavoritas/png\(imageId)").putData(...)
You'd probably also want to store that ID in your database when you store the image URL.

Swift: upload multiple images to Firestore and assign URLs to object

I'm making an app whereby users post 2 images. I'm using Firebase for storage and as my database.
In my method to upload the images what I had wanted to do was to essentially use this method to return the URLs separately as well. I had written the following:
private func uploadImage(image: UIImage) -> URL? {
let randomName = UUID()
let storageRef = storage.reference().child("\(randomName)/png")
guard let uploadData = image.pngData() else { return nil}
var imageUrl: URL?
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
imageUrl = url
}
}
}
return imageUrl
}
And then I wrote the following 'post' method which is run when the submit button is tapped:
#objc func post() {
if let question = questionText.text,
let hashtagText = hashtagTextField.text,
let userHandle = Auth.auth().currentUser?.email,
let firstImage = left.image,
let secondImage = right.image,
let firstImageURL = uploadImage(image: firstImage)?.absoluteString,
let secondImageURL = uploadImage(image: secondImage)?.absoluteString
{
db.collection("posts").addDocument(data: [
"firstImage" : firstImageURL,
"secondImage" : secondImageURL,
"question" : question,
"hashtagText" : hashtagText,
"userHandle" : userHandle
]) { (error) in
if let e = error {
print("There was an issue saving data to Firestore, \(e)")
} else {
print("Successfully saved data")
self.dismiss(animated: true, completion: nil)
}
}
}
}
However, obviously the first method is not going to work as the closure is run after imageUrl is returned, therefore returning nil.
I've been trying to figure out how to manage this scenario - I had considered using a loop to populate an array of images but this got messy and I'm sure it is not the standard way to handle this. Any help would be greatly appreciated.
The return imageUrl is in the wrong place. It will return before Firebase has had time to store the image and return the url.
Additionally, the name of the file is not going to work. You currently have
storage.reference().child("\(randomName)/png") // xxxxx/png?
when it should be
storage.reference().child("\(randomName).png") // xxxxx.png
You can't 'return' data from a Firebase closure because firebase is asynchronous - a completion handler may possibly be a solution, but we don't know what the total use case is.
Let's assume you want want to store a users vacation picture in storage and then store that url in Firestore
private func uploadImage(image: UIImage) {
guard let uid = Auth.auth().currentUser?.uid else { return } //this users uid
let storageRef = storage.reference().child(uid).child("vacation.png")
//the path will be storage/users uid/vacation.png
guard let uploadData = image.pngData() else { return nil}
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if url != nil {
//it's here where we store the imageUrl in Firestore
let dict = ["theUrl": url?.absoluteURL)]
let userRef = self.db.collection("users").document(uid)
//self.db points to *my* Firestore
userRef.collection("my_pics").addDocument(data: dict)
//will store in firstore/users/uid/docId/theUrl: the url
}
}
}
}
}

App crashing when trying to change profile photo

I am working in the edit profile portion of my application. When I try to change and update a users profile photo. The app crashes and I get this error
reason: 'URL scheme must be one of gs://, http://, or https://
When I create a new profile and add a profile photo or if I upload a photo it works fine but when I try to change the profile photo I get this. It will first remove the profile photo and update ( leaving the image view gray when a user doesn't have a photo) then when I try to rechange the photo again it will crash.
Here is the code I have.
func updateProfileImage() {
guard imageChanged == true else { return }
guard let currentUid = Auth.auth().currentUser?.uid else { return }
guard let user = self.user else { return }
Storage.storage().reference(forURL: user.profileImageUrl).delete(completion: nil)
let filename = NSUUID().uuidString
guard let updatedProfileImage = profileImageView.image else { return }
guard let imageData = updatedProfileImage.jpegData(compressionQuality: 0.3) else { return }
STORAGE_PROFILE_IMAGES_REF.child(filename).putData(imageData, metadata: nil) { (metadata, error) in
if let error = error {
print("Failed to upload image to storage with error: ", error.localizedDescription)
}
STORAGE_PROFILE_IMAGES_REF.downloadURL(completion: { (url, error) in
USER_REF.child(currentUid).child("profileImageUrl").setValue(url?.absoluteString, withCompletionBlock: { (err, ref) in
guard let userProfileController = self.userProfileController else { return }
userProfileController.fetchCurrentUserData()
self.dismiss(animated: true, completion: nil)
})
})
}
}
}
The first thing you check URL is valid or not using a guard.
guard let urlis = yourUrl else{
// url is nill.
return
}
if let url = NSURL(string: urlis) {
// your image code
}
else{
// url is invalid.
return
}
Add Exception Breakpoint: This quick tip will save you a lot of debugging time!. So Xcode will stop where the exception is caught.
In your project, go to the Breakpoint Navigator, click on the ’+’ button and ’Add Exception Breakpoint…’

How do I update an MKPointAnnotation image after downloading the right image?

I have a bunch of annotations on a map, which all have a custom photos. Some of the photos may not be downloaded to the application yet from Firebase, so if they do not the image available, it defaults to a white circle image and initiates a download. When the download completes, it does not set the new image. How can I go about this?
Heres some code:
func generateAnnotations() {
for photo in photos {
let annotation = DetailPhotoPointAnnotation()
let latitude = photo.latitude
let longitude = photo.longitude
annotation.coordinate.latitude = latitude
annotation.coordinate.longitude = longitude
if photo.image != nil {
annotation.image = photo.image
} else {
annotation.image = #imageLiteral(resourceName: "whiteCircle")
let path = getDocumentsDirectory().appendingPathComponent(photo.uid)
if let image = UIImage(contentsOfFile: path) {
annotation.image = image
} else {
let ref = FIRStorage.storage().reference(forURL: photo.imageUrl)
ref.data(withMaxSize: 5*1024*1024, completion: { (data, error) in
if error != nil {
print(error!)
} else {
if let imageData = data {
if let image = UIImage(data: imageData) {
photo.assignImage(image: image)
annotation.image = image
}
}
}
})
}
}
self.coordinates.append(CLLocationCoordinate2DMake(latitude, longitude))
self.mapView.addAnnotation(annotation)
}
generateOverlay()
}
As you can see, it first looks to if the photo object contains an image. If it doesn't, it looks in the documents directory for that image. If its not there, it will finally download it. Any suggestions?
You need to do something like this. Go to main thread. And then update
DispatchQueue.main.async(execute: {
imageView.image = image
})
In my solution it works.
Hope this helps.

I want to save UIImage instead of nil value on parse

I am implementing saving post part, the post contains user profile Picture when it is saved for showing on main page. but there would be a user who hasn't profile picture. I tried this code, it occurs error.
if let profilePicture = PFUser.currentUser()?.objectForKey("profile_picture") {
post["profile_picture"] = profilePicture
} else {
post["profile_picture"] = UIImage(named: "AvatarPlaceholder" )
}
post.saveInBackgroundWithBlock({ ( isSucessful: Bool, error : NSError?) -> Void in
if error == nil {
self.alert("success", message : "your post has been uploaded")
} else {
self.alert("Error", message : (error?.localizedDescription)!)
}
if user has profile photo, it will be fine. but if not, that is the problem.
I have Avatarplaceholder jpeg file in assets.
my questions are.....
how can I upload my avatarplaceHolder ?
or is there better way to cover nil value?,
actually I don't want to waste my cloud storage.
I guess that you have to wrap you UIImage into a PFFile as follows:
let imageData = UIImageJPEGRepresentation(UIImage(named: "AvatarPlaceholder")!, 0.5)!
let profileImageFile = PFFile(name: "profileImage", data: imageData)
post["profile_picture"] = profileImageFile