4

I have a SwiftData model Player and there should only ever be 0 or 1 in the database. When the user is on the Main Menu and taps to Start a New Game, I walk them through a couple of screens to pick the options for the game. Once all of the options are chosen, I delete all existing Player objects and insert a new one. Then I dismiss all of those options picking screens.

Then what I want to happen is to automatically show the game view. But I'm not sure how to bind my .fullScreenCover() to the Player.isShown property on the first element in my SwiftData @Query.

The code below doesn't work because: Cannot convert value of type 'Bool?' to expected argument type 'Binding<Bool>'

I'm still very new to Swift and SwiftUI, so my grasp on bindables and observables is still tenuous.

struct RootContentView: View {
    @Query var activeplayers: [Player] // should only ever be 0 or 1

    var body: some View {
        ZStack {
            // stuff            
        }
        .fullScreenCover(isPresented: activeplayers.first?.isshown content: {
                GameView()
        })
        
    }
    
}

How can I automatically show my GameView screen based on the optional first element in my SwiftData @Query?


I also tried just querying to see if any players exist with .isShown == true and then binding to $activeplayers.isEmpty (this is the reverse of the logic I actually want, but I was just trying to find SOME way to distill the data down to a single bool property and I figured I could work on reversing the logic later) but that didn't work either:

@Query (filter: #Predicate<Player> {
        $0.isshown == true
    }) var activeplayers: [Player]

// ...

// without the $, gives the error: 
// Cannot convert value of type 'Bool' to expected argument type 'Binding<Bool>'
.fullScreenCover(isPresented: activeplayers.isEmpty content: {
        GameView()
})

// with the $, gives the error:
// Cannot find '$activeplayers' in scope
.fullScreenCover(isPresented: $activeplayers.isEmpty content: {
        GameView()
})

1 Answer 1

3

You could try this simple approach, using an extra @State var shouldShow and setting it in .onAppear {...} as shown in the example code

struct RootContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query var activeplayers: [Player]
    
    @State var shouldShow = false // <-- here
    
    var body: some View {
        VStack {
            Text("testing")
        }
        .fullScreenCover(isPresented: $shouldShow) {  // <-- here
            GameView()
        }
        // -- here
        .onAppear {
            if let player = activeplayers.first, let doShow = player.isshown {
                shouldShow = doShow
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Ah! Neat. So much to learn about SwiftUI. This seems like a good solution in general. In my specific case, it didn't work out because onAppear doesn't get called when the subsequent screens are dismissed (because of the pageSheet presentation). But the sheet() action allows me to add a dismiss handler and then I can put your suggested solution into that. My code above shows fullScreenCover() instead of sheet(), so I accepted your solution. The original code design was using sheet() but I had experimented with fullScreenCover() to see if I could get things working and forgot to change above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.