TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Nil value where no nil value should be somehow

Forums > SwiftUI

I tried to adapt the navigation solution from the post "SOLVED: NavigationLink back button edit?" on May 23' such that i can pass data to the respective screens. I am know facing the problems that this works for one Screen, but not for the other, even though i am essentially doing the same thing.

In the following, I want to go from the actual Game screen to an End screen. For simplicity and testing, I used a button to do so:

Button("Go to GameEnd View") {
    navigation.path.append(Screens.GameEnd)
    print(navigation.path)
}
.navigationDestination(for: Screens.self) { screen in
    NavigationController.navigate(to: screen, test: Player(name: "this is a test"))
}

Here is the code of the navigate function:

class NavigationController {
    @ViewBuilder
    static func navigate(to screen: Screens, gameData: GameData? = nil, test: Player? = nil) -> some View {
        switch screen {
        case .Game:
            GameView(points: gameData!.selectedPoints, sets: gameData!.selectedSets, legs: gameData!.selectedLegs, checkIn: gameData!.selectedCheckIn, checkOut: gameData!.selectedCheckOut, players: gameData!.createDummyPlayer(amount: 2))
        case .GameEnd:
            GameEndView(winner: test ?? Player(name: "abc"))
        case .screenC:
            StartView()
        }
    }
}

When Pressing the Button, it only passing the default value (Player) since 'test' is apparently nil, which i don't understand since pass it as parameter directly.

Despite doing the same when navigating to the Games Screen 'GameView', it does not complain because of any nil values or whatsoever.

This is the rough structure of the destination.

struct GameEndView: View {
    @EnvironmentObject var navigation: Navigation
    @State var winner: Player

    var body: some View {
      ...
    }
}

Thanks in advance.

   

What does your Player struct/class look like? Specifically, what does its init look like?

   

Consider using a guard let in your static function:

static func navigate(to screen: Screens, gameData: GameData? = nil, test: Player? = nil) -> some View {
    //           👇🏼 Debugging helper.....
    guard let gameData { else print("STATIC FUNC NAVIGATE FAIL: gameData is NIL"); return }
    switch screen {
        case .Game:
            GameView(points: gameData!.selectedPoints, sets: gameData!.selectedSets, legs: gameData!.selectedLegs, checkIn: gameData!.selectedCheckIn, checkOut: gameData!.selectedCheckOut, players: gameData!.createDummyPlayer(amount: 2))
        case .GameEnd:
            GameEndView(winner: test ?? Player(name: "abc"))
        case .screenC:
            StartView()
        }
    }

   

@roosterboy this is how the struct looks like:

struct Player: Hashable {
    var name: String
    var points = 0

    var legs = 0
    var sets = 0
    var avg = 0.0
}

I ve also just found something really interesing...

Button("Go to GameEnd View") {
    navigation.path.append(Screens.GameEnd)
}
//.navigationDestination(for: Screens.self) { screen in
 //   NavigationController.navigate(to: screen, test: Player(name: "this is a test"))
//}

When pressing the button the View somehow still changes even though the navigate function is never being called. But this somewhat explains why it complains because of Nil value in that case.

   

I think @Obelix is on to the cause, actually. Your navigate function has the gameData parameter, which has a defauly value of nil if you don't pass something into it. And in this line:

NavigationController.navigate(to: screen, test: Player(name: "this is a test"))

you aren't passing anything for gameData, so it's value is nil.

Yet, you are force unwrapping gameData, so if it's nil you get a crash.

You need to handle the event that gameData is nil, using a guard or some other default value or something. Force unwrapping something that could be nil without protecting yourself from crashing is always a bad idea.

   

I do not think gameData has anything to do with that. I now provided default values and besides that, the first transition (StartView to GameView) works just fine. When leaving out the default value for GameEndView's Player parameter, the error for the crrash explicitly states: "... crashed due to implicitly unwrapped optional in Navigation.swift at line 38, which is the .GameEnd case.

class NavigationController {
    @ViewBuilder
    static func navigate(to screen: Screens, gameData: GameData? = nil, test: Player? = nil) -> some View {
        switch screen {
        case .Game:
            let points = gameData?.selectedPoints ?? 301
            let sets = gameData?.selectedSets ?? 1
            let legs = gameData?.selectedLegs ?? 1
            let checkIn = gameData?.selectedCheckIn ?? "Single In"
            let checkOut = gameData?.selectedCheckOut ?? "Double Out"
            let players = gameData?.createDummyPlayer(amount: 2) ?? [Player(name: "default")]

            GameView(points: points, sets: sets, legs: legs, checkIn: checkIn, checkOut: checkOut, players: players)
        case .GameEnd:
            GameEndView(winner: test!)
        case .screenC:
            StartView()
        }
    }
}

Could it be possible that the issue is linked to my previous post? Becasue I do not understand how the navigation is even possible when leaving out the navigationDestination, which would also explain why the somehow never called navigation function receives a nil value. Or am I wrong?

Button("Go to GameEnd View") {
    navigation.path.append(Screens.GameEnd)
}
//.navigationDestination(for: Screens.self) { screen in
 //   NavigationController.navigate(to: screen, test: Player(name: "this is a test"))
//}

   

Well I now got my programm to behave like I want it to, but by using another approach for Navigation. I guess we'll never know what the true problem was xD. But still thanks for helping :D

   

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

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.