I've got a problem with updating data. As you can see on this picture below, when I change those three textfields but leave current image and tap "Save", it will update my data in Firebase but also will upload a NEW image which is EMPTY so user will see an empty UIImageView.
Why is that?
Change Medication view image
Change Medication view no image
This is my method from Firebase to save image(data) and download URL:
func saveImageToStorage(cellImage: Data, completion: #escaping(Result<String, Error>) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else { return }
refStorage.child(uid).child(imageName).putData(cellImage, metadata: nil) { (_, error) in
guard error == nil else {
completion(.failure(NSError(domain: "Saving image to storage failed", code: 0)))
return
}
self.refStorage.child(uid).child(self.imageName).downloadURL { (url, error) in
guard let url = url, error == nil else { return }
let urlString = url.absoluteString
completion(.success(urlString))
print("URL downloaded: \(urlString)")
}
}
}
Here is extension with UIImagePickerControllerDelegate:
extension CurrentMedicationSettingsViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let compressionQualityValue: CGFloat = 0.1
let image = info[.originalImage] as? UIImage
userMedicationSettingView.medicationImageView.image = image
if let uploadData = image?.jpegData(compressionQuality: compressionQualityValue) {
imageData = uploadData
}
picker.dismiss(animated: true, completion: nil)
}
ViewModel where update medication method is:
func updateMedicationInfo(data: Data, pillName: String, capacity: String, dose: String, childId: String, completion: #escaping () -> Void) {
firebaseManager.saveImageToStorage(cellImage: data) { (result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let url):
self.firebaseManager.updateMedicationInfo(pillName: pillName, capacity: capacity, dose: dose, cellImageURL: url, childId: childId)
}
completion()
}
}
Thanks for any advice!
Cheers!
Related
The code here allows me to upload and download one photo to Firebase and save it to user defaults but I'm trying to figure out how to do it within a collectionView cell and display as many photos wanted, adding on new items
import UIKit
import FirebaseStorage
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
private let storage = Storage.storage().reference()
#IBOutlet var imageView: UIImageView!
#IBOutlet var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
label.numberOfLines = 0
guard let urlString = UserDefaults.standard.value(forKey: "url") as? String, let url = URL(string: urlString) else {
return
}
label.text = urlString
let task = URLSession.shared.dataTask(with: url, completionHandler: { data,_,error in
guard let data = data, error == nil else {
return
}
DispatchQueue.main.async {
let image = UIImage(data: data)
self.imageView.image = image
}
})
task.resume()
}
#IBAction func didTapButton() {
let picker = UIImagePickerController()
picker.sourceType = .photoLibrary
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
guard let imageData = image.pngData() else {
return
}
storage.child("Images/Photo.png").putData(imageData, metadata: nil) { (_, error) in
guard error == nil else {
print("Failed to Upload Data")
return
}
self.storage.child("Images/Photo.png").downloadURL(completion: {url, error in
guard let url = url, error == nil else {
return
}
let urlString = url.absoluteString
DispatchQueue.main.async {
self.label.text = urlString
self.imageView.image = image
}
print("Download URL: \(urlString)")
UserDefaults.standard.set(urlString, forKey: "url")
})
}
// Upload Image Data
// Get Download URL
// Save Download URL to userDefaults
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
To upload images to Firebase storage and show them in a collection view, you can use the following steps;
Set up collection view with an array of URLs (or Strings) as its
data source. You can use your custom models if required.
Keep a reference to your Firebase storage and upload the image. After successful upload, get the URL for the uploaded image using the image reference.
Save the url in Firebase Database(or Cloud Firestore). This is required only if you want to sync the collection view with the database and update it when new images are uploaded.
Add a listener to your Firebase database reference where you have
saved the image URLs. Update the local URLs array inside the listener and reload the collection view.
If you don't want to use Firebase database, omit steps 3 and 4, save the image URL to the array and reload the collection view right away.
I'm not adding the code for collection view setup here as it's not the objective of this answer.
let storageRef = Storage.storage().reference(withPath: "images")
let databaseRef = Database.database().reference(withPath:"images")
var images: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
addDatabaseListener()
}
private func addDatabaseListener() {
databaseRef.observe(.childAdded) { (snapshot) in
guard let value = snapshot.value as? [String: Any], let url = value["url"] as? String else { return }
self.images.append(url)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage, let data = image.jpegData(compressionQuality: 0.1) else { return }
let fileName = "\(Date.timeIntervalSinceReferenceDate).jpeg"
let newImageRef = storageRef.child(fileName)
newImageRef.putData(data, metadata: nil) { (_, error) in
if let error = error {
print("upload failed: ", error.localizedDescription)
return
}
newImageRef.downloadURL { (url, error) in
if let error = error {
print("error: ", error.localizedDescription)
return
}
self.databaseRef.childByAutoId().setValue(["url": url?.absoluteString])
}
}
}
I know this question has been asked before but none of the answers have fixed my issue. I'm getting the error:
Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled}
I have another project where I'm using this exact method in another project that works perfectly fine. I have tried to place #objc in front of the functions but get this error:
Objective-C method imagePickerController:didFinishPickingMediaWithInfo: provided by method imagePickerController(_:didFinishPickingMediaWithInfo:) conflicts with optional requirement method imagePickerController(_:didFinishPickingMediaWithInfo:) in protocol UIImagePickerControllerDelegate.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard let uid = Auth.auth().currentUser?.uid else { return }
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
let editImage = editedImage.withRenderingMode(.alwaysOriginal)
guard let uploadData = editImage.jpegData(compressionQuality: 0.3) else { return }
let filename = NSUUID().uuidString
let stoarageRef = Storage.storage().reference().child("profile_images").child(filename)
stoarageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if let error = error {
print("Failed update profile image:", error)
}
stoarageRef.downloadURL(completion: { (downloadUrl, error) in
guard let profileImageUrl = downloadUrl?.absoluteString else { return }
print("Successfully updated image in storage:", profileImageUrl)
let dictionaryValues = ["profileImageUrl": profileImageUrl]
Database.database().reference().child("users").child(uid).updateChildValues(dictionaryValues, withCompletionBlock: { (error, ref) in
if let error = error {
print("There was an error:", error)
return
}
print("Successfully saved user info to db")
})
})
}
} else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
let origImage = originalImage.withRenderingMode(.alwaysOriginal)
guard let uploadData = origImage.jpegData(compressionQuality: 0.3) else { return }
let filename = NSUUID().uuidString
let stoarageRef = Storage.storage().reference().child("profile_images").child(filename)
stoarageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if let error = error {
print("Failed update profile image:", error)
}
stoarageRef.downloadURL(completion: { (downloadUrl, error) in
guard let profileImageUrl = downloadUrl?.absoluteString else { return }
print("Successfully updated image in storage:", profileImageUrl)
let dictionaryValues = ["profileImageUrl": profileImageUrl]
Database.database().reference().child("users").child(uid).updateChildValues(dictionaryValues, withCompletionBlock: { (error, ref) in
if let error = error {
print("There was an error:", error)
return
}
print("Successfully saved user info to db")
})
})
}
}
dismiss(animated: true, completion: nil)
}
Thank you for any help fixing this issue.
Swift 5, Xcode 11
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
backgroundImage = (info[UIImagePickerController.InfoKey.originalImage] as? UIImage)!
ivBackground.image = backgroundImage
picker.dismiss(animated: true, completion: nil)
}
This is not just Swift 5 problem. Type of info parameter for this delegate method was changed from [String : Any] to [UIImagePickerController.InfoKey : Any] a long time ago.
This is why compiler complains that your, in this case your own, method conflicts with method declared by implemented protocol.
So, you need to implement delegate's method with correct type of parameter
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
<#code#>
}
The new function name is what causes the error, it is confused with two different functions that do the same thing.
It used to be [String : Any] (which is what you have) but now they have changed it to [UIImagePickerController.InfoKey : Any]. This change just means that the info is going to be type of UIImagePickerController.InfoKey.
Try use this as your function name, it got changed a while ago (not in Swift 5):
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
Hope this helps!
I've got a problem regarding Firebase and the upload of pictures..
I've been tried to follow the Firebase doc but I'm not sur to do the right things ...
In my application I want to send in firebase the value of 2 textfields and 1 segmented control plus one picture which is coming from the iphone's gallery.
well my save button :
#IBAction func saveBtnWasPressed(_ sender: Any) {
//Informations from the segmented control
if isMe == false {// Si SE
acftType = "SE"
}else if isMe == true {//Si ME
acftType = "ME"
}
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let usersPlanes : NSDictionary = [ "Registration" : self.acftRegTxtField.text!,
"model": self.acftModelTxtField.text!,
"Type" : self.acftType]
if isMe == false {// Si SE
ref.child("Planes").child(userID!).child("SE").childByAutoId().setValue(usersPlanes)
}else if isMe == true {//Si ME
ref.child("Planes").child(userID!).child("ME").childByAutoId().setValue(usersPlanes)
}else{
print("Error: Impossible to find the type of aircraft...")
}
let Dpalert = UIAlertController(title: nil, message: "Your informations as been upload", preferredStyle: .alert)
Dpalert.addAction(UIAlertAction(title: "Roger", style: .cancel, handler: nil))
self.present(Dpalert, animated: true)
}
And my function to allow user to select an image from his gallery is :
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let userID = Auth.auth().currentUser?.uid
self.dismiss(animated: true, completion: nil)
if let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.planImageView.image = selectedImage
var data = Data()
data = selectedImage.jpegData(compressionQuality: 0.75)!
}else{
print("Error : Impossible to deal with this image...")
}
let imageRef = Storage.storage().reference().child("Images").child(userID!).child(randomString(20));
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
But nothing appears in firebase when the picture is load in the app and How can I add it in the same folder as my first 3 information send with the save button ?
I'm totally lost with all this information. How can I solve my problem ?
Thanks very much for your help !
Flyer-74
In this function
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let userID = Auth.auth().currentUser?.uid
self.dismiss(animated: true, completion: nil)
if let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.planImageView.image = selectedImage
var data = Data()
data = selectedImage.jpegData(compressionQuality: 0.75)!
}else{
print("Error : Impossible to deal with this image...")
}
let imageRef = Storage.storage().reference().child("Images").child(userID!).child(randomString(20));
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
Try to put an output when your putData fail
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
print("Error with upload \(String(describing: error?.localizedDescription))")
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
print("Error with download URL: \(String(describing: error?.localizedDescription))")
return
}
}
Maybe it will help you to recognize the error; tell me what you got in the error
I am making a project very similiar to Instagram's multiple image post feature, and I have 2 errors that are connected to each other in my capturePhotoController because of my ImageUploadManager. The errors are:
Cannot assign to immutable expression of type 'ImageUploadManager.Type'
Type 'ImageUploadManager?' has no member 'uploadImage'
It is happening in this view controller: *The errors are marked with a comment
import UIKit
class takePicViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.sourceType = .camera
imagePicker.allowsEditing = false
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
// Error ImageUploadManager = ImageUploadManager()
// Error ImageUploadManager?.uploadImage(userPickedImage, progressBlock: { (percentage) in
print(percentage)
}, completionBlock: { (fileURL, errorMessage) in
print(fileURL)
print(errorMessage)
})
PhotoArray.sharedInstance.photosArray.append(userPickedImage) //append converted data in array
imageView.image = userPickedImage
}
imagePicker.dismiss(animated: true, completion: nil)
}
#IBAction func cameraButtonPressed(_ sender: UIButton) {
present(imagePicker, animated: true, completion: nil)
}
}
Here is the code for the ImageUploadManager:
import UIKit
import FirebaseStorage
import Firebase
struct Constants {
struct Car {
static let imagesFolder: String = "carImages"
}
}
class ImageUploadManager: NSObject {
func uploadImage(_ image: UIImage, /**/ progressBlock: #escaping (_ percentage: Double) -> Void, /**/ completionBlock: #escaping (_ url: String?, _ errorMessage: String?) -> Void) {
let data = Data()
let storage = Storage.storage()
let storageRef = storage.reference()
let imageName = "\(Date().timeIntervalSince1970).jpg"
let imagesReference = storageRef.child(Constants.Car.imagesFolder).child(imageName)
if let imageData = UIImageJPEGRepresentation(image, 0.8) {
let metadata = storageRef.child("image/jpg")
let uploadTask = metadata.putData(data, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
storageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
return
}
}
}
uploadTask.observe(.progress, handler: { (snapshot) in
guard let progress = snapshot.progress else {
return
}
let percentage = (Double(progress.completedUnitCount) / Double(progress.totalUnitCount)) * 100
progressBlock(percentage)
})
} else {
completionBlock(nil, "Image could not be converted to Data.")
}
}
}
Is the commented code:
// Error ImageUploadManager = ImageUploadManager()
your declaration of ImageUploadManager? if so that is totally wrong. It should be declared as below:
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let imageUploadManager = ImageUploadManager()
imageUploadManager.uploadImage(userPickedImage, prog.....
Reading your ImageUploadManager it looks like it's an stateless object and you can declare your function as a class function.
`class func uploadImage(_ image: UIImage`......`
You can then call it:
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
ImageUploadManager.uploadImage(userPickedImage, prog.....
I am trying to take a printed item and inserting it into my Firebase Database along with the rest of outlets. Thanks!
#objc(imagePickerController:didFinishPickingMediaWithInfo:) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
myImageView.image = image
} else {
//error
}
self.dismiss(animated: true, completion: nil)
let storageRef = FIRStorage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.myImageView.image!) {
storageRef.put(uploadData, metadata: nil, completion:
{
(metadata, error) in
if error != nil {
print("error")
return
} else {
print((metadata?.downloadURL()?.absoluteString)!)
//i want to take the line above and insert it into the database
}
})
}
}
#IBAction func addPost(_ sender: Any) {
if self.titleText.text != "" && self.authorText.text != "" && self.mainText.text != "" && self.dateText.text != "" {
ref?.child("Posts").childByAutoId()
.setValue(["Title": titleText.text,
"Article": mainText.text,
"Author": authorText.text,
"Date": dateText.text ])
//insert the download URL above
self.performSegue(withIdentifier: "kost", sender: self)
}
}
I see what your problem is now. You are tackling uploading images to firebase in totally the wrong manner. In the event didFinishPickingMediaWithInfo you should not be doing anything with firebase. You should only be setting your UIImageViews image to store the value of the data given from the image picker.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
self.dismiss(animated: true, completion: nil)
if let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage{
myImageView.image = editedImage
}else{
print("Something went wrong")
}
}
Then in your IBAction, you should upload everything to firebase. Here is a simple function that will upload your image to the database it:
func uploadImage(){
if let fileData = UIImageJPEGRepresentation(myImageView.image!, 0.8){
let storageRef = storage.reference().child("images").child("testImage.jpg")
storageRef.put(fileData, metadata: nil, completion: { (metadata, error) in
if error != nil{
print(error?.localizedDescription ?? "error")
return
}
let downloadURL = metadata?.downloadURL()?.absoluteString
// Write the download URL to your Database
self.ref?.child("images").setValue(downloadURL)
})
}else{
print("error")
}
}