From 4ddab24623661479e39007b6ba95d54c01a42f00 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Fri, 1 Dec 2023 16:14:37 +0100 Subject: [PATCH] :alembic: (Navigation): Add programmatic navigation from HomeView --- Examples/iOSApp/Sources/Array+SafeIndex.swift | 18 +++++ .../iOSApp/Sources/CurriculumListView.swift | 2 +- Examples/iOSApp/Sources/DataModels.swift | 11 ++- Examples/iOSApp/Sources/HomeView.swift | 51 +++++++++++- Examples/iOSApp/Sources/Navigation.swift | 77 +++++++++++++++++++ 5 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 Examples/iOSApp/Sources/Array+SafeIndex.swift diff --git a/Examples/iOSApp/Sources/Array+SafeIndex.swift b/Examples/iOSApp/Sources/Array+SafeIndex.swift new file mode 100644 index 0000000000..3720b5249e --- /dev/null +++ b/Examples/iOSApp/Sources/Array+SafeIndex.swift @@ -0,0 +1,18 @@ +// Leka - iOS Monorepo +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import Foundation + +// TODO(@ladislas): move to UtilsKit +extension Array { + + subscript(safe index: Int) -> Element? { + guard indices.contains(index) else { + return nil + } + + return self[index] + } + +} diff --git a/Examples/iOSApp/Sources/CurriculumListView.swift b/Examples/iOSApp/Sources/CurriculumListView.swift index 60e823f3e8..8e1b3a2844 100644 --- a/Examples/iOSApp/Sources/CurriculumListView.swift +++ b/Examples/iOSApp/Sources/CurriculumListView.swift @@ -42,7 +42,7 @@ struct CurriculumInfoView: View { } Section("List of activities") { - ForEach(Activity.all.filter { curriculum.activities.contains($0.id) }) { activity in + ForEach(curriculum.activities) { activity in NavigationLink(value: activity) { Text(activity.name) } diff --git a/Examples/iOSApp/Sources/DataModels.swift b/Examples/iOSApp/Sources/DataModels.swift index 137e1348f2..e747256856 100644 --- a/Examples/iOSApp/Sources/DataModels.swift +++ b/Examples/iOSApp/Sources/DataModels.swift @@ -26,9 +26,18 @@ struct Activity: Identifiable, Hashable { struct Curriculum: Identifiable, Hashable { let id: String let name: String - let activities: [String] + let activities: [Activity] let similarCurriculums: [String]? + init(id: String, name: String, activities: [String], similarCurriculums: [String]?) { + self.id = id + self.name = name + self.activities = activities.compactMap { activityId in + Activity.all.first { $0.id == activityId } + } + self.similarCurriculums = similarCurriculums + } + static let all: [Curriculum] = [ Curriculum( id: "curriculum_1", name: "Curriculum 1", activities: ["activity_1", "activity_2", "activity_3"], diff --git a/Examples/iOSApp/Sources/HomeView.swift b/Examples/iOSApp/Sources/HomeView.swift index 117dce9fb7..622f346399 100644 --- a/Examples/iOSApp/Sources/HomeView.swift +++ b/Examples/iOSApp/Sources/HomeView.swift @@ -13,6 +13,53 @@ struct HomeView: View { Text( /*@START_MENU_TOKEN@*/"Hello, World!" /*@END_MENU_TOKEN@*/) .font(.largeTitle) + Button("Go to Activities / Activity 1") { + navigation.set(path: Activity.all[0], for: .activities) + } + .buttonStyle(.borderedProminent) + .tint(.pink) + + Button("Go to Activities / Activity 1 / Activity 2 / Activity 3") { + navigation.set(path: Activity.all[0], Activity.all[1], Activity.all[2], for: .activities) + } + .buttonStyle(.borderedProminent) + .tint(.orange) + + Button("Go to Curriculums / Curriculum 1") { + navigation.set(path: Curriculum.all[0], for: .curriculums) + } + .buttonStyle(.borderedProminent) + .tint(.yellow) + + Button("Go to Curriculums / Curriculum 1 / Activity 2 (w/ objects)") { + navigation.set(path: Curriculum.all[0], Curriculum.all[0].activities[1], for: .curriculums) + } + .buttonStyle(.borderedProminent) + .tint(.purple) + + Button("Go to Curriculums / Curriculum 1 / Activity 2 (w/ id + index)") { + navigation.set(curriculum: "curriculum_1", activity: 1) + } + .buttonStyle(.borderedProminent) + .tint(.indigo) + + Button("Go to Curriculums / Curriculum 1 / Activity -1 (w/ id + index) NOT WORKING") { + navigation.set(curriculum: "curriculum_1", activity: -1) + } + .buttonStyle(.borderedProminent) + .tint(.red) + + Button("Go to Curriculums / Curriculum 1 / Activity 1 (w/ id + id)") { + navigation.set(curriculum: "curriculum_1", activity: "activity_1") + } + .buttonStyle(.borderedProminent) + .tint(.mint) + + Button("Go to Curriculums / Curriculum 1 / Activity 1 (w/ id + id) NOT WORKING") { + navigation.set(curriculum: "curriculum_1", activity: "bad_id") + } + .buttonStyle(.borderedProminent) + .tint(.red) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.cyan) @@ -21,5 +68,7 @@ struct HomeView: View { } #Preview { - HomeView() + NavigationStack { + HomeView() + } } diff --git a/Examples/iOSApp/Sources/Navigation.swift b/Examples/iOSApp/Sources/Navigation.swift index fe5d5e2774..3ddd67f923 100644 --- a/Examples/iOSApp/Sources/Navigation.swift +++ b/Examples/iOSApp/Sources/Navigation.swift @@ -30,6 +30,10 @@ class Navigation: ObservableObject { var selectedCategory: Category? = .home { willSet { disableUICompletly = true + guard !isProgrammaticNavigation else { + print("isProgrammaticNavigation is true, early return to avoid reseting path") + return // ? Note: early return to avoid reseting path + } backupPath(for: selectedCategory) } didSet { @@ -46,6 +50,8 @@ class Navigation: ObservableObject { } } + private var isProgrammaticNavigation: Bool = false + private var homeNavPathBackup: NavigationPath = NavigationPath() private var activitiesNavPathBackup: NavigationPath = NavigationPath() private var curriculumsNavPathBackup: NavigationPath = NavigationPath() @@ -110,4 +116,75 @@ class Navigation: ObservableObject { } } + func set(path newPath: AnyHashable..., for newCategory: Category) { + switch newCategory { + case .home: + break + case .activities: + activitiesNavPathBackup = newPath.compactMap { $0 as? Activity } + .reduce(into: NavigationPath()) { $0.append($1) } + case .curriculums: + var localPath = NavigationPath() + newPath.forEach { + if let activity = $0 as? Activity { + print("append activity: \(activity)") + localPath.append(activity) + } else if let curriculum = $0 as? Curriculum { + print("append curriculum: \(curriculum)") + localPath.append(curriculum) + } else { + print("append unknown: \($0)") + } + } + print("localPath: \(localPath)") + curriculumsNavPathBackup = localPath + print("bckupPath: \(curriculumsNavPathBackup)") + } + + underProgrammaticNavigation { + select(category: newCategory) + } + } + + func set(curriculum: String, activity index: Int) { + guard let curriculum = Curriculum.all.first(where: { $0.id == curriculum }) else { return } + guard let activity = curriculum.activities[safe: index] else { return } + + var localPath = NavigationPath() + + localPath.append(curriculum) + localPath.append(activity) + + curriculumsNavPathBackup = localPath + + underProgrammaticNavigation { + select(category: .curriculums) + } + } + + func set(curriculum: String, activity id: String) { + guard let curriculum = Curriculum.all.first(where: { $0.id == curriculum }) else { return } + guard let activity = curriculum.activities.first(where: { $0.id == id }) else { return } + + print("curri: \(curriculum)") + print("activ: \(activity)") + + var localPath = NavigationPath() + + localPath.append(curriculum) + localPath.append(activity) + + curriculumsNavPathBackup = localPath + + underProgrammaticNavigation { + select(category: .curriculums) + } + } + + private func underProgrammaticNavigation(_ callback: () -> Void) { + isProgrammaticNavigation = true + callback() + isProgrammaticNavigation = false + } + }