skip to Main Content

Updating cartArray from ViewModel doesn’t append to the current elements, but adds object everytime freshly. I need to maintain cartArray as global array so that it can be accessed from any view of the project. I’m adding elements to cartArray from ViewModel. I took a separate class DataStorage which has objects that can be accessible through out the project

Example_AppApp.swift
import SwiftUI

@main
struct Example_AppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(DataStorage())
        }
    }
}
DataStorage.swift

import Foundation

class DataStorage: ObservableObject {
    @Published var cartArray = [Book]()
}
ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var showSheetView = false

    var body: some View {
        NavigationView{
            
        ListViewDisplay()
           .navigationBarItems(trailing:
                    Button(action: {
                        self.showSheetView.toggle()
                    }) {
                        Image(systemName: "cart.circle.fill")
                            .font(Font.system(.title))
                    }
                )
            
            
 
        }.sheet(isPresented: $showSheetView) {
            View3()
        }
        
    }
}



struct ListViewDisplay: View{
    
    
    var book = [
       Book(bookId: 1 ,bookName: "Catch-22"),
       Book(bookId: 2 ,bookName: "Just-Shocking" ),
       Book(bookId: 3 ,bookName: "Stephen King" ),
       Book(bookId: 4,bookName: "A Gentleman in Moscow"),
    ]
    
    var body: some View {
        List(book, id: .id) { book in
            Text(book.bookName)

            NavigationLink(destination: View1(book: book)) {
            
        }
        }
        
}
}



View1Modal.swift

import Foundation

struct Book: Codable, Identifiable {
    var id:String{bookName}  
    var bookId : Int
    var bookName: String

}
 struct BookOption: Codable{
    var name: String
    var price: Int
}

View1ViewModel.swift
import Foundation
import Combine


class View1ViewModel : ObservableObject{
    
    var dataStorage = DataStorage()
   
    func addBook (bookId:Int ,bookName : String){
           dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
        print(dataStorage.cartArray)
    }
  
}

View1.swift
import SwiftUI

struct View1: View {
    
    @ObservedObject var vwModel = View1ViewModel()
    @EnvironmentObject var datastrg: DataStorage
   
    var book:Book
    
    var body: some View {
        
        Text(book.bookName).font(.title)
        Spacer()
        Button(action: {
            vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
            
        }, label: {
            Text("Add Book to Cart")
            
                .frame(maxWidth: .infinity, minHeight: 60)
                .background(Color.red)
                .foregroundColor(Color.white)
                .font(.custom("OpenSans-Bold", size: 24))
            
        })
        
 
        
    }
}

View3.swift

import SwiftUI

struct View3: View {
    @EnvironmentObject var datastorage : DataStorage
    var body: some View {
        NavigationView {
            List(datastorage.cartArray,id:.id){book in

   
                    VStack{
                    Text(book.bookName)
                        .font(.custom("OpenSans-Bold", size: 20))
                          
                        }
                    
            }
            
                    .navigationBarTitle(Text("Cart"), displayMode: .inline)
                }
    }
}

When addBook func is called for the first time it prints as

[Example_App.Book(bookId: 1, bookName: "Catch-22")]

When I go back and come back to this View1 and add another book by calling addBook func it adds as new object to cartArray

[Example_App.Book(bookId: 3, bookName: "Stephen King")]

Printing number of elements in cartArray gives as 1 element instead of 2 elements. When I go to View3 and display the Books in list, cartArray shows as empty(0 elements)

I think there is something wrong with var dataStorage = DataStorage() in ViewModel class. Everytime this is being created freshly, so the prevoius values are not stored. But I couldn’t understand how to preserve its state
How to display List in View3 ? Any ideas/ suggestions will be helpful

2

Answers


  1. You are not calling your function addBook anywhere, add an onappear to your view3 calling the function and your list will populate with data.

    Login or Signup to reply.
  2. You need to have one instance of DataStorage that gets passed around. Any time you write DataStorage() that creates a new instance.

    .environmentObject will let you inject that one instance into the view hierarchy. Then, you can use the @EnvironmentObject property wrapper to access it within a View.

    Inside View1, I used onAppear to set the dataStorage property on View1ViewModel — that means that it has to be an optional on View1ViewModel since it will not be set in init. The reason I’m avoiding setting it in init is because an @EnvironmentObject is not set as of the init of the View — it gets injected at render time.

    @main
    struct Example_AppApp: App {
        var dataStorage = DataStorage()
        
        var body: some Scene {
            WindowGroup {
                ContentView().environmentObject(dataStorage)
            }
        }
    }
    
    class DataStorage: ObservableObject {
        @Published var cartArray = [Book]()
    }
    
    struct ContentView: View {
        @State var showSheetView = false
        
        var body: some View {
            NavigationView{
                
                ListViewDisplay()
                    .navigationBarItems(trailing:
                                            Button(action: {
                        self.showSheetView.toggle()
                    }) {
                        Image(systemName: "cart.circle.fill")
                            .font(Font.system(.title))
                    }
                    )
            }.sheet(isPresented: $showSheetView) {
                View3()
            }
        }
    }
    
    struct ListViewDisplay: View {
        var book = [
            Book(bookId: 1 ,bookName: "Catch-22"),
            Book(bookId: 2 ,bookName: "Just-Shocking" ),
            Book(bookId: 3 ,bookName: "Stephen King" ),
            Book(bookId: 4,bookName: "A Gentleman in Moscow"),
        ]
        
        var body: some View {
            List(book, id: .id) { book in
                Text(book.bookName)
                NavigationLink(destination: View1(book: book)) {
                    
                }
            }
            
        }
    }
    
    struct Book: Codable, Identifiable {
        var id:String{bookName}
        var bookId : Int
        var bookName: String
        
    }
    
    struct BookOption: Codable{
        var name: String
        var price: Int
    }
    
    class View1ViewModel : ObservableObject{
        
        var dataStorage : DataStorage?
        
        func addBook (bookId:Int ,bookName : String) {
            guard let dataStorage = dataStorage else {
                fatalError("DataStorage not set")
            }
            dataStorage.cartArray.append(Book(bookId:bookId, bookName: bookName)) // Adding to global array
            print(dataStorage.cartArray)
        }    
    }
    
    struct View1: View {
        
        @ObservedObject var vwModel = View1ViewModel()
        @EnvironmentObject var datastrg: DataStorage
        
        var book:Book
        
        var body: some View {
            
            Text(book.bookName).font(.title)
            Spacer()
            Button(action: {
                vwModel.addBook(bookId: book.bookId, bookName: book.bookName)
                
            }, label: {
                Text("Add Book to Cart")
                
                    .frame(maxWidth: .infinity, minHeight: 60)
                    .background(Color.red)
                    .foregroundColor(Color.white)
                    .font(.custom("OpenSans-Bold", size: 24))
                
            })
                .onAppear {
                    vwModel.dataStorage = datastrg
                }
        }
    }
    
    struct View3: View {
        @EnvironmentObject var datastorage : DataStorage
        var body: some View {
            NavigationView {
                List(datastorage.cartArray,id:.id){book in
                    VStack{
                        Text(book.bookName)
                            .font(.custom("OpenSans-Bold", size: 20))
                        
                    }
                }
                .navigationBarTitle(Text("Cart"), displayMode: .inline)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search