SwiftUI Code History cannot use NavigationLink as deprecated

I am on part 6 of the Code History project and I am disappointed to see that I cannot use iOS16 to complete the project. I get the following error using the NavigationLink:
.background(
NavigationLink(destination: Text(“Game Over!”),
isActive: .constant(viewModel.gameIsOver),
label: { EmptyView() })
)

‘init(destination:isActive:label:)’ was deprecated in iOS 16.0: use NavigationLink(value:label:) inside a NavigationStack or NavigationSplitView

I don’t want to use a lower version of SwiftUi in order to complete the project and ideally would like to know how to implement so I can use in future versions of SwiftUI.

I have tried to implement using:
.navigationDestination(isPresented: $viewModel.gameIsOver) {
Text(“Game Over!”)
}

but I get the following error which I cannot seem to resolve:
Cannot assign to property: ‘gameIsOver’ is a get-only property

Has anyone managed to get the project to work in iOS16.

Thanks

Colin

Just FYI, it’s not an error, it’s an advisory. The code will still build and the app runs in my emulator.

Hi Colin!
I run into the same problem. Did you by any chance find how to fix it for newer versions?
Sincerely, Lia

Hi,

These are the changes that I made to get it working:

.navigationDestination(isPresented: $viewModel.isOver, destination: {
Text(“Game over”)
})

Need to also make these changes in GameViewModel

var isOver: Bool = false

update func displayNextScreen as we need to set Bool value

func displayNextScreen() {
    game.updateGameStatus()
    if game.isOver {
        isOver = true
    }
}

// GameViewModel file full code

import SwiftUI

class GameViewModel: ObservableObject {

@Published private var game = Game()

var currentQuestion: Question {
    game.currentQuestion
}

var guessWasMade: Bool {
    if let _ = game.guesses[currentQuestion] {
        return true
    } else {
        return false
    }
}

var isOver: Bool = false

var gameIsOver: Bool {
game.isOver
}

var questionProgressText: String {
    "\(game.currentQuestionIndex + 1) / \(game.numberOfQuestions)"
}

var correctGuesses: Int {
    game.guessCount.correct
}

var incorrectGuesses: Int {
    game.guessCount.incorrect
}

func makeGuess(atIndex index: Int) {
    game.makeGuessForCurrentQuestion(atIndex: index)
}

func displayNextScreen() {
    game.updateGameStatus()
    if game.isOver {
        isOver = true
    }
}

func color(forOptionIndex optionIndex: Int) -> Color {
    if let guessedIndex = game.guesses[currentQuestion] {
        if guessedIndex != optionIndex {
            return GameColor.main
        } else if guessedIndex == currentQuestion.correctAnswerIndex {
            return GameColor.correctGuess
        } else {
            return GameColor.incorrectGuess
        }
    } else {
        return GameColor.main
    }
}

}

// gameView file full code

import SwiftUI

struct GameView: View {

@StateObject var viewModel = GameViewModel()


var body: some View {
    NavigationStack {
        ZStack {
            GameColor.main.ignoresSafeArea()
            VStack {
                Text(viewModel.questionProgressText)
                    .font(.callout)
                    .multilineTextAlignment(.leading)
                    .padding()
                QuestionView(question: viewModel.currentQuestion)
            }
            .foregroundColor(.white)
            .navigationBarHidden(true)
            .environmentObject(viewModel)
        }
        .navigationDestination(isPresented: $viewModel.isOver) {
            ScoreView(viewModel: ScoreViewModel(correctGuesses: 8, incorrectGuesses: 2))
        }
    }

}

}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
GameView()
}
}

I figured a way around the IOS 16 problem. This is my code:

// GameViewModel.swift
// Code History
//
// Created by Raj on 6/23/23.
//

import Foundation
import SwiftUI

class GameViewModel: ObservableObject{
@Published private var game = Game()

var currentQuestion: Question{
    game.currentQuestion
}

var correctness: Double{
    return Double(game.guessCount.correct/game.numberOfQuestions)
}

var questionProgressText: String{
    "\(game.currentQuestionIndex + 1) / \(game.numberOfQuestions)"
}

var correctGuesses: Int{
    game.guessCount.correct
}

var incorrectGuesses: Int{
    game.guessCount.incorrect
}

var guessWasMade: Bool{
    if let _ = game.guesses[currentQuestion]{
        return true
    }
    else{
        return false
    }
}

var isOver: Bool = false

var gameIsOver: Bool{
    game.isOver
}

func makeGuess(atIndex index: Int){
    game.makeGuessForCurrentQuestion(atIndex: index)
}

func displayNextScreen(){
    game.updateGameStatus()
    if game.isOver{
        isOver = true
    }
}

func color(forOptionIndex optionIndex: Int) -> Color{
    if let guessedIndex = game.guesses[currentQuestion]{
        if guessedIndex != optionIndex{//checks if the button was picked or hte user chose another button, so the button background would stay as main color if another button was picked
            return GameColor.mainColor
        }
        else if guessedIndex == currentQuestion.correctAnswerIndex{
            return GameColor.correctGuess
        }
        else{
            return GameColor.incorrectGuess
        }
    }
    else{
        return GameColor.mainColor
    }
}

}

//
// Game.swift
// Code History
//
// Created by Raj on 6/19/23.
//

import Foundation

struct Game{
private(set) var currentQuestionIndex = 0
private(set) var guesses = Question: Int
private(set) var isOver = false
private let questions = Question.allQuestions.shuffled()

var guessCount: (correct: Int, incorrect: Int){
    var count: (correct: Int, incorrect: Int) = (0, 0)
    for (question, guessedIndex) in guesses{
        if (question.correctAnswerIndex == guessedIndex){
            count.correct += 1
        }
        else{
            count.incorrect += 1
        }
    }
    return count
}

var numberOfQuestions: Int{
    return questions.count
}

var currentQuestion: Question{
    questions[currentQuestionIndex]
}

mutating func makeGuessForCurrentQuestion(atIndex index: Int){
    guesses[currentQuestion] = index
}

mutating func updateGameStatus(){
    if currentQuestionIndex < (questions.count - 1){
        currentQuestionIndex += 1
    }
    else{
        isOver = true
    }
    
}

}

//
// QuestionView.swift
// Code History
//
// Created by Raj on 6/23/23.
//

import SwiftUI

struct QuestionView: View {

@EnvironmentObject var viewModel: GameViewModel
let question: Question

var body: some View {
    VStack{
        Text(question.questionText).font(.largeTitle).bold().multilineTextAlignment(.leading).padding()
        Spacer()
        HStack{
            ForEach(0..<question.possibleAnswers.count){answerIndex in
                Button(action: {
                    print("Tapped \(question.possibleAnswers[answerIndex])")
                    viewModel.makeGuess(atIndex: answerIndex)
                }, label: {
                    ChoiceTextView(choiceText: question.possibleAnswers[answerIndex]).background(viewModel.color(forOptionIndex: answerIndex))
                })
                .disabled(viewModel.guessWasMade)
            }
        }
        if viewModel.guessWasMade{
            if viewModel.isOver{
                NavigationLink(destination: ScoreView(viewModel: ScoreViewModel(correctGuesses: viewModel.correctGuesses, incorrectGuesses: viewModel.incorrectGuesses)), label: {
                    BottomTextView(str: "Next")
                })
            }
            else{
                Button(action: {
                    viewModel.displayNextScreen()
                }, label: {
                    BottomTextView(str: "Next")
                })
            }
        }
    }
}

}

struct QuestionView_Previews: PreviewProvider {
static var previews: some View {
QuestionView(question: Game().currentQuestion)
}
}

//
// ContentView.swift
// Code History
//
// Created by Raj on 6/11/23.
//

import SwiftUI

struct GameView: View {
//makes question constant from question model
let question = Question(
questionText: “What was the first computer bug?”,
possibleAnswers: [“Ant”, “Beetle”, “Moth”, “Fly”],
correctAnswerIndex: 2)

//@State allows mainColor to change
//@StateObject allows changes of viewModel and updates it to the game view model
@StateObject var viewModel = GameViewModel()
var body: some View {
    ZStack{
        GameColor.mainColor.ignoresSafeArea() //expands game to farthest point of screen
        VStack{
            Text(viewModel.questionProgressText).font(.callout).multilineTextAlignment(.leading).padding()
            QuestionView(question: viewModel.currentQuestion)
        }.foregroundColor(.white)
            .navigationBarHidden(true)
            .environmentObject(viewModel)
    }//.navigationDestination(isPresented: $viewModel.isOver){
        //ScoreView(viewModel: ScoreViewModel(correctGuesses: viewModel.correctGuesses, incorrectGuesses: viewModel.incorrectGuesses))
    //}
}

}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
GameView()
}
}

This is similar to cfmcarthur because I used his ideas as a starting point but his code wouldn’t work for me so I made these sligtht adjustments. Hopefully this helps!

1 Like

Actually you can use navigation links in iOS 16 with navigationStack, by choosing this one :


this version of the navigation Link is not deprecated and can be used in iOS 16.

this is for you @cfmcarthur