-1

I am sending information to my firebase database via textfields. I would like to make one of the nodes Users and then under that node different nodes separated by a user's uid. Under each UID node there would be three datapoint from textfields that are filled out from three textfields on the custom cell. I want to be able to type the user's name in the name text field and have it send their respective UID. I currently have the following code creating this:

database now

@IBAction func sendButtonTapped(_ sender: Any) {
    
    let companyNameC = companyNameTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    let companyDescriptionC = companyDescriptionTextFieldConsiderations.text!.trimmingCharacters(in: .whitespacesAndNewlines)
    
      let today = Date()
            let formatter1 = DateFormatter()
            formatter1.dateFormat = "MMM d y"
            print(formatter1.string(from: today))
            let todaysDate = formatter1.string(from: today)
            
            let storageRef = Storage.storage().reference(forURL: "I have my code here")
            let imageName = companyNameTextFieldConsiderations.text!
            let storageCompanyRef = storageRef.child("Company_Image_Considerations").child("\(todaysDate)").child(imageName)
            let companyDescriptionTextFieldText = companyDescriptionTextFieldConsiderations.text
            let dateToStart = startDateTextFieldConsiderations.text
            let dateToDecide = endDateTextFieldConsiderations.text
            let companyRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName)
    let considerationInfluencerRef = Database.database().reference().child("Considerations").child("\(todaysDate)").child(imageName).child("Users")
            let cell = tableView.dequeueReusableCell(withIdentifier: AddPersonCell) as! ConsiderationsCell
            let nameTFC = cell.nameTextFieldConsiderations.text!
            let feedTFC = cell.feedTextFieldConsiderations.text
            let storyTFC = cell.storyTextFieldConsiderations.text
            let compensationTFC = cell.compensationTextFieldConsiderations.text
            let values = ["Name": nameTFC]
    
            guard let imageSelected = self.CompanyImage.image else {
                               print ("Avatar is nil")
                               return
                           }
                   
                               var dict: Dictionary<String, Any> = [
                                  "Company Image": "",
                                   "Company Description": companyDescriptionTextFieldText!,
                                   "Start Date": dateToStart,
                                   "Decision Date": dateToDecide
                              ]
    
                   
                           guard let imageData = imageSelected.jpegData(compressionQuality: 0.5) else {
                               return
                           }
    
                           let metadata = StorageMetadata()
                           metadata.contentType = "image/jpeg"
                           storageCompanyRef.putData(imageData, metadata: metadata, completion:
                               { (StorageMetadata, error) in
                                   if (error != nil) {
                                       return
                                   }
                                
                                   storageCompanyRef.downloadURL { (url, error) in
                                       if let metadateImage = url?.absoluteString {
                                           dict["Company Image"] = metadateImage
                   
                                           companyRef.updateChildValues(dict, withCompletionBlock:  {
                                               (error, ref) in
                                               if error == nil {
                                                   print("Done")
                                                   return
                                              }
                   
                                               }
                   
                                           )
                                       }
                                   }
                   
                                   storageRef.updateMetadata(metadata) { metadata, error in
                                       if error != nil {
                                        //Uh-oh, an error occurred!
                                     } else {
                                       // Updated metadata for 'images/forest.jpg' is returned
                                     }
                                   }
                           })

    considerationInfluencerRef.updateChildValues(values as [AnyHashable : Any]) { (error, ref) in
    if error != nil {
        print(error ?? "")
        return
    }
    
                                self.navigationController?.popViewController(animated: true)
                   
                           }
}

I want it to look like this:

preferred database

Edit:

Here is an image of the dynamic prototype cell:

cell

You're saying is to use the name textfield to name an object in an array. That object has it's own array that would consist of the two textfields with # and the textfield with $?

EDIT 2:

import UIKit

class ConsiderationCellModel: NSObject {
var feedNumberQauntity: String?
var storyNumberQuantity: String?
var compensationAmmount: String?

}

CoderCoop
  • 79
  • 5
  • Please format your code so it's readable. Also, It’s a good idea to include code and structures as *text*, not links and images. That way, if they are needed in an answer, they can be copied and pasted. To get your Firebase structure, use the Firebase console->Export JSON and copy and paste a snippet of your structure. See [images and links are evil](http://idownvotedbecau.se/imageofcode). Lastly, images are not searchable which may prevent future readers from locating the question. Also, I am not seeing any code that associates a typed user name to a user id. Maybe I am overlooking it. – Jay Jul 22 '20 at 18:52
  • To be blunt, this whole approach is wrong. You should not be attempting to read values back from a tableView cell - or the tableView at all. tableViews are one way elements - they provide information to the user that comes from a **dataSource**. If the user inputs data into a tableView, it should be stored in the backing datasource. Additionally, as scrolling occurs the 'stuff' in the tableView will often get deallocated. Lastly, behind the scenes, UITableViewCells are re-used and only the cells currently visible will show you actual data - which could be deallocated at any time. – Jay Jul 23 '20 at 20:21
  • What should be happening is when a user enters something in a cell and hit's return, you should take that data and store it in your backing dataSource, probably an array. When the user clicks SAVE, you read the data **from the array** and store it in Firebase. Does that make sense? – Jay Jul 23 '20 at 20:22
  • That's what I want to happen. I'm using the table to just display the cells so I can controller the number of textfields. I want to display the data in my firebase and then retrieve it to the user it was sent to. I don't know how to do it and can't find anything to help me. – CoderCoop Jul 23 '20 at 20:25
  • If you look at your own posted answer here, along with the code in this question, it's not what you're doing. You're trying to loop over your cells using `let index = IndexPath(row:0,section:0)` with `guard let cell = tableView.cellForRow(at:index)` along with reading the textFields within the cell `let dateToStart = startDateTextFieldConsiderations.text`. You should be reading that data from your dataSource, not the cell. – Jay Jul 23 '20 at 20:29
  • How do I do that considering there are new cells created with a button by adding something to an array? The array updates and the row count is determined but the array count. – CoderCoop Jul 23 '20 at 20:33
  • The start dates are in the view and are writing them to firebase properly. – CoderCoop Jul 23 '20 at 20:34
  • When the user clicks 'add row' you add an object/row to the array and refresh the tableView to display it. See the Edit to my answer below. – Jay Jul 23 '20 at 21:22

3 Answers3

1

This became a multi-part question. To address the followup question which is

'How to I create a class, populate an array with that class, update an element within that array and write it to firebase.

Here's the answer with comments.

class ConsiderationCellModel: NSObject {
    var uid = "" //this will be used when reading in to keep track of which user it is
    var name = "" //not sure what 'name' is but here ya go
    var feedNumberQuantity = ""
    var storyNumberQuantity = ""
    var compensationAmount = ""
    
    func getDict() -> [String: String] {
        let dict = [
            "Compensation": compensationAmount,
            "NumberOfFeedPosts": feedNumberQuantity,
            "NumberOfStoryPosts": storyNumberQuantity
        ]
        return dict
    }
}

var myArray = [ConsiderationCellModel]()

//suppose thre are 3 models, and user adds a new model, so it will be #4, which 
//   is index 3 in the array (0, 1, 2, 3)
let model = ConsiderationCellModel()
myArray.append(model)

//reload the tableView which will now display this empty model. User enters some 
//   data, update the model at index 3 from the entered data
model.name = "some name"
model.feedNumberQuantity = "10"
model.storyNumberQuantity = "20"
model.compensationAmount = "1.00"

//at some point later the user clicks Save, so get the current index
//  retrieve the data from the model and store in Firebase.

let index = 3 //if there are 4 objects in the array and this is the one just added
              //you can also get the .last element in the array
let modelData = myArray[index]
let dictToSave = modelData.getDict()

let usersRef = self.ref.child("users_test")
let childRef = usersRef.child("uid_0")
childRef.setValue(dictToSave)

and the result structure is

users
   uid_0 //a users uid
      Compensation: "1.00"
      NumberOfFeedPosts: "10"
      NumberOfStoryPosts: "20"
Jay
  • 34,438
  • 18
  • 52
  • 81
  • So with this, will I be able to create multiple cells and have multiple uid? The name part is the name of the user that is under each of the user's nodes. The idea would be I could input the name and it would send the user's respective UID. I am also confused on where I am putting this. The class is going to be i's own swift file. And everything else goes in the view controller? I really owe you some drinks or something Jay. I can only imagine how frustrating helping me with this is. – CoderCoop Jul 24 '20 at 19:05
  • @CoderCoop if you refer back to one of your prior questions, I presented two options but it depends on the overall structure of your project. Generally speaking, if you have a viewController that contains a tableView and that tableView contains a cell that has the name, a sub-tableView of users etc, then the hosting viewController could contain that code. That's actually preferred. I don't like putting a lot of networking logic within tableView cells as previously mentioned, they are deallocated etc. Having one centralized section of code to handle Firebase is preferred. – Jay Jul 24 '20 at 20:12
  • I would suggest getting a better understanding of tableViews. Take a look at [this tutorial](http://www.thomashanning.com/uitableview-tutorial-for-beginners/) and also [another tutorial](https://theswiftdev.com/uitableview-tutorial-in-swift/) because it covers tableViews with sections. Most importantly, understanding MVC design patterns because it's the backbone of app development. See [LearnAppMaking](https://learnappmaking.com/view-controller-uiviewcontroller-ios-swift/) – Jay Jul 24 '20 at 20:29
0

Well, if you look at the structure you want

root
   Considerations
      Jul 16 2020
         COMPANY
            Company Description: ...
         users
            some_user_uid
               Compensation

you're code doesn't do that.

While it writes out your company info correctly

companyRef.updateChildValues(dict

which is the COMPANY name, Company Description etc

it kinda goes off the rails here

let considerationInfluencerRef = Database.database().reference()
                                                    .child("Considerations")
                                                    .child("\(todaysDate)")
                                                    .child(imageName)
                                                    .child("Users")

Because that points to a totally different node

root
   Considerations
      todaysDate 
         imageName <- ??
            Users

Edit

The biggest issue here is that you're attempting to read data from the cells in the tableView - those cells are dynamic, will be dellocated etc.

You should be storing your data in a dataSource, typically an array. As the user inputs info into your tablevIew cells, that should in turn update the array. So for example Say you have a tableView that displays recipes. There would be an array that holds the recipe and the ingredients as a list

class RecipeClass {
   var name = ""
   var ingredients = [String]()
}

var recipeArray = [RecipeClass]()

The tableView would have section headers, which are the RecipeClass name and then the rows would be the ingredients.

Say the recipe calls for 1 cup sugar as the first ingredient. So in the tableview the user clicks that field (field 0 of recipe 0 which correlates to row 0 of section 0) and changed it to 3/4 cup - at that point the object in the array should be updated.

Later on, when the user taps Save, whatever recipe index (section) is currently being display would be read from the dataSource array and then written to Firebase.

Any time a user taps Add Row, you add another object to the array within the recipe that's being displayed and refresh the row/cell of the tableView to reflect that new object

recipe.ingredients.add("") //"" is a placeholder or use "New Ingredient"

I am not an ASCII art guy but your UI would look like this, the ingredients would probably be a scrollable tableView so if they had 20 ingredients they could scroll through them

Shepards Pie            Save Button
   3 Potato
   1 Carrot
   9 Peas
   1Cup Gravy

If they wanted to change 3 Potato to 2 Potato, they would tap the 3 Potato field (cell if its a tableView), type in 2 Potato and that would then update the dataSource array.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • Hey Jay. I spent all afternoon rephrasing the question and communicating everything clearly. I also cleaned up my code so it was easier to understand. If you could check that out I would really appreciate it. I apologize for the headache that I have been causing. This is a huge thorn in my side right now. – CoderCoop Jul 22 '20 at 21:52
  • Hey Jay. I figured out the next step to my problem. The code: let index = IndexPath(row: 0,section: 0) guard let cell = tableView.cellForRow(at:index) as? ConsiderationsCell else { print("Not shown") ; return } Unfortunately it only works for one row and I need it to work for multiple rows. How do I do that? – CoderCoop Jul 23 '20 at 19:18
  • Okay. I'm going to use my names to compare them to your example to make sure I understand. My name textfield that is used in my custom cell is going to be used to insert text into an array. So the name would be the recipe name. Each name array has an array inside of it made up of the other three textfields from within that same cell. It would be like the ingredients array in your example. In your example you have a name array. Each name has an array that is appended by the cell's other textfields. So if you put Shepards Pie as the name that would be in the same array as say Pudding. – CoderCoop Jul 23 '20 at 21:30
  • The pudding or Shepards Pie node/text will have an array attached to it that would be filled out by the ingredients textfields. So the row could be Pizza. The row with the pizza name will have cheese, pepperoni, bread, etc and that will get stored in an array under pizza which is also in an array. If I'm understanding you correctly. – CoderCoop Jul 23 '20 at 21:32
  • @CoderCoop I added a little more to my answer but I think you're on the right track. I would prefer to call it an Array of Recipe Objects with each Recipe Object containing an array of ingredients. The Recipe object has a name which corresponds to Section headers in a tableView and ingredients which correspond to the rows within each Section. – Jay Jul 23 '20 at 21:44
  • Okay sounds good. I kind of understand your code but because I am somewhat illiterate in code, I can't figure out how to make it work for me. How do I use the textfield in a row to name the Object that has the other row's textfields in an array? I'll edit my question to show what I mean. – CoderCoop Jul 23 '20 at 21:48
  • Hey Jay. Were you able to see if what I edited made sense? – CoderCoop Jul 24 '20 at 15:08
  • @CoderCoop It's an array of objects and array's don't have names, they have indexes such as 0, 1, 2, 3 etc. If you review my answer, `recipeArray` is an array of RecipeClass objects; the Object at array index 0 is the Shepards Pie RecipeClass. The object at index 1 will be the Meatloaf Recipe RecipeClass. Within each Recipe class, there's an ingredients array; String objects in this case. *textfield in a row to name the Object* You don't, tableView displays the contents of the recipeArray where each RecipeClass name is a section header and the ingredients array are the rows underneath each. – Jay Jul 24 '20 at 18:11
  • Ok. I am more concerned with the nodes and how they are sent to my database. I am just using the table to be able to create cells depending on how many people I need to send the information too. I am not retrieving it yet and have somewhat of an understand on how to do a basic retrieval. But will probably need help with each specific user. I created a class for the cell that I have put in the question under an edit section. I don't know what to do after that. – CoderCoop Jul 24 '20 at 18:19
  • Hey Jay. I took your advice. I completely changed my approach. I made some good progress but I am having a hard time with setting the labels to the textfield input. If you could check out my latest question that would be awesome. – CoderCoop Jul 27 '20 at 22:03
  • Hey Jay! I have made some real progress on this problem and posted a new question about it. If you could check it out that would be awesome! – CoderCoop Aug 06 '20 at 20:14
  • @CoderCoop I would need a link since you've posted a number of questions (and already answered them yourself). – Jay Aug 06 '20 at 21:13
  • https://stackoverflow.com/questions/63289856/send-form-to-users-using-firebase-realtime-database – CoderCoop Aug 06 '20 at 21:16
0

I solved one of my problems. I needed to change the line of code:

let cell = tableView.dequeueReusableCell(withIdentifier: AddPersonCell) as! ConsiderationsCell

I needed to use cellForRow

let index = IndexPath(row:0,section:0)
guard let cell = tableView.cellForRow(at:index) as? ConsiderationsCell else { print("Not shown") ; return }

This only solved one of my problems. Now I need to make it work for multiple rows.

CoderCoop
  • 79
  • 5