Reputation: 3
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()
}
}
}
}
Upvotes: 0
Views: 2197
Reputation: 7254
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.
Upvotes: 2