How to add another key/value to Firebase Array - swift

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.

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. :)

Unable to merge two firebase arrays in Firebase Database

Hello I am relatively new to Swift/Firebase and I am struggling to merge two arrays so that the downloadURL is seen amongst both the name and the email fields. One function adds the name and the email through the button click the other is another function to save the URL. When I try and merge them I get this (as seen in the image below). Here is my code:
#IBAction func createAccountAction(_ sender: AnyObject) {
let Users = Database.database().reference().child("Users")
let userDictionary : NSDictionary = ["email" : emailTextField.text as String!, "Name": nameTextField.text!]
Users.childByAutoId().setValue(userDictionary) {
(error, ref) in
if self.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!) { (user, error) in
if error == nil {
print("You have successfully signed up")
//Goes to the Setup page which lets the user take a photo for their profile picture and also chose a username
var imgData: NSData = NSData(data: UIImageJPEGRepresentation((self.profilePicture?.image)!, 0.8)!)
self.uploadProfileImageToFirebase(data: imgData)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.present(vc, animated: true, completion: nil)
} else {
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)
}
}
}
}
}
func addImageURLToDatabase(uid:String, values:[String:AnyObject]){
let Users = Database.database().reference().child("Users")
let ref = Database.database().reference(fromURL: "https://example.firebaseio.com/")
Users.updateChildValues(values) { (error, ref) in
if(error != nil){
print(error)
return
}
self.parent?.dismiss(animated: true, completion: nil)
}
}
Something like this is what you want. I removed a few variables from your function but you can add them back. I just wanted to make sure the code compiles.
#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 }
// HERE YOU SET THE VALUES
usersRef.child(user.uid).setValue(userDictionary, withCompletionBlock: { (error, ref) in
if error != nil { print(error); return }
self.addImageURLToDatabase(uid: user.uid, values: ["Put": "Your Values Here" as AnyObject])
})
}
}
}
func addImageURLToDatabase(uid:String, values:[String:AnyObject]){
let usersRef = Database.database().reference().child("Users").child(uid)
usersRef.updateChildValues(values) { (error, ref) in
if(error != nil){
print(error)
return
}
self.parent?.dismiss(animated: true, completion: nil)
}
}

Firebase Xcode Swift - stuck on downloadURL

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

navigation controller doesn't work

Signup UIViewController design that validates email, password and confirm password.
Signup is embedded in NavigationController
func showAlert(ttl:String,msg:String){
let alert = UIAlertController(title: "\(ttl)", message: "\(msg)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
#IBAction func signUp(_ sender: UIButton) {
if (email.text != nil && password.text != nil && confrimPassword.text != nil){
if !(isValidEmail(testStr: email.text!)){
showAlert(ttl: "Invaild Email-ID", msg: "Please enter an valid Email-ID")
}
else if !(isValidPassword(testStr: password.text)){
showAlert(ttl: "Invalid Password", msg: "Password must have at least one uppercase,one digit,one lowercase and minimum 8 characters")
}
else if !(passwordMatch(password: password.text!, confirmPassword: confrimPassword.text!)){
showAlert(ttl: "Passwords doesn't Match", msg: "Please re-enter your password")
}
else{
if Connectivity.isConnectedToInternet {
Authentication.Signup(for: email.text!, password: password.text!,finished: { resdata in do{ let res = try JSONSerialization.jsonObject(with: resdata) as! Dictionary<String, AnyObject>
print(res)
DispatchQueue.main.async {
let view: Verification = self.storyboard?.instantiateViewController(withIdentifier: "Verify") as! Verification
view.email = self.email.text!
//doesnot work
self.navigationController?.pushViewController(view, animated: true)
}
}
catch{
print("Error")
}
})
}
else{
showAlert(ttl: "No Internet", msg: "Please check your internet connection")
}
}
}
else{
self.showAlert(ttl: "Enter all Credentials", msg: " ")
}
}
}
Tried using segue also that doesn't suit my requirements
#IBAction func signUp(_ sender: UIButton) {
Authentication.Signup(for: email.text!, password: password.text!,finished: { resdata in do{
let res = try JSONSerialization.jsonObject(with: resdata) as! Dictionary<String, AnyObject>
print(res)
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "Verify") as! Verification
controller.email = self.email.text!
self.present(controller, animated: true, completion: nil)
}
}

Can't get account info Parse & FB SDK

I'm using the ParseFacebookUtilsV4 and the fb idk seem to be having issues with retrieving the users info just to print into the console for now. The issue is that it seems that the block to start the request isn't being executed since whenever i debug it just seems to skip the start completion handler.
// View controller code
PFFacebookUtils.facebookLoginManager().loginBehavior = .web
var loginTask = PFFacebookUtils.logInInBackground(withReadPermissions: [])
loginTask.continue( { (bfTask) -> Any? in
print("I'm here")
let request = FBSDKGraphRequest(graphPath:"me", parameters: ["fields":"id,email,name,first_name,last_name,picture"] )
print(request)
request?.start {
(connection, result, error) in
print(result)
}
return ""
})
// App delegate config
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let configuration = ParseClientConfiguration {
$0.applicationId = "xxxx"
$0.clientKey = "xxxxx"
$0.server = "https://parseapi.back4app.com"
}
Parse.initialize(with: configuration)
PFFacebookUtils.initializeFacebook(applicationLaunchOptions: launchOptions)
PFTwitterUtils.initialize(withConsumerKey: "xxxxx", consumerSecret: "xxxxx")
// Override point for customization after application launch.
return true
}
here's the func I use for the Facebook request. I am using a block request:
func loadFacebookUserDetails() {
// Define fields we would like to read from Facebook User object
let requestParameters = ["fields": "id, email, first_name, last_name, name"]
// Send Facebook Graph API Request for /me
let userDetails = FBSDKGraphRequest(graphPath: "me", parameters: requestParameters)
userDetails.startWithCompletionHandler({
(connection, result, error: NSError!) -> Void in
if error != nil {
let userMessage = error!.localizedDescription
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
PFUser.logOut()
return
}
// Extract user fields
let userId:String = result.objectForKey("id") as! String
let userEmail:String? = result.objectForKey("email") as? String
let userFirstName:String? = result.objectForKey("first_name") as? String
let userLastName:String? = result.objectForKey("last_name") as? String
// Get Facebook profile picture
let userProfile = "https://graph.facebook.com/" + userId + "/picture?type=large"
let profilePictureUrl = NSURL(string: userProfile)
let profilePictureData = NSData(contentsOfURL: profilePictureUrl!)
// Prepare PFUser object
if(profilePictureData != nil)
{
let profileFileObject = PFFile(name:"profilePic.jpeg",data:profilePictureData!)
PFUser.currentUser()?.setObject(profileFileObject!, forKey: "ProfilePic")
}
PFUser.currentUser()?.setObject(userFirstName!, forKey: "Name")
PFUser.currentUser()?.setObject(userLastName!, forKey: "last_name")
if let userEmail = userEmail
{
PFUser.currentUser()?.email = userEmail
PFUser.currentUser()?.username = userEmail
}
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
if(error != nil)
{
let userMessage = error!.localizedDescription
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
PFUser.logOut()
return
}
if(success)
{
if !userId.isEmpty
{
NSUserDefaults.standardUserDefaults().setObject(userId, forKey: "user_name")
NSUserDefaults.standardUserDefaults().synchronize()
dispatch_async(dispatch_get_main_queue()) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
}
})
})
}