title: How to create subcollections with Firestore in SwiftUI
body: <p>As much as I understand the theory behind the subcollections and SwiftUI, I have a hard time applying it on code. The idea is to create a supplier which in turn has brands which in turns has models, colors, sizes, etc. I'm creating a patient/inventory management app and I copied the patient code (which I got a lot of help from @peterfriese) to the inventory one provided all I needed was to change a few lines to be able to enable a subcollections to create itself but I'm completely baffled. I have as much difficulty applying the code to the view as I do for viewModel. I also, for some reason, have encountered an issue with the saving an edit for my supplier which simply adds an entry instead of editing it. Here is my code.</p>
  <p>SupplierListView</p>
  <pre><code>
  
  import SwiftUI
  
  
  struct SupplierListView: View {
      
      @ObservedObject private var viewModel = SupplierViewModel()
      @State private var presentAddNewSupplierScreen = false
      
      
      private var addButton: some View {
         Button(action: { self.presentAddNewSupplierScreen.toggle() }) {
           Image(systemName: &quot;plus&quot;)
         }
       }
      
      private func supplierRowView(supplier: SupplierModel) -&gt; some View {
         NavigationLink(destination: SupplierDetailView(supplier: supplier)) {
           VStack(alignment: .leading) {
              Text(supplier.supplier ?? &quot;&quot;)
               .font(.headline)
             
           }
         }
       }
      
      var body: some View {
          NavigationView {
              List {
                     ForEach (viewModel.suppliers) { supplier in
                       supplierRowView(supplier: supplier)
                     }
                     .onDelete() { indexSet in
                       viewModel.removeSuppliers(atOffsets: indexSet)
                     }
                   }
                   .navigationBarTitle(&quot;Suppliers&quot;)
                   .navigationBarItems(trailing: addButton)
                   .onAppear() {
                     print(&quot;SupplierListView appears. Subscribing to data updates.&quot;)
                     self.viewModel.subscribe()
                   }
                   .onDisappear() {
                     // By unsubscribing from the view model, we prevent updates coming in from
                     // Firestore to be reflected in the UI. Since we do want to receive updates
                     // when the user is on any of the child screens, we keep the subscription active!
                     //
                     // print(&quot;BooksListView disappears. Unsubscribing from data updates.&quot;)
                     // self.viewModel.unsubscribe()
                   }
                   .sheet(isPresented: self.$presentAddNewSupplierScreen) {
                     SupplierEditView()
                   }
                 }
      }
  }
  
  struct SupplierListView_Previews: PreviewProvider {
      static var previews: some View {
          SupplierListView()
      }
  }
  </code></pre>
  <p>SupplierEditView</p>
  <pre><code>
  import SwiftUI
  
  enum ModeSupplier {
    case new
    case edit
  }
  
  enum ActionSupplier {
    case delete
    case done
    case cancel
  }
  
  extension Optional where Wrapped == String {
      var _boundSupplier: String? {
          get {
              return self
          }
          set {
              self = newValue
          }
      }
      public var boundSupplier: String {
          get {
              return _boundSupplier ?? &quot;&quot;
          }
          set {
              _boundSupplier = newValue.isEmpty ? nil : newValue
          }
      }
  }
  
  
  struct SupplierEditView: View {
      
     @Environment(\.presentationMode) var presentationMode
      @State var presentActionSheet = false
  
      
      @ObservedObject var viewModel = SupplierDetailViewModel()
      var mode: Mode = .new
      var completionHandler: ((Result&lt;Action, Error&gt;) -&gt; Void)?
      
      
      var cancelButton: some View {
        Button(action: { self.handleCancelTapped() }) {
          Text(&quot;Cancel&quot;)
        }
      }
      
      var saveButton: some View {
        Button(action: { self.handleDoneTapped() }) {
          Text(mode == .new ? &quot;Done&quot; : &quot;Save&quot;)
        }
        .disabled(!viewModel.modified)
      }
  
      var body: some View {
          NavigationView {
              Form {
                  Section(header: Text(&quot;Supplier Name&quot;)) {
                      TextField(&quot;Supplier Name&quot;, text: $viewModel.supplier.supplier.bound)
                  }
                  if mode == .edit {
                          Section {
                            Button(&quot;Delete Supplier&quot;) { self.presentActionSheet.toggle() }
                              .foregroundColor(.red)
                          }
                        }
              }
          
          .navigationBarTitle(&quot;New Supplier&quot;, displayMode: .inline)
          .navigationBarTitleDisplayMode(mode == .new ? .inline : .large)
  
          .navigationBarItems(
              leading: cancelButton,
              trailing: saveButton
            )
              .actionSheet(isPresented: $presentActionSheet) {
                  ActionSheet(title: Text(&quot;Are you sure?&quot;),
                  buttons: [
                  .destructive(Text(&quot;Delete Supplier&quot;),
                                  action: { self.handleDeleteTapped() }),
                          .cancel()
                      ])
                  }
          }
      }
  
  func handleCancelTapped() {
      dismiss()
  }
  
  func handleDoneTapped(){
      viewModel.saveSupplier()
      dismiss()
  }
      
  func handleDeleteTapped() {
        viewModel.handleDeleteTapped()
        self.dismiss()
        self.completionHandler?(.success(.delete))
      }
      
  func dismiss(){
      presentationMode.wrappedValue.dismiss()
  }
  
  }
  
  struct SupplierEditView_Previews: PreviewProvider {
      static var previews: some View {
          let supplier = SupplierModel(id: &quot;&quot;, supplier: &quot;&quot;)
          let supplierViewModel = SupplierDetailViewModel(supplier: supplier)
          return SupplierEditView(viewModel: supplierViewModel, mode: .edit)
      }
  }
  
  
  
  </code></pre>
  <p>SupplierDetailView</p>
  <pre><code>import SwiftUI
  
  struct SupplierDetailView: View {
      
      @Environment(\.presentationMode) var presentationMode
       @State var presentEditSupplierSheet = false
       
       
       var supplier: SupplierModel
            
       private func editButton(action: @escaping () -&gt; Void) -&gt; some View {
         Button(action: { action() }) {
           Text(&quot;Edit&quot;)
         }
       }
      var body: some View {
          Form {
               Section(header: Text(&quot;Supplier Name&quot;)) {
                  Text(supplier.supplier ?? &quot;&quot;)
               }
             
             }
          .navigationBarTitle(supplier.supplier ?? &quot;&quot;)
             .navigationBarItems(trailing: editButton {
               self.presentEditSupplierSheet.toggle()
             })
             .onAppear() {
               print(&quot;SupplierDetailView.onAppear() for \(self.supplier.supplier)&quot;)
             }
             .onDisappear() {
               print(&quot;SupplierDetailView.onDisappear()&quot;)
             }
             .sheet(isPresented: self.$presentEditSupplierSheet) {
               SupplierEditView(viewModel: SupplierDetailViewModel(supplier: supplier), mode: .edit) { result in
                 if case .success(let action) = result, action == .delete {
                   self.presentationMode.wrappedValue.dismiss()
                 }
               }
             }
           }
      }
  
  
  struct SupplierDetailView_Previews: PreviewProvider {
      static var previews: some View {
          let supplier = SupplierModel(id: &quot;&quot;, supplier: &quot;&quot;)
          return
              NavigationView{
          SupplierDetailView(supplier: supplier)
              }
      }
  }
  
  </code></pre>
  <p>SupplierModel</p>
  <pre><code>import Foundation
  import FirebaseFirestoreSwift
  
  struct SupplierModel: Identifiable, Codable {
      @DocumentID var id : String? = UUID().uuidString
      var supplier: String?
  }
  
  </code></pre>
  <p>SupplierModelView</p>
  <pre><code>import Foundation
  import FirebaseFirestore
  import FirebaseFirestoreSwift
  
  
  class SupplierViewModel: ObservableObject {
      @Published var suppliers = [SupplierModel]()
      
      private var db = Firestore.firestore()
      private var listenerRegistration: ListenerRegistration?
  
      deinit {
          unsubscribe()
      }
      
      func unsubscribe() {
         if listenerRegistration != nil {
           listenerRegistration?.remove()
           listenerRegistration = nil
         }
       }
      
      
      func subscribe() {
          if listenerRegistration == nil {
              listenerRegistration = db.collection(&quot;suppliers&quot;).addSnapshotListener { (querySnapshot, error) in
              guard let documents = querySnapshot?.documents else {
                  print(&quot;No suppliers&quot;)
                  return
              }
              
              self.suppliers = documents.compactMap { queryDocumentSnapshot -&gt; SupplierModel? in
                  return try? queryDocumentSnapshot.data(as: SupplierModel.self)
               
              }
          }
      }
  }
      func removeSuppliers(atOffsets indexSet: IndexSet) {
         let suppliers = indexSet.lazy.map { self.suppliers[$0] }
         suppliers.forEach { supplier in
           if let documentId = supplier.id {
             db.collection(&quot;suppliers&quot;).document(documentId).delete { error in
               if let error = error {
                 print(&quot;Unable to remove document: \(error.localizedDescription)&quot;)
               }
             }
           }
         }
       }
  }
  
  </code></pre>
  <p>SupplierDetailModelView</p>
  <pre><code>import Foundation
  import Firebase
  import Combine
  
  class SupplierDetailViewModel: ObservableObject {
      
      @Published var supplier: SupplierModel
      @Published var modified = false
      
      private var db = Firestore.firestore()
      
      private var cancellables = Set&lt;AnyCancellable&gt;()
      
      init(supplier: SupplierModel = SupplierModel(id: &quot;&quot;, supplier: &quot;&quot;)) {
          self.supplier = supplier
          
          self.$supplier
              .dropFirst()
              .sink { [weak self] supplier in
                  self?.modified = true
              }
              .store(in: &amp;cancellables)
      }
      
      func addSupplier(supplier: SupplierModel){
          do {
              let _ = try db.collection(&quot;suppliers&quot;).addDocument(from: supplier)
          }
          catch {
              print(error)
          }
      }
      
      private func updateSupplier(_ supplier: SupplierModel) {
         if let documentId = supplier.id {
           do {
             try db.collection(&quot;suppliers&quot;).document(documentId).setData(from: supplier)
           }
           catch {
             print(error)
           }
         }
       }
      
      private func updateOrAddSupplier() {
          if let _ = supplier.id {
            self.updateSupplier(self.supplier)
          }
          else {
              addSupplier(supplier: supplier)
          }
        }
      
      private func removeSupplier() {
        if let documentId = supplier.id {
          db.collection(&quot;supplier&quot;).document(documentId).delete { error in
            if let error = error {
              print(error.localizedDescription)
            }
          }
        }
      }
      
      func handleDoneTapped() {
        self.updateOrAddSupplier()
      }
      
      func handleDeleteTapped() {
        self.removeSupplier()
      }
      
      func saveSupplier(){
          addSupplier(supplier: supplier)
      }
  
  }
  
  
  
  </code></pre>