1

I'm trying to show array values, which I get from API call. I'm making addToCart request in one view and need to show its result in other view.

class Cart: ObservableObject {

@Published var data: ShoppingCartContent?
@Published var sections: [SectionContent] = []
private var subscriptions = Set<AnyCancellable>() }

extension Cart {

func addToCart(productId: Int, productQty: Int) {
    
    guard let token = UserDefaults.standard.string(forKey: "token") else { return }
    NetworkManager.addProductToCart(token: token, productId: productId, productQty: productQty)
        .sink(
          receiveCompletion: { completion in
              switch completion {
              case .finished:
                  print("finished")
              case .failure(let error):
                  print("AddToCart error", error)
              }
          },
          receiveValue: { value in
              print("Success:", value.success)
              print("Data:", value.data)
              self.data = value.data
              self.sections = value.data.sections
              print("Sections:", self.sections)
          })
        .store(in: &subscriptions) }}

I'm getting data from API, save them in @Published var sections, but I can't show them in other view, print shows it's nil.

struct ShoppingView: View {

@EnvironmentObject var viewModel: Cart

var body: some View {
    ScrollView {
    VStack(alignment: .leading) {
        
    HStack {
        Text("Delivery:")
            .foregroundColor(Color.textFieldGrayColor)
          
        ForEach(viewModel.sections, id: \.self) { section in
            ForEach(section.items, id: \.self) { product in
                Text(product.product_name)
            }
        }
}

}.onAppear {
 print("My cart arr:", cartViewModel.data?.sections)
}
}
}

P.S. ShoppingView - is one of the tabs, I added (environmentObject) in MainView

@EnvironmentObject var viewModel: Cart
ShoppingView().environmentObject(viewModel)

I also tried to access array with @ObservedObject var viewModel = Cart() but it also shows empty array.

2 Answers 2

3

I do not recommend using ShoppingView().environmentObject(Cart()): this will create a new Cart instance every time the view is called. You will finish by re-creating the contents of your variables each time.

What I propose is to replace this call in your original code in the MainView:

@EnvironmentObject var viewModel: Cart

with:

@StateObject var viewModel = Cart()

This will create a StateObject in the MainView that will be passed to ShoppingView through the environment. Depending on your needs, also let viewModel = Cart() works.

Sign up to request clarification or add additional context in comments.

5 Comments

You mean in ShoppinView I should declare @StateObject var viewModel = Cart() and what about MainView and ProductDetail? How I should declare viewModel in these views?
I mean, in MainView you should declare your one and only instance of Cart. Then, pass this instance (that in MainView you called viewModel) to all other views using .environmentObject(viewModel). If you pass it to one of the views, all child views will automatically receive the same environment object, so the array in the Cart object will always be the same for all views. In ProductDetail and ShoppingView you maintain @EnvironmentObject var viewModel: Cart.
Thank you! Finally understand it.
@HunterLion Where is the source that a Cart instance is created every time the view is called? I disagree. .environmentObject(Cart()) works exactly like @StateObject where only the initial view owns the view model, and @EnvironmentObject works like @ObservedObject. The benefit of @EnvironmentObject is that you don't need to hand over the object through the entire view hierarchy.
@vadian: you are correct, as long as you pass .environmentObject(Cart()) only once in your code since the beginning. If instead you use it in a view, changing the state of that view might create a new instance in the environment - not in every case, but it depends on how your code works. I just proposed an approach which I deem safer to use everywhere. But your solution can work.
1

When you are going to inject the view model you have to create an instance

Replace

@EnvironmentObject var viewModel: Cart
ShoppingView().environmentObject(viewModel)

with

ShoppingView().environmentObject(Cart())

The @EnvironmentObject property wrapper is only necessary if you inherit the object.

8 Comments

Replaced. Nothing changed :( You think array is empty due to this.
Where do you call addToCart?
In ProductDetailView, @ObservedObject var cartViewModel = Cart() cartViewModel.addToCart(productId: product.id, productQty: self.productCount)
There Cart() is a different instance. Use the instance in the environment. And you have to use @StateObject if you own the object.
Yes, exactly, but ProductDetail must be a descendant of MainView
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.