0

When I modify the score using the stepper on View1 it successfully updates the score on View1.ViewModel. However when I dismiss the sheet it does not update the score on the players section.

I know that you can set @Binding var on Player1 but how do I pass this down to the View1.ViewModel so that when it makes changes it updates the parent view?

ContentView

import SwiftUI

struct ContentView: View {

    @State var game: Game = Game(name: "Game #1")
    @State var isPresented: Bool = true
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("PLAYERS"), content: {
                    ForEach(game.players) { player in
                        HStack {
                            Text(player.name)
                            Spacer()
                            Text("\(player.score)")
                        }
                    }
                })
            }
            .navigationTitle(game.name)
        }
        .sheet(isPresented: $isPresented, onDismiss: {}, content: {
            TabView {
                ForEach(game.players) { player in
                    View1(player: player)
                }
            }
            .tabViewStyle(PageTabViewStyle())
        })
        .onAppear(perform: {
            
            for p in 1...3 {
                let player = Player(name: "Player #\(p)")
                self.game.players.append(player)
            }
            
        })
    }

}

struct View1: View {
    
    @ObservedObject var vm: View1.ViewModel
    
    init(player: Player) {
        vm = ViewModel(player: player)
    }
    
    var body: some View {
        VStack {
            Spacer()
            Text(vm.name)
            Divider()
            Group {
                Stepper("Earned Points: \(vm.earnedPoints)", value: $vm.earnedPoints, in: 0...50)
            }
            .padding(.horizontal)
            Spacer()
        }
    }
    
}

extension View1 {
    
    class ViewModel: ObservableObject {
        
        @Published private var player: Player
        @Published private var round: Round
        
        init(player: Player) {
            self.player = player
            self.round = player.rounds[0]
        }
        
        var name: String {
            return player.name
        }
        
        var earnedPoints: Int {
            get {return self.round.earnedPoints}
            set {self.round.earnedPoints = newValue}
        }
        
    }
    
}

Game

import SwiftUI

struct Game: Identifiable {
    
    init(name: String) {
        self.name = name
    }
    
    var id: UUID = UUID()
    
    var name:String
    var players: Array<Player> = Array<Player>()
    var isMastersEdition: Bool = false
    
}

Player

import SwiftUI

struct Player: Identifiable {
    
    init(name: String) {
        self.name = name
    }
    
    var id: UUID = UUID()
    
    var name:String
    var rounds: Array<Round> = [Round()]
    
    var score: Int {
        return self.rounds.map({$0.earnedPoints}).reduce(0, +)
    }
    
}

Round

import SwiftUI

struct Round: Identifiable {
    
    var id: UUID = UUID()
    
    var earnedPoints: Int = 0
    var completedPhase: Bool = false
    var selectedPhase: Int = 0
    
}
eleethesontai
  • 454
  • 5
  • 19
  • What part of your code makes a 2-way connection between `game` and the `ViewModel`? You provide an initial value to the `ViewModel`. – lorem ipsum Dec 01 '21 at 14:29
  • @loremipsum that is what I am trying to figure out. If I don't use the viewmodel I can send the player to view1 as Binding and it works. but when I use the viewModel which I am trying to do to adopt mvvm it does not work. – eleethesontai Dec 01 '21 at 14:34
  • That is what you need to implement. You have to somehow pass the `player` back to `game`. How to do it is up to you. `@Binding` is the simplest, the next one would be to have a single source of truth for you persistence. A `PersistenceManager` of some kind that keeps track of of the objects. – lorem ipsum Dec 01 '21 at 14:37
  • @loremipsum do you know of a good sample of this? if I used binding then I do not have the viewmodel which I want so that I can put all of the network requests inside of it and separate the concerns? – eleethesontai Dec 01 '21 at 14:43
  • How about the [Apple SwiftUI Tutorials?](https://developer.apple.com/tutorials/swiftui) This is pretty basic stuff in SwiftUI. – Yrb Dec 01 '21 at 14:45
  • I have read those and several tutorials on mvvm they all show simple examples. but when i try to adopt it to my project i get a myriad of issues which is why I came here for clarification. At first I tried a source of truth style by using a observableobject passed as a environment object and this had troubles passing the elements down using binding because of the nature of the structs being passed around. So I tried switching to a more mvvm style per the tutorials and now I am having the troubles that I posted here looking for clarification. – eleethesontai Dec 01 '21 at 14:50
  • There are so many ways to solve this that it is hard to provide a good source I would say look into [`@EnvironmentObject`](https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app) since you are dealing with a `struct`. The next step is to find questions that deal with the type of requests that you are doing or look for `Firebase` questions there are tons out there. Look to design patterns and how to implement. Truly understand MVVM. – lorem ipsum Dec 01 '21 at 14:50
  • Be more specific in your questions. If you have a specific issue focus on that, don't come here to have someone just give you a solution. From the comments you know what the issue was you just want someone to answer it for you and tell you what to do. Try solving your issue and come here for the kinks. – lorem ipsum Dec 01 '21 at 14:52
  • @loremipsum I am not trying to be rude, but truly trying to understand because I keep having this issue on this forum. I felt that I expressed the question adequately of what I am trying to do. So I am not sure how to be more specific lol. – eleethesontai Dec 01 '21 at 14:53
  • Show you `EnvironmentObject` attempt and express what didn't work. what exactly didn't work. People here have been doing this for a long time and can tell when people are just fishing for an answer. Any solution to put a band-aid on their problem. But if you truly want to learn that band-aid won't do anything for you. – lorem ipsum Dec 01 '21 at 14:56
  • @loremipsum I will try to put together a demo tonight of EnvironmentObject version. I am not fishing I have been playing with this since Friday lol. Thank you for your patience and help thus far. – eleethesontai Dec 01 '21 at 15:01
  • You can look at [this](https://stackoverflow.com/questions/70016175/foreach-not-properly-updating-with-dynamic-content-swiftui/70130763#70130763) it isn’t what you are looking for but it points out some of the obvious things to consider. – lorem ipsum Dec 01 '21 at 16:34

0 Answers0