WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

ObservableObject not updating view in nested loop

Forums > SwiftUI

Hello,

Regarding the following project :

You have an amountSum of 100 When you click on one user "plus" button, this specific user have to pay this amount but if you click on multiple user "plus" button, the amount to pay is divided between them equally.

Any idea how I can update the entire Model2.MustPayM2 prop when I click on the "plus" button please ?

import SwiftUI

struct Model1: Identifiable, Codable {
    var id: String = UUID().uuidString
    var nameM1: String
    var amountM1: Double
    var amountSumM1: Double = 100
    var arrayM2: [Model2]
    var isVisible: Bool = false
}

struct Model2: Identifiable, Codable {
    var id: String = UUID().uuidString
    var nameM2: String
    var amountM2: Double = 0
    var mustPayM2: Bool = false
}

class ViewModel1: ObservableObject {

    @Published var Publi1: Model1
    @Published var Publi1s: [Model1] = []
    @Published var Publi2: Model2
    @Published var Publi2s: [Model2] = []

    init() {
        let pub2 = Model2(nameM2: "init")
        let pub1 = Model1(nameM1: "init", amountM1: 0, arrayM2: [pub2])

        self.Publi2 = pub2
        self.Publi1 = pub1

        var newPub1s: [Model1] = []
        for i in (0..<5) {
            let newNameM1 = "name\(i+1)"
            let newAmountM1 = Double(i+1)
            var newModel1 = Model1(nameM1: newNameM1, amountM1: newAmountM1, arrayM2: [pub2])
            var newPub2s: [Model2] = []
            for i in (0..<5) {
                let newNameM2 = "\(newNameM1)-user\(i+1)"
                let newModel2 = Model2(nameM2: newNameM2)
                newPub2s.append(newModel2)
            }
            newModel1.arrayM2 = newPub2s
            newPub1s.append(newModel1)
        }

        Publi1s = newPub1s
        Publi1 = newPub1s[0]
        Publi2s = newPub1s[0].arrayM2
        Publi2 = newPub1s[0].arrayM2[0]
    }

}

struct View1: View {

    @EnvironmentObject var VM1: ViewModel1

    @State private var tt: String = ""

    private let screenHeight = UIScreen.main.bounds.height

    var body: some View {
        ZStack {
            VStack {
                ForEach(0..<VM1.Publi2s.count, id: \.self) { i in

                    Text("\(VM1.Publi2s[i].nameM2)")

                    Text(tt)

                    Button {
                        VM1.Publi2s[i].mustPayM2.toggle()
                        var a = VM1.Publi2s.filter { $0.mustPayM2 == true }
                        let b = VM1.Publi1.amountM1 / Double(a.count)

                        // How can I update the new props between all users ??
                        // for j in 0..<a.count {
                        //     a[j].amountM2 = b
                        // }

                    } label: {
                        Image(systemName: "plus")

                    }
                }

                Spacer()

                Button {
                    VM1.Publi1.isVisible.toggle()
                } label: {
                    Text("SHOW ME")

                }

                Spacer()

            }

            View2()
                .offset(y: VM1.Publi1.isVisible ? 0 : screenHeight)
        }
    }
}

struct View2: View {

    @EnvironmentObject var VM1: ViewModel1

    var body: some View {
        VStack {
            Spacer()
            ForEach(0..<VM1.Publi2s.count, id: \.self) { i in

                Text("\(VM1.Publi2s[i].amountM2)")

            }
        }
    }
}

struct View2_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            View1()
        }
        .environmentObject(ViewModel1())
    }
}

   

Mandri joins and asks a tough question on the SAME day!

Any idea how I can update the entire Model2.MustPayM2 prop when I click on the "plus" button please ?

Hello Mandri: Are you following the 100 Days of SwiftUI course? Please let us know what day you are on! We can point you to the right lesson to answer your question.

Or maybe you're just here to ask a design question?

Your question above is a business rule! Ask yourself:

Mandri? Which object in my application is best suited to executing this business rule?

Please consider the answer probably should never be, "a view".

Views take data from a model and project that data somehow onto the screen.

I think the best place to execute your business rule in in the ViewModel1 class. By the way, please pick better names! Use class names that actually describe what your class is doing!

Consider writing a function called catHair in your ViewModel1 class. This function should execute when you've pressed the plus button in your View1 view. I am kidding about naming the function catHair, but perhaps you see the point. Please think of better names for your views and functions to describe their specific intent.

When you click the plus button it should execute a function within your viewModel, you've named VM1. The code might look something like this:

Button {
           VM1.doSomethingAmazing() // <-- This is what your button should do. Put this in your view model
           } label: {
                 Image(systemName: "plus")
           }

Inside your class, you'll have a function named doSomethingAmazing().

Start there.

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.