3

I've created some SwiftUI code that use an EnvironmentObject to store boolean values for popping back to the root view and another EnvironmentObject to store a score variable that is intended to be used across multiple views.

In this example, I have two games, a Red Game and a Blue Game. I have booleans (.redStacked and .blueStacked) that are initially set to false. Their respective NavigationLinks set them to true. At the end of the game, a "Home" button sets it back to false, and this unwinds the navigation stack back to the root view.

The problem that I am running into is that any update to the score EnvironmentObject unexpectedly and prematurely pops the navigation stack back to the root view.

In the following example code, the only difference between games is that the Red Game button adds +1 to its score environmental variable. In this instance, the one point is added and the navigation link to the final page is executed, but then it immediately rubberbands back to the start. The Blue Game does not update the score environmental variable and transitions as expected.

I welcome any insights on why this occurs. Thanks in advance.

import SwiftUI

class Pop: ObservableObject {
    @Published var redStack = false
    @Published var blueStack = false
}
class Score: ObservableObject {
    @Published var redTotal = 0
    @Published var blueTotal = 0
}

struct GameSelection: View {
    @ObservedObject var pop = Pop()
    @ObservedObject var score = Score()
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: RedStart(), isActive: $pop.redStack) {
                    Text("Play Red Game") //Tapping this link sets redStacked to true           
                }.foregroundColor(.red)
                
                Divider()
                
                NavigationLink(destination: BlueStart(), isActive: $pop.blueStack) {
                    Text("Play Blue Game") //Tapping this link sets blueSteacked to true
                }.foregroundColor(.blue)
            }
        }
        .environmentObject(score)
        .environmentObject(pop)
    }
}

struct RedStart: View {
    @State var goToNextView : Bool = false
    @EnvironmentObject var score : Score
    var body: some View {
        VStack {
            NavigationLink(destination: RedEnd(), isActive: $goToNextView) {}
            Button(action: {
                score.redTotal += 1
                goToNextView = true
            }, label: {
                Text("Add 1 Point. Move to Next Screen")
                    .foregroundColor(.red)
            })
        }
    }
}

struct BlueStart: View {
    @State var goToNextView : Bool = false
    @EnvironmentObject var score : Score
    var body: some View {
        VStack {
            NavigationLink(destination: BlueEnd(), isActive: $goToNextView) {}
            Button(action: {
                //          score.blueTotal += 1
                goToNextView = true
            }, label: {
                Text("Do NOT add points.  Move to Next Screen")
                    .foregroundColor(.blue)
            })
        }
    }
}

struct RedEnd: View {
    @EnvironmentObject var pop: Pop
    @EnvironmentObject var score: Score
    var body: some View {
        
        Button(action: {
            pop.redStack = false
        }, label: {
            VStack {
                Text("Your Score: \(score.redTotal)")
                Text("Game Over")
                Image(systemName: "house.fill")
            }
            .foregroundColor(.red)
            
        })
    }
}

struct BlueEnd: View {
    @EnvironmentObject var pop: Pop
    @EnvironmentObject var score: Score
    var body: some View {
        
        Button(action: {
            pop.blueStack = false
        }, label: {
            VStack {
                Text("Your Score: \(score.blueTotal)")
                Text("Game Over")
                Image(systemName: "house.fill")
            }.foregroundColor(.blue)
        })
    }
}

struct GameSelection_Previews: PreviewProvider {
    static var previews: some View {
        GameSelection()
    }
}

Swifter
  • 67
  • 6
  • Change `@ObservedObject` to `@StateObject` – lorem ipsum Jun 09 '21 at 22:40
  • @loremipsum No dice. The RedGame still rubber-bands when the score is updated. Something about updating an independent environment variable seems to trick swiftui into thinking the boolean is suddenly false... – Swifter Jun 09 '21 at 23:07
  • It is interesting to see because they talk about this in the "Demystifying SwiftUI" video that came out today and it says that it shouldn't do this. And, BTW I am in MacOS Monterrey and Xcode 13 and it works as expected no popping back. I don't have Xcode 12.5 installed on this device to test it there. – lorem ipsum Jun 09 '21 at 23:23
  • I'm on 12.5 currently. Thought it might be best to finish this current app before upgrading, but if others can confirm that the behavior is unique to 12.5 (or below), I'd be willing to try updating... – Swifter Jun 09 '21 at 23:27
  • yes on MacOS Monterrey using Xcode 13 it works as expected, but not on ios 14.7. Think twice before upgrading to macos 12. I find xcode 13 nice but very slow. – workingdog support Ukraine Jun 09 '21 at 23:31
  • I just moved it over to 12.5 and see the "popping". I also caution with trying Monterrey and Xcode 13, use an external hard drive or a secondary device. I found another issue in Xcode and CoreData with Transformable it is still very buggy. – lorem ipsum Jun 09 '21 at 23:42
  • Thank you both for the confirmations. I wonder if there is another way to code this then that might work in 12.5 for now? I don't quite get what causes the rubber-banding in 12.5... – Swifter Jun 10 '21 at 00:12
  • A couple of additional tidbits: I don't recall seeing this behavior in 12.4. Using an if statement, I can confirm that a change to score also forces redStack from true to false. However, this only occurs when there are two NavigationLinks in the NavigationView. If I comment out the NavigationLink for the Blue Game, then changes to score do not affect the boolean (shrug). – Swifter Jun 10 '21 at 12:07
  • Confirmed that this behavior does not occur in 12.4. It is unique to 12.5. Is there an etiquette for noting whether this constitutes an answer? – Swifter Jun 12 '21 at 16:13

0 Answers0