Firebase Xcode Swift - stuck on downloadURL - swift

I know the downloadURL function has been deprecated, but I can't seem to get the new completion function to work:
#IBAction func postButtonClicked(_ sender: Any) {
let mediaFolder = Storage.storage().reference().child("media")
if let data = UIImageJPEGRepresentation(postImage.image!, 0.5) {
mediaFolder.child("\(uuid).jpg").putData(data, metadata: nil, completion: { (metadata, error) in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
let imageURL = mediaFolder.downloadURL(completion: { (url, error) in
if error != nil {
print("error!!!!")
} else {
return url?.absoluteString
}
})
print(imageURL)
}
})
}
}
I just can't get this to work. I always get the error!!!!! message in the log and I'm not sure why. I've been struggling with this code for the past 3 hours and for some reason I just can't get the imageURL to print.
All I want is to get imageURL to equal url?.absoluteString
Any help would greatly be appreciated

You're not getting the URL to print out because you're not querying the right storage references.
First, check if the image is actually uploaded in the storage and then add the child Id to your mediaFolder in the else statement:
mediaFolder.child("\(uuid).jpg").downloadURL(completion: { (url, error) in
})

Related

How to get the latest Document ID in Firestore using Swift iOS

I am giving users the possibility to set up Project-Details in an app.
Since there is also the opportunity to upload an image, i will have to make use of Firebase Storage. Because I want to give the users an overview of their projects combining the data and the uploaded picture I need to make a reference between the DocumentID in database and the ImageID in storage.
I thought about a two step approach: 1. Users are generating a Project -> User clicks "next" (document getting generated) 2. User uploads an image -> Image gets stored in Storage with Reference to the DocumentID of the just generated Project.
The goal is simply to link the Image to the Document ID of the project.
Can anyone give me a hint how to solve that problem? Here are my codes so far:
For the Project-Details:
// MARK: Store the Project Infos in Database
// Check the fields and validate that the data is correct. If everything is correct, this method returns nil. Otherwise, it returns the error message
func validateFields() -> String? {
// Check that all fields are filled in
if txtLocation.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
txtProjectTitle.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
txtProjectDescription.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
endDate.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
beginnDate.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all fields."
}
return nil
}
#IBAction func saveProject(_ sender: Any) {
let user = Auth.auth().currentUser
if let user = user {
let uid = user.uid
// Validate the fields
let error = validateFields()
if error != nil {
// There is something wrong with the fields, show error message
showError(error!)
} else {
// Create cleaned versions of the data
let projectTitle = txtProjectTitle.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let projectLocation = txtLocation.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let projectDescription = txtProjectDescription.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let projectBeginn = beginnDate.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let projectEnd = endDate.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// Save stuff
let db = Firestore.firestore()
db.collection("Projects").addDocument(data: ["Project Title": projectTitle, "Project Location": projectLocation, "Project Description": projectDescription, "Project Start": projectBeginn, "Project Finish": projectEnd, "uid": uid]) { (error) in
if error != nil {
// Show error message
self.showError("Error saving user data")
}
}
}
} else { self.showError("Please log out and log in again")}
}
// Error Handling for save
func showError(_ message:String) {
errorLabel.text = message
errorLabel.alpha = 1
}
And for the Image upload
//MARK: Imagepicker
// Image Picker functions
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
newImage.image = image
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// MARK: - Actions
// Image Picker
#IBAction func addImage(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Photo Source", message: "Choose a source", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action: UIAlertAction) in imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action: UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
//MARK: Upload Image
#IBAction func uploadImage(_ sender: Any) {
guard let image = newImage.image, let data = image.jpegData(compressionQuality: 1.0) else {
self.showError("Something went wrong")
return
}
let imageName = UUID().uuidString
let imageReference = Storage.storage().reference()
.child("imagesFolder")
.child(imageName)
imageReference.putData(data, metadata: nil) {(metadata, err) in
if let err = err {
self.showError("Something went wrong")
return
}
imageReference.downloadURL(completion: { (url, err) in
if let err = err {
self.showError("Something went wrong")
return
}
guard let url = url else {
self.showError("Something went wrong")
return
}
let dataReference = Firestore.firestore().collection("imageReferences").document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let imageUID = documentUid
let data = ["Image UID": imageUID, "Image URL": urlString]
dataReference.setData(data, completion: {(err) in
if let err = err {
self.showError("Something went wrong")
return
}
UserDefaults.standard.set(documentUid, forKey: imageUID)
})
})
}
}
// Error Handling for save
func showError(_ message:String) {
errorLabel.text = message
errorLabel.alpha = 1
}
So in best case I could do this even in one View Controller without the need of a two step approach. If it is not possible I would like to put the generated Document ID in
let data = ["Image UID": imageUID, "Image URL": urlString]
of
let dataReference = Firestore.firestore().collection("imageReferences").document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let imageUID = documentUid
let data = ["Image UID": imageUID, "Image URL": urlString]
dataReference.setData(data, completion: {(err) in
if let err = err {
self.showError("Something went wrong")
return
}
I would be very pleased if someone could help me. :)

How to add another key/value to Firebase Array

The problem that I'm facing is that I have successfully created the array and have displayed the values like so:
Users
-uid
- Name: Example
- Profile Pic URL: example12345
- email: example#example.co.uk
However, in another swift file I have successfully generated a personality type and am struggling to add this to the array so that I end up with something that looks like this:
Users
-uid
- Name: Example
- Profile Pic URL:
- email: example#example.co.uk
- personality type: INTJ
I have tried copying the code from the previous swift class to no avail
This is the code for the working firebase array
#IBAction func createAccountAction(_ sender: AnyObject) {
let usersRef = Database.database().reference().child("Users")
let userDictionary : NSDictionary = ["email" : emailTextField.text!, "Name": nameTextField.text!]
if emailTextField.text == "" {
let alertController = UIAlertController(title: "Error", message: "Please enter your email and password", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
} else {
Auth.auth().createUser(withEmail: self.emailTextField.text ?? "", password: self.passwordTextField.text ?? "") { (result, error) in
if error != nil {
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
return
}
guard let user = result?.user else { return }
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.present(vc, animated: true, completion: nil)
// HERE YOU SET THE VALUES
usersRef.child(user.uid).setValue(userDictionary, withCompletionBlock: { (error, ref) in
if error != nil { print(error); return }
let imageName = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("profile_images").child("\(imageName).png")
if let profileImageUrl = self.profilePicture.image, let uploadData = UIImageJPEGRepresentation(self.profilePicture.image!, 0.1) {
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil, metadata != nil {
print(error ?? "")
return
}
storageRef.downloadURL(completion: { (url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let profileImageUrl = url?.absoluteString {
self.addImageURLToDatabase(uid: user.uid, values: ["profile photo URL": profileImageUrl as AnyObject])
}
})
})
}
}
)}
}
}
This is the other swift file function which generates the personality type which I would like to add to the array
#IBAction func JPbtn(_ sender: Any) {
if (Judging < Perceiving){
Result3 = "P"
} else {
Result3 = "J"
}
let PersonalityType = "\(Result) \(Result1) \(Result2) \(Result3)"
print(PersonalityType)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Example") as! ViewController
self.present(vc, animated: true, completion: nil)
}
So if you are just trying to add a new key with a value, all you need to do is create a new reference like this.
guard let currentUserUID = Auth.auth().currentUser?.uid else { return }
print(currentUserUID)
let userPersonalityRef = Database.database().reference().child("users").child(currentUserUID).child("personality")
userPersonalityRef.setValue("Some Value")
When you set the value it can also be a dictionary if you want. But if your users don't all have personality make sure it optional on your data model or else It might crash your app. When you are getting your user from firebase.

Swift and Firebase Database - Missing argument label 'andPriority' in call

The code below is being used to update my firebase database when a user registers in the app, it is suppose to authenticate the user in the database and also save a boolean value. I get the following error when this is implemented (see below).
#IBAction func signupPressed(_ sender: UIButton) {
Auth.auth().createUser(withEmail: emailEntryField.text!, password: passwordEntryField.text!) { (user, error) in
if error == nil {
//Show SVProgressHUD
SVProgressHUD.show()
//save User to Database
let newUserInfo : [String:Any] = ["email":self.emailEntryField.text!, "state":true]
self.ref.child("users").child(user?.user.uid).setValue(newUserInfo, withCompletionBlock: { (error, ref) in
print("New User Saved")
}
//Dismiss SVProgressHUD
SVProgressHUD.dismiss()
//move to info view controller
self.performSegue(withIdentifier: "signupToInfo", sender: self)
} else {
print("Signup Unsuccessul")
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
There is a missing ) after the block
self.ref.child("users").child(user?.user.uid).setValue(newUserInfo, withCompletionBlock: { (error, ref) in
print("New User Saved")
}
Add ) at the end, like this
self.ref.child("users").child(user?.user.uid).setValue: newUserInfo, withCompletionBlock: { (error, ref) in
print("New User Saved")
})
Hope it helps

Tableview works on sim but not on test device

I have no idea whats wrong with this function. I'm calling it in viewdidload and it prints the array as blank when I load it on my phone. When I do it in the simulator it fills the array though. Using ObjectMapper if that helps at all.
func getData() {
let myURLString = "http://meetup.x10host.com/api/get_event.php?radius=15"
let myURL = NSURL(string: myURLString)!
var myCardsArray = [Card]()
let mySession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let myDataTask = mySession.dataTaskWithURL(myURL) { (data, response, error) in
guard error == nil else {
let alertController = UIAlertController(title: "No Connection", message:
"Can't connect to database, perhaps turn on your WiFi?", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
return
}
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments)
for someCard in jsonData as! NSArray{
let card = Mapper<Card>().map(someCard)
myCardsArray.append(card!)
self.nameArray.append(card!.titlee!)
self.textArray.append(card!.text!)
self.userArray.append(card!.attending!)
self.latArray.append(card!.latitude!)
self.longArray.append(card!.longitude!)
self.timeArray.append(card!.time!)
self.locTextArray.append(card!.locationText!)
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
} catch {
print("There was an error")
}
}
myDataTask.resume()
print(nameArray)
}

Update value in Parse.com

I am trying to update a value in my Parse.com table named: "currentUploaded".
This is my query from Parse.com code:
http://pastebin.com/Jr0EcJuy
Parse.com “currentUploads” class: http://i.stack.imgur.com/E8tND.png
This is the button i want to do the update value(as it is now, it create a new row in another class, but i just want to increase the "reportedCount" of the selected item instead:
#IBAction func reportContentAction(sender: AnyObject) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.collectionView)
let indexPath = self.collectionView.indexPathForItemAtPoint(buttonPosition)
////
println(indexPath?.item)
////
let post = self.arrayOfDetails[indexPath!.item]
var alertMessage = NSString(format:"*User: %#\r *Text: %#\r *Created at %#", post.username, post.text, post.CreatedAt)
var reportAlert = UIAlertController(title: "Report Content", message:alertMessage as String, preferredStyle: UIAlertControllerStyle.Alert)
reportAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Report Logic here")
var currentUploads = PFObject(className: "banned")
currentUploads["username"] = post.username
currentUploads["imageText"] = post.text
currentUploads["imageFile"] = post.image
currentUploads["identifierForVendor"] = post.deviceID
currentUploads["flaggedBy"] = PFUser.currentUser()?.username
currentUploads["flaggedByUUID"] = UIDevice.currentDevice().identifierForVendor.UUIDString
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
//**Success saving, now save image.**//
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
// Take user home
print("Data uploaded")
// Show UIAlertView
let alert = UIAlertView()
alert.title = "Message"
alert.message = "You report has been sent. Thank you for your support."
alert.addButtonWithTitle("Close")
alert.show()
}
else{
print(error)
}
})
}
else{
print(error)
}
})
}))
reportAlert.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Cancel Logic here")
}))
presentViewController(reportAlert, animated: true, completion: nil)
}
I have also tried using this code when the user click yes on the popup, but it doesn't work:
reportAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Report Logic here")
var query = PFQuery(className: "currentUploads")
query.whereKey("imageFile", equalTo: post.image)
query.getFirstObjectInBackgroundWithBlock {
(myObject: PFObject?, error: NSError?) -> Void in
if (error != nil){
println(error)
//
}
else{
query.setValue("1", forKey: "reportedCount")
}
}
}))
Please, can someone show me how it should be correct? Have been struggling on this for many many hours now..
a small example
let someQuery = PFQuery(className: "banned")
someQuery.getObjectInBackgroundWithId(someclass.objectId) {
(updatedObject: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let updatedObject = updatedObject {
updatedObject["NameOfTheFieldYouWantToChange"] = newValue
updatedObject.saveInBackground()
}
}
someclass.objectId — it's just an objectId value for the raw you want to update. I don't know where you are storing it.
"NameOfTheFieldYouWantToChange" — name of the field you want to change :)
newValue — new value for this field.
P.S. Also it seems that you are trying to put your Parse code to your ViewController class. Compiler won't care so it will work, but it's not a good practice. It would be much more simpler for you to put all these database operations to different class (MVC pattern).