Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
987 views
in Technique[技术] by (71.8m points)

firebase - Mapping arrays in Firestore documents to Swift structs in a SwiftUI app

I managed to get the array data from Firestore, but as you can see from the images when I iterate over the orderDetails, the details repeat themselves three times instead of showing the three details!

Could you please check it and help me to know what is wrong with it?

Please be patient as I'm new to SwiftUI :)

Here is the Struct:

import SwiftUI
import FirebaseFirestoreSwift
import Firebase

struct OrderDetailsStruct: Identifiable, Codable {
    @DocumentID var id: String?
    var prodName: String
      var prodPic: String
      var prodPrice: String
      var prodQuantity: Int
    
   
}

struct OrderData: Identifiable, Codable {
  @DocumentID  var id: String?
    var orderStatus: String
      var timeStamp: Date
      var orderDetails: [OrderDetailsStruct]?
    
}

Here is the view model:

class OrderDataModel : ObservableObject{
    @Published var userID = "[email protected]"
    @Published var orderData = [OrderData]()
    
    private var db = Firestore.firestore()
    
    func fetchData() {
        
        
        db.collection("orders2/users/(self.userID)").addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                print("No documents")
                return
            }
            
            self.orderData = documents.compactMap { queryDocumentSnapshot -> OrderData? in
                return try? queryDocumentSnapshot.data(as: OrderData.self)
                
            }
        }
    }
    
}

Here is the order data View:

struct OrdersListView: View {
    
     var formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "DD/M MM/YYYY"
        return formatter
    }()
    
    @Environment(.presentationMode) var present
   // @ObservedObject var orderdetails = getOrderData()
    @StateObject var orderData = OrderDataModel()
    var body: some View {
        
        VStack(alignment: .trailing) {
            HStack(spacing: 20){
                
                Button(action: {present.wrappedValue.dismiss()}) {
                    
                    Image(systemName: "chevron.left")
                        .font(.system(size: 20, weight: .heavy))
                    //.foregroundColor(Color("pink"))
                }
                Spacer()
                Text("My orders")
                    .font(.system(size: 25))
                    //.fontWeight(.heavy)
                    .foregroundColor(.black)
                
                Spacer()
            }
            .padding()
            List{
                ForEach(self.orderData.orderData) {i  in
                    NavigationLink(
                        destination: OrderDetailsView(orderID: i.id!),
                        label: {
                            VStack(alignment: .leading){
                                Text("Order Id: (i.id!)")
                                .font(.system(size : 13))
                                
                                Text("Order Date: (self.formatter.string(from: i.timeStamp) )")
                                .font(.system(size: 13))
                                
                                
                                Text("Order Status: (i.orderStatus)")
                                    .font(.system(size: 13))
                           
                            }
                        })
                    
                }
            }.environment(.layoutDirection, .rightToLeft)
        }.onAppear{
            orderData.fetchData()
        }
        .navigationBarHidden(true)
        .navigationBarBackButtonHidden(true)
            
       
        
    }
    
   
}

Here is the order Details View:

import SDWebImageSwiftUI

struct OrderDetailsView: View {
   
    
    @StateObject var orderData = OrderDataModel()
    @State var orderID : String
    
    var body: some View {
         
        VStack(alignment: .leading) {
            ScrollView {
                        ForEach(orderData.orderData) { details in
                           
                                    ForEach((details.orderDetails)!) { orderdetails in
                                        if self.orderID == orderdetails.id {
                                            HStack{
                                               
                                        AnimatedImage(url: URL(string: orderdetails.prodPic))
                                            .resizable()
                                            .aspectRatio(contentMode: .fit)
                                            .frame(width: 50, height: 50)
                                            .background(Color(#colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1)))
                                            .cornerRadius(10)
                                                
                                                VStack(alignment: .leading){
                                       
                                            Text("Product Name: (orderdetails.prodName)")
                                                .font(.system(size : 13))
                                        Text("Price: (orderdetails.prodPrice)")
                                            .font(.system(size : 13))
                                        Text("Quantity: (orderdetails.prodQuantity)")
                                            .font(.system(size : 13))
                                                }
                                            }
                                        }
                                    }
                              
                        }
                        .padding(.leading, -10.0)
                    }
                   
            
            .onAppear{
                orderData.fetchData()
        }
        }
        
    }
}

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Mapping Firestore data is a lot easier when you use Firestore's Codable support.

For the basics, read my article SwiftUI: Mapping Firestore Documents using Swift Codable - Application Architecture for SwiftUI & Firebase

To handle nested data, just define another struct.

Here's how your code would like:

struct OrderDetails: Codable {
  var prodName: String
  var prodPic: String
  var prodPrice: String
  var prodQuantity: Int
}

struct OrderData: Codable {
  var orderStatus: String
  var timeStamp: Date
  var orderDetails: [OrderDetails]?
}

Note that I marked the orderDetails array as optional, to prevent the mapping from breaking in case the attribute doesn't exist on your document.

Here's the view model:

class OrderDataModel: ObservableObject {
  @Published var userID = "[email protected]"
  @Published var orderData = [OrderData]()
    
  private var db = Firestore.firestore()
    
  func fetchData() {
    db.collection("orders2/users/(self.userID)").addSnapshotListener { (querySnapshot, error) in
    guard let documents = querySnapshot?.documents else {
      print("No documents")
      return
    }

    self.orderData = documents.compactMap { queryDocumentSnapshot -> OrderData in
      return try? queryDocumentSnapshot.data(as: OrderData.self)
    }
  }
}

(As a side note - class names should always start with an uppercase letter.)

And the view:

struct OrderDetailsView: View {
    
  @Environment(.presentationMode) var present
    
  @StateObject var orderData = OrderDataModel()

  var body: some View {
    VStack(alignment: .trailing) {
      List {
        ForEach(orderData.orderDetails) { details in
          VStack(alignment: .leading){
            Text("(details.orderStatus)")
            Text("(details.timeStamp)")
          }
        }
      }
    }
    .onAppear{
      orderData.fetchData()
    }
  }
}

EDIT: after looking at Mohammed's code, it turned out the actual issue for seeing duplicate entries in the list was that the document IDs on the order details weren't unique. As List requires all items to be unique, this issue results in unpredicted behaviour. The best solution is to make sure the document IDs are unique.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...