Coding while Loading
Coding while Loading

Reputation: 413

Sending data to Firebase as an array is giving me bugs

As of now, my data base is structured like this - which works for me. But here is the mishap i am having - These two nodes separated by auto ID's are actually the same post.

If you see the previous was my DB was structured (using an array), you will see that each post could have multiple images, dates and plans. which worked well, but gave me nightmares when trying to access indices and such. So i switched to this more advisable way. But now i am having issues grouping the posts together. For instance, these two below should be apart of the exact same post.

How would i be able to assure that when i read data from my DB - i group each auto ID node together with their appropriate posts? ie: These two nodes are actually the same post - as you can tell by them each having the same title.

   Planit
     -LETR-XJvQsZCOpG-T1N
        Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
        date: "Jun 9, 2018 at 10:00 AM"
        image: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c..
        plan: "Bike Rodeo, Safety Presentation and Riding Tour o"
        title: "This weekend Plans?"

     -LETR-XKqXf7NY1gh6Jm
        Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
        date: "Jun 11, 2018 at 10:00 AM"
        image: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c..
        plan: "Fun night out at the Parlor"
        title: "This Weekend Plans?""

My DB was previously structured in this way- which worked perfect on a functional level, but was wrong by Firebase programmatic principals. and i am only putting this in so you can tell the structure that i actually need, these posts need to be grouped in someway so that when i read the from FB and load them into a table view, the appropriate data is stored together - For example - 1 post can have 2 images, 2 plans, and 2 dates, but only 1 TITLE (of post).

Plant: 
 -LETYTJh1WEHGgGcVCEp
    dates
       0: " "
       1: " "
    images
       0: " "
       1: " "
    plans
       0: " "
       1: " "
    senderId
       0: " "
    title: close

Below is the function i use to push the data to Firebase - i looped through an array so that each iteration would push the images, dates, plans, title as one "bunch".

    for planits in chosenPlanArray {
        DataService.instance.uploadPlanitData(withTitleOfPlan: [planits.nameOfEvent!], 
           withDate: [planits.eventStartsAt!], 
           withImage: [planits.imageForPlan!], 
           withTitle: nameOfPlanit.text!, 
           forUID: (Auth.auth().currentUser?.uid)!, 
           sendComplete: { (isGood) in

            if isGood {

            }
        })
    }

And here is my actual implementation in the Data Service Class

func uploadPlanitData(withTitleOfPlan plan: [String], withDate: [String], withImage: [String], withTitle: String, forUID uid: String, sendComplete: @escaping (_ status: Bool) -> ()) {


    _REF_PLANITS.childByAutoId().updateChildValues(["plan" : plan, "date" : withDate, "image": withImage, "title": withTitle, "Uid": uid])
    sendComplete(true)
}

Upvotes: 1

Views: 143

Answers (1)

Jay
Jay

Reputation: 35648

I think I understand the question and there are a few ways to go about it.

1) Group within the post

PlanIt
 plans
  -LETR-XJvQsZCOpG-T1N
    Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
    date: "20180609"
    images:
       image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
       image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
    plans
       plan_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
       plan_1: "Bike Rodeo, Safety Presentation and Riding Tour o"
    title: "This weekend Plans?"
  -Yjijiioimoifijjiuee
    Uid: "asdaasdas"
    etc

The keys of image_0, image_0, plan_0, plan_1 etc would be created with childByAutoId.

With this, you can load a post once and you get all of the needed child data with it.

Note that while this structure appears 'deep' which is usually discouraged in Firebase NoSQL, you won't be performing any queries on the deep data and that data is specific to the post so it's fine in this case.

2) Denormalize/Flatten the structure

PlanIt
 plans
  -LETR-XJvQsZCOpG-T1N
    Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
    date: "20180609"
    title: "This weekend Plans?"
  -UYijs09as9jiosdijfi
    Uid: "some uid"
    date: "20180609"
    title: "some title"

 all_images:
    -LETR-XJvQsZCOpG-T1N
       image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
       image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."

 all_plan_details
    -LETR-XJvQsZCOpG-T1N
       detail_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
       detail_1: "Bike Rodeo, Safety Presentation and Riding Tour o"

the second option would require an initial loading of the post node to obtain it's key and then one call to the all_images node to load the images and a call to all_plans to get it's plans. Note that I am using the posts key to reference the all_images and all_plans child node.

The idea here is that we use the key of the PlanIt child as a reference to that node within the all_images and all_plans node. image_0, image_1, plan_0, plan_1 are keys generated with .childByAutoId

The second option is kind cool because for example, say you have a tableview with a list of just titles and dates and then when the user taps it, you can load images or plans into another view... This option would be loading far less data at a time which makes it a bit more manageable memory wise.

EDIT

To clarify the process, here's the code to read in the plan, images and plan details and print them to the console based on the structure above.

let mainRef = self.ref.child("PlanIt")
let plansRef = mainRef.child("plans")
let allImagesRef = mainRef.child("all_images")
let allPlanDetailsRef = mainRef.child("all_plan_details")

plansRef.observeSingleEvent(of: .value, with: { snapshot in

 for child in snapshot.children {
     let childSnap = child as! DataSnapshot
     let dict = childSnap.value as! [String: Any]
     let planKey = childSnap.key
     let title = dict["title"] as! String

     let thisPlansImagesRef = allImagesRef.child(planKey)
     let thisPlansDetailsRef = allPlanDetailsRef.child(planKey)
     thisPlansImagesRef.observeSingleEvent(of: .value, with: { imageSnapshot in

         thisPlansDetailsRef.observeSingleEvent(of: .value, with: { planDetailSnapshot in
             //printing is done here because firebase is asynchronous and all data 
             //  will be valid for each node ONLY at this point
             print("plan: \(planKey)  \(title)")
             for imageChild in imageSnapshot.children {
                 let imageChildSnap = imageChild as! DataSnapshot
                 let image = imageChildSnap.value as! String
                 print("  image: \(image)")
             }

             for planDetailChild in planDetailSnapshot.children {
                 let planDetailChildSnap = planDetailChild as! DataSnapshot
                 let planDetail = planDetailChildSnap.value as! String
                 print("  planDetail: \(planDetail)")
             }
         })
     })
  }
})

and the output will be:

plan: plan_0  This weekend plans
  image: some image for plan_0
  image: another_image for plan_0
  planDetail: some plan detail for plan_0
  planDetail: another plan detail for plan_0
plan: plan_1  Next weekend plans

I added some text values within my Firebase so it would be more clear what's going on in the output.

Upvotes: 1

Related Questions