Firebase accessing sub-collection

Hi @Chris_Parker,

Using Firestone, How would I display a document of subcollection?

This is where I have started. I just can’t see to get past the collection ID:

class DisplayTest: UIViewController {
    
    
    @IBOutlet var displayText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        fetchUser()
    }
    

    
    func fetchUser() {
        let db = Firestore.firestore()
        
        let users = db.collection("pickup")
        
        let userUID = Auth.auth().currentUser?.uid
        
        let query = users.whereField("uid", isEqualTo: userUID!)
        
        query.getDocuments { snapshot, error in
            guard error == nil else { return}
            guard let snapshot = snapshot else {
                return
            }
            for doc in snapshot.documents {
                let userName = doc["street"] as! String

                self.displayText.text = userName

            }
        }
    }

}

@coder3000

Hi Gilbert,

I have made this a new thread since the existing thread has already been answered (a long time ago) and that thread was getting very long.

It looks like you are adding the pickup collection within the users collection. Is that the case?

If so, are you adding the pickup collection within a specific user in the users collection?

(edit) The other question I have is what does the pickup collection store?

  1. Yes sir. Pickup is being added to users.
  2. Pickup is a collection specific to individual users.
  3. Here is what Pickup contains:

 let db = Firestore.firestore()
                // Extract user id to use in file path

                db.collection("users").addDocument(data:
                                                    ["firstName": firstName,
                                                     "lastName": lastName,
                                                     "username": username,
                                                     "email": email,
                                                     "cellphoneNumber": cellphoneNumber,
                                                     "uid": Auth.auth().currentUser?.uid ?? "",
                                                     "profileimageurl": imageUrl.absoluteString]
                                                   
                ).collection("/pickup").addDocument(data: ["street": street,
                                                                 "city": city,
                                                                 "state": state,
                                                                 "country": country,
                                                                 "latitude": latitude,
                                                                 "longitude": longitude,
                                                                 "postalCode": postalCode,
                                                                 "unit": unit,
                                                                 "instructions": instructions
                
                                                                        ]
                
                )
                { (error) in

                    if error != nil {

                        // 12. Show error message
                        // Unwrap error because it is Optional.
                        if let error = error {
                            self.showError(message: error.localizedDescription)
                        }
                    } else {
                        self.showError(message: "Successfully saved user data.")

                        // 13. Transition to the home screen
                        self.transitionToHome()
                    }
                }
            }
        }

I will need to display as well as post(update) this data.

@coder3000

I think you are pretty close with the code you have. Just remove the forward slash from the sub-collection path so it’s like this:

let db = Firestore.firestore()
                // Extract user id to use in file path

                db.collection("users").addDocument(data:
                                                    ["firstName": firstName,
                                                     "lastName": lastName,
                                                     "username": username,
                                                     "email": email,
                                                     "cellphoneNumber": cellphoneNumber,
                                                     "uid": Auth.auth().currentUser?.uid ?? "",
                                                     "profileimageurl": imageUrl.absoluteString])
                .collection("pickup").addDocument(data: ["street": street,
                                                                 "city": city,
                                                                 "state": state,
                                                                 "country": country,
                                                                 "latitude": latitude,
                                                                 "longitude": longitude,
                                                                 "postalCode": postalCode,
                                                                 "unit": unit,
                                                                 "instructions": instructions ])
                { (error) in

Some things to maybe think about:

  • I would think that you are going to have to validate the username at Sign Up to ensure that it is not already in use.
  • Also validate the cellphoneNumber to ensure they have not miss-typed the number, not enough digits or maybe entered a number that already exists in the database. Some people do dumb things.
  • Are you going to require the country code with the cellphone number?
  • You could derive the country code from the country they entered and have a lookup table to figure it out.

I am still unable to access the data beyond the collection id. I can access everything in users but not in pickup/collectionID/…

import FirebaseAuth
import FirebaseDatabase
import FirebaseFirestore
import FirebaseStorage
import UIKit

class DisplayTest: UIViewController {
    
    
    @IBOutlet var displayText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        fetchUser()
    }
    

    
    func fetchUser() {
        let db = Firestore.firestore()
        
        let users = db.collection("pickup")
        
        let userUID = Auth.auth().currentUser?.uid
        
        let query = users.whereField("uid", isEqualTo: userUID!)
        
        query.getDocuments { snapshot, error in
            guard error == nil else { return}
            guard let snapshot = snapshot else {
                return
            }
            for doc in snapshot.documents {
                let userName = doc["street"] as! String

                self.displayText.text = userName

            }
        }
    }

}

Can you display the street document? I have’t figured out how to display and update sub collections.

No you have to access the pickup collection via the users collection since the pickup collection is nested within the users collection.

The first step is to identify the user in the users collection and then access the pickup within that users collection.

I have not written any code yet to try to mirror what you are doing.

Were you able to sign up a new user and save the pickup data at the same time?

Yes. Nothing has changed except there is a new collection id.

@coder3000

OK that’s great. I’ll have to dummy up some fields in my test code here to come up with a fetchUser() that retrieves the fields from the pickup collection. Might be while before I get back to you.

1 Like

Sounds good. Thank you so much!

I just thought about this. Will I as an administrator be able to update the Pickup data is other users? I will be making a separate app managing the users in this app. I will be sending them order regularly similar to like a Doordash business.

You can, but you should not be making changes to users data. What you should have is a screen where the user edits their own data themselves and makes any alterations if needed. I mean if they change their address then it’s up to them to update that data.

I have made those provisions. Users can edit their own data. I mean to make a means of communication between me and individual users. When a customer places an order it should populate the pickup, dropoff, and status collections.

I see what you mean.

Maybe you need some kind of notification screen that will show messages to the user?

I am currently setting up alerts to look and behave like this: Food delivery app - YouTube

That’s quite complex.

I created the basic design for it though. The only hiccup still is the ability to read and update sub collections. I need to differentiate between pickup, drop-off, and status. That’s why I need to use collections. It’s the collection’s ID that I can’t figure out how to get into so I can use the data they’re in. I know we do this all the time when we sign in. Authentication can automatically read past the collection id to get to the user’s data. This is what I need to do to get to the user’s pickup, drop-off, and status. How can I make that work? It is this tidbit of information that will get me where I need to be.

I did happen to stumble upon this information: Cloud Firestore Data model  |  Firebase
It looks like I am trying to make a more complex document rather than a collection. How might I accomplish this?

@coder3000

Hi Gilbert. I’ve been somewhat busy so have been unable to look into your request for help. I’ll have a look tomorrow night my time since it is now rather late.

1 Like

@coder3000

Hi Gilbert.

I’m having a look at dealing with sub-collections now. I’ve got my code working to save pickup details but there is a few things I need to have clear before I figure out what code is required to retrieve those pickup details.

Where in your App are you wanting to retrieve that data? By that I mean which ViewController? I’m just trying to get an idea of the App hierarchy so that I can mirror what you are doing in my test App.

When a new user signs up I assume that you are getting their pickup details at that time or do you have a separate screen that collects that detail?

(edit) By the way, I now have Xcode 14 installed (as of a couple of days ago) and just had a heck of a time trying to get Firebase re-installed via Swift Package Manager in the test App I am using to help you out. Seems to be stable now.

That’s great to here!

Where in your App are you wanting to retrieve that data? By that I mean which ViewController? I’m just trying to get an idea of the App hierarchy so that I can mirror what you are doing in my test App.

I have a view controller called TasksViewController were all the alerts will be coming in. I’ll be using:

pod 'FloatingPanel' 

…to display all my alerts. It very much like what you have seen in the video I shared with you.

When a new user signs up I assume that you are getting their pickup details at that time or do you have a separate screen that collects that detail?

That is correct at the moment of signing up is when the user gets the pickup, delivery and status details.

{
  "pickup": {
    "name": "Kermit's Salads",
    "phoneNumber": "+12125551234",
    "street": "26 Broadway",
    "city": "New York City",
    "state": "NY",
    "postalCode": "10004",
    "country": "US",
    "latitude": 40.716038,
    "longitude": -74.00631,
    "unit": "104B",
    "instructions": "Use back entrance"
  },
  "dropoff": {
    "name": "Miss Piggy",
    "phoneNumber": "+12125555678",
    "street": "312 Broadway",
    "city": "New York City",
    "state": "NY",
    "postalCode": "10004",
    "country": "US",
    "latitude": 40.24377,
    "longitude": -74.10277,
    "unit": "Suite 300",
    "instructions": "Leave with security guard"
  },
  "orderDetails": [
    {
      "title": "Salad Green",
      "quantity": 3
    }
  ],
  "pickupTime": "2015-09-22T18:30:00.0000000Z",
  "quoteExternalReference": "basket_e699aece",
  "deliveryExternalReference": "order_713a8bd9",
  "tip": 3.5,
  "deliveryId": "cb1915b1-3330-4477-b4bf-88c9b935943c",
  "statusUpdateUrl": "https://mySite.com/v1",
  "dropoffTime": "2015-09-22T18:30:00.0000000Z",
  "controlledContents": "Alcohol,Tobacco",
  "allowedVehicles": "Walker,Bicycle,DeliveryBicycle,Car,Van",
  "orderValue": 22.5,
  "brandName": "BigBellyBurger",
  "currency": "USD"
}

Once I recieve a customers order in a separate app I will push that data to populate those fields just like in the video. Eventually I’ll be charting the data in a separate screen for the driver’s purposes as well as my own, but I’ll tackle that part later.

Btw… Pickup, Dropoff, and orderDetails will have their own screens. They will pop up into the TaskViewController when I have a routine in place.

Also you made a point of making username and phone numbers unique at the signup. I would like that as well. I was going to try to modify it, but if you already know how to implement the check, for unique entry, please share.