NavigationPathのRestoreを行うとクラッシュする場合があります。
iOSおよびXcodeの更新を待ちましょう。
確認したバージョンはOSはiOS16.0のシミュレータで、
Xcodeは14.0.0と14.0.1です。
追記
Xcode14.1 beta2とiOS16.1 betaの組み合わせでは動作しました。
Xcode14.1 beta2とiOS16.0ではダメだったのでiOS側のアップデートが必要なようです。
不具合の出るコードについて
//NavigationPath(NavigationPath.CodableRepresentation)
NavigationPath(representation)
要はイニシャライザに復元用データを渡すと不具合が出ます。
以下のサンプルコードにNavigationStackとデータの保存部分を追加して実行してください。
データ保存はUserDefaultsとかで良いです。
私が使ったテストコードは記事の最下部に置いておきます。
面倒な方がこちらをコピペして使ってください。
画面遷移後にアプリを終了させ、再度起動したら遷移後の画面からルートに戻してください。
以下のエラーが出てクラッシュします。
SwiftUI/NavigationPath.swift:214: Fatal error: attempting to remove 1 items from path with 1 items
意味としては「1つのアイテムを持つパスから1つのアイテムを削除しようとしています。」
ルートの場合pathのcountは0なので間違った事は何もしていません。
イニシャライザ内の処理の問題なので手の付けようがありません。
修正を待ちましょう。
対処法
現状はどうしようもないのでNavigationPath外でデータを保存しましょう。
単純に別途保存して起動時にNavigationPathにappendすれば問題ないです。
余談
原因について
恐らくJSONと変換する時に問題が起きています。
NavigationPath.codableからNavigationPathを初期化しても問題が起きないからです。
JSONへのEncodeまたはDecodeの何処かにミスがあるのではないかと思います。
Xcodeのバージョンについて
どうやらXcode14 beta4まででも発生しており、フォーラムにもこの問題がありました。
beta5で解決したとのコメントがありましたが、最近また発生したとのコメントもありました。
どうやらデグレしたようですね・・・
テストコード
import SwiftUI
struct NavigationStackView: View {
@Environment(\.scenePhase) var scenePhase
@StateObject private var pathState = MyModelObject()
var body: some View {
VStack {
NavigationStack(path:$pathState.path){
IntListView()
.navigationDestination(for: Int.self) { item in
StringListView()
}
.navigationDestination(for: String.self) { item in
DetailView()
}
}.onChange(of: scenePhase) { phase in
if phase == .background {
pathState.save()
}
}
Text("\(pathState.path.count)")
}
}
}
class MyModelObject: ObservableObject {
@Published var path: NavigationPath
static func readSerializedData() -> Data? {
// Read data representing the path from app's persistent storage.
let data = UserDefaults.standard.data(forKey: "path")
if data != nil {
print(String(data: data!, encoding: .utf8))
}
return data
}
static func writeSerializedData(_ data: Data) {
// Write data representing the path to app's persistent storage.
UserDefaults.standard.set(data, forKey: "path")
print(String(data: data, encoding: .utf8))
}
init() {
if let data = Self.readSerializedData() {
do {
let representation = try JSONDecoder().decode(
NavigationPath.CodableRepresentation.self,
from: data)
self.path = NavigationPath(representation)
} catch {
self.path = NavigationPath()
}
} else {
self.path = NavigationPath()
}
}
func save() {
guard let representation = path.codable else { return }
do {
let encoder = JSONEncoder()
let data = try encoder.encode(representation)
Self.writeSerializedData(data)
} catch {
// Handle error.
}
}
}
struct IntListView: View {
let items = 1..<4
var body: some View {
List(items ,id:\.self) { item in
NavigationLink("\(item)", value: item)
}.navigationTitle("IntList")
}
}
struct StringListView: View {
let items = ["Item1","Item2","Item3"]
var body: some View {
List(items, id:\.self) { item in
NavigationLink("\(item)", value: item)
}.navigationTitle("StringList")
}
}
コメント
[…] […]