简体   繁体   中英

@EnvironmentObject property not working properly in swiftUI

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

您没有在任何地方调用您的函数 addBook,在调用该函数的 view3 中添加一个 onappear,您的列表将填充数据。

You are creating a local DataStorage in your view model instead of using the one in the @EnvironmentObject so instead inject it into the view model.

First change the view model so you must inject DataStorage

class ViewModel: ObservableObject {
    var dataStorage: DataStorage
    //...

    init(dataStorage: DataStorage) {
        self.dataStorage = dataStorage
    }
    //...
}

Then change View1 so you create the view model in an init

struct View1: View {
    @ObservedObject var vwModel: ViewModel
    @EnvironmentObject var datastrg: DataStorage

    init() {
        vwModel = ViewModel(dataStorage: datastrg)
    }
    //...
}

You need to have one instance of DataStorage that gets passed around. Any time you write DataStorage() that create 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)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM