From e1d7bd7e63454ee1cefe975585f4e0fe744a5139 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Mon, 21 Aug 2023 23:34:59 +0200
Subject: [PATCH 1/8] Adding Decide to the project

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 Food Truck.xcodeproj/project.pbxproj | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/Food Truck.xcodeproj/project.pbxproj b/Food Truck.xcodeproj/project.pbxproj
index c879589..8f7d230 100644
--- a/Food Truck.xcodeproj/project.pbxproj	
+++ b/Food Truck.xcodeproj/project.pbxproj	
@@ -7,6 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		220EFD2A2A93AF0E004488DE /* Decide in Frameworks */ = {isa = PBXBuildFile; productRef = 220EFD292A93AF0E004488DE /* Decide */; };
+		2273ABDE2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
+		2273ABDF2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
 		6178CE6E2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */; };
 		6178CE6F2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */; };
 		84ACCDC3282D671600756FB7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 84ACCDC1282D671600756FB7 /* Localizable.strings */; };
@@ -161,6 +164,7 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoodTruckState.swift; sourceTree = "<group>"; };
 		6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardNavigationHeader.swift; sourceTree = "<group>"; };
 		7AE906C103F323E6CEF38CA5 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
 		84ACCDC5282D671600756FB7 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -248,6 +252,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				220EFD2A2A93AF0E004488DE /* Decide in Frameworks */,
 				E0C37BDF28232B8B007B925B /* FoodTruckKit in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -399,6 +404,7 @@
 				E039E3522834AC4800508176 /* SalesHistoryView.swift */,
 				E0C37C0B2823339F007B925B /* TruckView.swift */,
 				E08D705F283C492C00014B89 /* SocialFeedContent.swift */,
+				2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */,
 			);
 			path = Truck;
 			sourceTree = "<group>";
@@ -505,6 +511,7 @@
 			name = "Food Truck";
 			packageProductDependencies = (
 				E0C37BDE28232B8B007B925B /* FoodTruckKit */,
+				220EFD292A93AF0E004488DE /* Decide */,
 			);
 			productName = "Food Truck";
 			productReference = E0C37BC12823189A007B925B /* Food Truck.app */;
@@ -559,6 +566,9 @@
 				ar,
 			);
 			mainGroup = E0C37BB82823189A007B925B;
+			packageReferences = (
+				220EFD282A93AF0E004488DE /* XCRemoteSwiftPackageReference "Decide" */,
+			);
 			productRefGroup = E0C37BC22823189A007B925B /* Products */;
 			projectDirPath = "";
 			projectRoot = "";
@@ -623,6 +633,7 @@
 				E0510748283C187300FCE3E6 /* CardNavigationHeaderLabelStyle.swift in Sources */,
 				E0510749283C187300FCE3E6 /* TruckDonutsCard.swift in Sources */,
 				E051074A283C187300FCE3E6 /* DonutGallery.swift in Sources */,
+				2273ABDF2A940FB8005EFE4F /* FoodTruckState.swift in Sources */,
 				E051074B283C187300FCE3E6 /* SocialFeedPlusSettings.swift in Sources */,
 				E0C4A536283EBD67007D5B83 /* FlowLayout.swift in Sources */,
 				E051074C283C187300FCE3E6 /* DetailColumn.swift in Sources */,
@@ -665,6 +676,7 @@
 				E0C37C102823339F007B925B /* SocialFeedView.swift in Sources */,
 				E0C37C112823339F007B925B /* OrderCompleteView.swift in Sources */,
 				6178CE6E2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */,
+				2273ABDE2A940FB8005EFE4F /* FoodTruckState.swift in Sources */,
 				E0C37BEF282331AA007B925B /* RecommendedParkingSpotCard.swift in Sources */,
 				E0C37C28282333C9007B925B /* StoreSupportView.swift in Sources */,
 				E0C37BFC28233374007B925B /* DonutEditor.swift in Sources */,
@@ -1167,7 +1179,23 @@
 		};
 /* End XCConfigurationList section */
 
+/* Begin XCRemoteSwiftPackageReference section */
+		220EFD282A93AF0E004488DE /* XCRemoteSwiftPackageReference "Decide" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/MaximBazarov/Decide";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 0.1.0;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
 /* Begin XCSwiftPackageProductDependency section */
+		220EFD292A93AF0E004488DE /* Decide */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 220EFD282A93AF0E004488DE /* XCRemoteSwiftPackageReference "Decide" */;
+			productName = Decide;
+		};
 		E0510733283C187300FCE3E6 /* FoodTruckKit */ = {
 			isa = XCSwiftPackageProductDependency;
 			productName = FoodTruckKit;

From 4b0a206169190f6d6f1189f62478413338ea0cc6 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Mon, 21 Aug 2023 23:35:22 +0200
Subject: [PATCH 2/8] Adding FoodTruckState

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/Truck/FoodTruckState.swift | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 App/Truck/FoodTruckState.swift

diff --git a/App/Truck/FoodTruckState.swift b/App/Truck/FoodTruckState.swift
new file mode 100644
index 0000000..a02e41f
--- /dev/null
+++ b/App/Truck/FoodTruckState.swift
@@ -0,0 +1,32 @@
+//
+//  FoodTruckState.swift
+//  Food Truck
+//
+//  Created by Anton Kolchunov on 21.08.23.
+//  Copyright © 2023 Apple. All rights reserved.
+//
+
+import Decide
+import FoodTruckKit
+
+final class FoodTruckState: AtomicState {
+    @Mutable @Property public var selectedDonut: Donut.ID = -1
+
+    final class Index: AtomicState {
+        @Mutable @Property public var donut = [Donut.ID]()
+    }
+
+    final class Data: KeyedState<Donut.ID> {
+        @Mutable @Property public var donut: Donut = Donut.newDonut
+    }
+}
+
+extension Donut {
+    static var newDonut = Donut(
+        id: Donut.all.count,
+        name: String(localized: "New Donut", comment: "New donut-placeholder name."),
+        dough: .plain,
+        glaze: .none,
+        topping: .none
+    )
+}

From bf74ce5aea068a829fd19209725d25e2adf084ba Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:22:59 +0200
Subject: [PATCH 3/8] Adding ApplicationEnvironment extension to bootstrap data

* Adding ApplicationEnvironment extension as temporary solution to feed
  data to the Environment

  Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 .../ApplicationEnvironment+Preview.swift      | 49 +++++++++++++++++++
 Food Truck.xcodeproj/project.pbxproj          |  6 +++
 2 files changed, 55 insertions(+)
 create mode 100644 App/General/ApplicationEnvironment+Preview.swift

diff --git a/App/General/ApplicationEnvironment+Preview.swift b/App/General/ApplicationEnvironment+Preview.swift
new file mode 100644
index 0000000..3bbce21
--- /dev/null
+++ b/App/General/ApplicationEnvironment+Preview.swift
@@ -0,0 +1,49 @@
+//
+//  ApplicationEnvironment+Preview.swift
+//
+//
+//  Created by Anton Kolchunov on 21.08.23.
+//
+
+import Foundation
+import FoodTruckKit
+@testable import Decide
+
+extension ApplicationEnvironment {
+    static var preview: ApplicationEnvironment {
+        bootstrap()
+        return `default`
+    }
+
+    static func bootstrap(donuts: [Donut] = Donut.all) {
+        bootstrap(donuts.map { $0.id }, for: \FoodTruckState.Index.$donut)
+        for donut in donuts {
+            bootstrap(donut, for: \FoodTruckState.Data.$donut, at: donut.id)
+        }
+        bootstrap(0, for: \FoodTruckState.$selectedDonut)
+    }
+
+    // TODO: Move to Decide framework
+    static func bootstrap<S: AtomicState, Value>(
+        _ newValue: Value,
+        for keyPath: KeyPath<S, Mutable<Value>>
+    ) {
+        `default`.setValue(
+            newValue,
+            keyPath.appending(path: \.wrappedValue)
+        )
+    }
+
+    // TODO: Move to Decide framework
+    static func bootstrap<I:Hashable, S: KeyedState<I>, Value>(
+        _ newValue: Value,
+        for keyPath: KeyPath<S, Mutable<Value>>,
+        at identifier: I
+    ) {
+        `default`.setValue(
+            newValue,
+            keyPath.appending(path: \.wrappedValue),
+            at: identifier)
+    }
+}
+
diff --git a/Food Truck.xcodeproj/project.pbxproj b/Food Truck.xcodeproj/project.pbxproj
index 8f7d230..c3dee12 100644
--- a/Food Truck.xcodeproj/project.pbxproj	
+++ b/Food Truck.xcodeproj/project.pbxproj	
@@ -10,6 +10,8 @@
 		220EFD2A2A93AF0E004488DE /* Decide in Frameworks */ = {isa = PBXBuildFile; productRef = 220EFD292A93AF0E004488DE /* Decide */; };
 		2273ABDE2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
 		2273ABDF2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
+		2273ABE42A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */; };
+		2273ABE52A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */; };
 		6178CE6E2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */; };
 		6178CE6F2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */; };
 		84ACCDC3282D671600756FB7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 84ACCDC1282D671600756FB7 /* Localizable.strings */; };
@@ -165,6 +167,7 @@
 
 /* Begin PBXFileReference section */
 		2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoodTruckState.swift; sourceTree = "<group>"; };
+		2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApplicationEnvironment+Preview.swift"; sourceTree = "<group>"; };
 		6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardNavigationHeader.swift; sourceTree = "<group>"; };
 		7AE906C103F323E6CEF38CA5 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
 		84ACCDC5282D671600756FB7 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -414,6 +417,7 @@
 			children = (
 				E0C37C14282333AE007B925B /* WidthThresholdReader.swift */,
 				E0C4A534283EBD67007D5B83 /* FlowLayout.swift */,
+				2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */,
 			);
 			path = General;
 			sourceTree = "<group>";
@@ -636,6 +640,7 @@
 				2273ABDF2A940FB8005EFE4F /* FoodTruckState.swift in Sources */,
 				E051074B283C187300FCE3E6 /* SocialFeedPlusSettings.swift in Sources */,
 				E0C4A536283EBD67007D5B83 /* FlowLayout.swift in Sources */,
+				2273ABE52A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */,
 				E051074C283C187300FCE3E6 /* DetailColumn.swift in Sources */,
 				E051074D283C187300FCE3E6 /* WidthThresholdReader.swift in Sources */,
 				E051074E283C187300FCE3E6 /* SignUpView.swift in Sources */,
@@ -695,6 +700,7 @@
 				E08D7060283C492C00014B89 /* SocialFeedContent.swift in Sources */,
 				E0C37BE328233197007B925B /* SignUpView.swift in Sources */,
 				E0C37BED282331AA007B925B /* CityWeatherCard.swift in Sources */,
+				2273ABE42A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */,
 				E0C37C26282333C9007B925B /* SubscriptionStoreView.swift in Sources */,
 				E09DC36A2833222700040525 /* TruckSocialFeedCard.swift in Sources */,
 				E09DC36B2833222700040525 /* TruckWeatherCard.swift in Sources */,

From 854ba9da03d5b2bbb28fb68e7f6ee45c1f1b05b9 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:24:15 +0200
Subject: [PATCH 4/8] Making Int Identifiable

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/General/Int+Identifiable.swift   | 13 +++++++++++++
 Food Truck.xcodeproj/project.pbxproj |  6 ++++++
 2 files changed, 19 insertions(+)
 create mode 100644 App/General/Int+Identifiable.swift

diff --git a/App/General/Int+Identifiable.swift b/App/General/Int+Identifiable.swift
new file mode 100644
index 0000000..e40e016
--- /dev/null
+++ b/App/General/Int+Identifiable.swift
@@ -0,0 +1,13 @@
+//
+//  Int+Identifiable.swift
+//  Food Truck
+//
+//  Created by Anton Kolchunov on 21.08.23.
+//  Copyright © 2023 Apple. All rights reserved.
+//
+
+import Foundation
+
+extension Int: Identifiable {
+    public var id: Int { self }
+}
diff --git a/Food Truck.xcodeproj/project.pbxproj b/Food Truck.xcodeproj/project.pbxproj
index c3dee12..f969515 100644
--- a/Food Truck.xcodeproj/project.pbxproj	
+++ b/Food Truck.xcodeproj/project.pbxproj	
@@ -10,6 +10,8 @@
 		220EFD2A2A93AF0E004488DE /* Decide in Frameworks */ = {isa = PBXBuildFile; productRef = 220EFD292A93AF0E004488DE /* Decide */; };
 		2273ABDE2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
 		2273ABDF2A940FB8005EFE4F /* FoodTruckState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */; };
+		2273ABE12A941145005EFE4F /* Int+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE02A941145005EFE4F /* Int+Identifiable.swift */; };
+		2273ABE22A941145005EFE4F /* Int+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE02A941145005EFE4F /* Int+Identifiable.swift */; };
 		2273ABE42A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */; };
 		2273ABE52A941173005EFE4F /* ApplicationEnvironment+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */; };
 		6178CE6E2845B07E00F240E8 /* CardNavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */; };
@@ -167,6 +169,7 @@
 
 /* Begin PBXFileReference section */
 		2273ABDD2A940FB8005EFE4F /* FoodTruckState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoodTruckState.swift; sourceTree = "<group>"; };
+		2273ABE02A941145005EFE4F /* Int+Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Identifiable.swift"; sourceTree = "<group>"; };
 		2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApplicationEnvironment+Preview.swift"; sourceTree = "<group>"; };
 		6178CE6D2845B07E00F240E8 /* CardNavigationHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardNavigationHeader.swift; sourceTree = "<group>"; };
 		7AE906C103F323E6CEF38CA5 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
@@ -417,6 +420,7 @@
 			children = (
 				E0C37C14282333AE007B925B /* WidthThresholdReader.swift */,
 				E0C4A534283EBD67007D5B83 /* FlowLayout.swift */,
+				2273ABE02A941145005EFE4F /* Int+Identifiable.swift */,
 				2273ABE32A941173005EFE4F /* ApplicationEnvironment+Preview.swift */,
 			);
 			path = General;
@@ -626,6 +630,7 @@
 				E0510739283C187300FCE3E6 /* App.swift in Sources */,
 				E051073A283C187300FCE3E6 /* OrderDetailView.swift in Sources */,
 				E051073B283C187300FCE3E6 /* SocialFeedView.swift in Sources */,
+				2273ABE22A941145005EFE4F /* Int+Identifiable.swift in Sources */,
 				E051073C283C187300FCE3E6 /* OrderCompleteView.swift in Sources */,
 				E051073F283C187300FCE3E6 /* RecommendedParkingSpotCard.swift in Sources */,
 				E0510740283C187300FCE3E6 /* StoreSupportView.swift in Sources */,
@@ -692,6 +697,7 @@
 				E09DC3692833222700040525 /* CardNavigationHeaderLabelStyle.swift in Sources */,
 				E09DC36D2833222700040525 /* TruckDonutsCard.swift in Sources */,
 				E0C37C0028233374007B925B /* DonutGallery.swift in Sources */,
+				2273ABE12A941145005EFE4F /* Int+Identifiable.swift in Sources */,
 				E0C37C29282333C9007B925B /* SocialFeedPlusSettings.swift in Sources */,
 				E0C37C1E282333B8007B925B /* DetailColumn.swift in Sources */,
 				E0C4A52B283E8FD5007D5B83 /* TopFiveDonutsView.swift in Sources */,

From d0fd054d8697cfd3c4cb6105d690fe3e892664c8 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:24:52 +0200
Subject: [PATCH 5/8] Integrating Decide into DonutEditor

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/Donut/DonutEditor.swift       | 20 +++++++++++++-------
 App/Navigation/DetailColumn.swift |  2 +-
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/App/Donut/DonutEditor.swift b/App/Donut/DonutEditor.swift
index 0fbb309..8c2e594 100644
--- a/App/Donut/DonutEditor.swift
+++ b/App/Donut/DonutEditor.swift
@@ -7,10 +7,16 @@ The donut editor view.
 
 import SwiftUI
 import FoodTruckKit
+import Decide
 
 struct DonutEditor: View {
-    @Binding var donut: Donut
-    
+    @Observe(\FoodTruckState.$selectedDonut) var id
+    @BindKeyed(\FoodTruckState.Data.$donut) var donuts
+
+    var donut: Donut {
+        donuts[id]
+    }
+
     var body: some View {
         ZStack {
             #if os(macOS)
@@ -76,7 +82,7 @@ struct DonutEditor: View {
     @ViewBuilder
     var editorContent: some View {
         Section("Donut") {
-            TextField("Name", text: $donut.name, prompt: Text("Donut Name"))
+            TextField("Name", text: donuts[id].name, prompt: Text("Donut Name"))
         }
         
         Section("Flavor Profile") {
@@ -108,14 +114,14 @@ struct DonutEditor: View {
         }
         
         Section("Ingredients") {
-            Picker("Dough", selection: $donut.dough) {
+            Picker("Dough", selection: donuts[id].dough) {
                 ForEach(Donut.Dough.all) { dough in
                     Text(dough.name)
                         .tag(dough)
                 }
             }
             
-            Picker("Glaze", selection: $donut.glaze) {
+            Picker("Glaze", selection: donuts[id].glaze) {
                 Section {
                     Text("None")
                         .tag(nil as Donut.Glaze?)
@@ -126,7 +132,7 @@ struct DonutEditor: View {
                 }
             }
             
-            Picker("Topping", selection: $donut.topping) {
+            Picker("Topping", selection: donuts[id].topping) {
                 Section {
                     Text("None")
                         .tag(nil as Donut.Topping?)
@@ -165,7 +171,7 @@ struct DonutEditor_Previews: PreviewProvider {
         @State private var donut = Donut.preview
 
         var body: some View {
-            DonutEditor(donut: $donut)
+            DonutEditor()
         }
     }
 
diff --git a/App/Navigation/DetailColumn.swift b/App/Navigation/DetailColumn.swift
index d073ae4..094188a 100644
--- a/App/Navigation/DetailColumn.swift
+++ b/App/Navigation/DetailColumn.swift
@@ -42,7 +42,7 @@ struct DetailColumn: View {
         case .donuts:
             DonutGallery(model: model)
         case .donutEditor:
-            DonutEditor(donut: $model.newDonut)
+            DonutEditor()
         case .topFive:
             TopFiveDonutsView(model: model)
         case .city(let id):

From 54417d1bf754b0619fa465884e320e0d48097fbb Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:25:26 +0200
Subject: [PATCH 6/8] Integrating Decide into DonutGallery

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/Donut/DonutGallery.swift | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/App/Donut/DonutGallery.swift b/App/Donut/DonutGallery.swift
index f08a900..c9758eb 100644
--- a/App/Donut/DonutGallery.swift
+++ b/App/Donut/DonutGallery.swift
@@ -7,9 +7,14 @@ The donut gallery view.
 
 import SwiftUI
 import FoodTruckKit
+import Decide
 
 struct DonutGallery: View {
     @ObservedObject var model: FoodTruckModel
+
+    @Bind(\FoodTruckState.$selectedDonut) var selectedDonut
+    @Bind(\FoodTruckState.Index.$donut) var donutsIndex
+    @ObserveKeyed(\FoodTruckState.Data.$donut) var donutsData
     
     @State private var layout = BrowserLayout.grid
     @State private var sort = DonutSortOrder.popularity(.week)
@@ -19,8 +24,8 @@ struct DonutGallery: View {
     @State private var selection = Set<Donut.ID>()
     @State private var searchText = ""
     
-    var filteredDonuts: [Donut] {
-        model.donuts(sortedBy: sort).filter { $0.matches(searchText: searchText) }
+    var filteredDonuts: [Donut.ID] {
+        donutsIndex.filter { donutsData[$0].matches(searchText: searchText) }
     }
     
     var tableImageSize: Double {
@@ -61,10 +66,18 @@ struct DonutGallery: View {
         .searchable(text: $searchText)
         .navigationTitle("Donuts")
         .navigationDestination(for: Donut.ID.self) { donutID in
-            DonutEditor(donut: model.donutBinding(id: donutID))
+            DonutEditor()
+                .onAppear()  {
+                    selectedDonut = donutID
+                }
         }
         .navigationDestination(for: String.self) { _ in
-            DonutEditor(donut: $model.newDonut)
+            DonutEditor()
+                .onAppear() {
+                    let newID = donutsIndex.count
+                    selectedDonut = newID
+                    donutsIndex.append(newID)
+                }
         }
     }
     
@@ -78,13 +91,13 @@ struct DonutGallery: View {
     
     var table: some View {
         Table(filteredDonuts, selection: $selection) {
-            TableColumn("Name") { donut in
-                NavigationLink(value: donut.id) {
+            TableColumn("Name") { donutId in
+                NavigationLink(value: donutId) {
                     HStack {
-                        DonutView(donut: donut)
+                        DonutView(donut: donutsData[donutId])
                             .frame(width: tableImageSize, height: tableImageSize)
 
-                        Text(donut.name)
+                        Text(donutsData[donutId].name)
                     }
                 }
             }
@@ -180,5 +193,6 @@ struct DonutBakery_Previews: PreviewProvider {
         NavigationStack {
             Preview()
         }
+        .appEnvironment(.preview)
     }
 }

From 14c0fcf1253ab4b1ff0d3a2affe2ec125f7664a7 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:25:54 +0200
Subject: [PATCH 7/8] Integrating Decide into DonutGalleryGrid

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/Donut/DonutGalleryGrid.swift | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/App/Donut/DonutGalleryGrid.swift b/App/Donut/DonutGalleryGrid.swift
index a9bb87d..73d9bc5 100644
--- a/App/Donut/DonutGalleryGrid.swift
+++ b/App/Donut/DonutGalleryGrid.swift
@@ -7,10 +7,13 @@ The grid view used in the DonutGallery.
 
 import SwiftUI
 import FoodTruckKit
+import Decide
 
 struct DonutGalleryGrid: View {
-    var donuts: [Donut]
+    var donuts: [Donut.ID]
     var width: Double
+
+    @ObserveKeyed(\FoodTruckState.Data.$donut) var donutsData
     
     #if os(iOS)
     @Environment(\.horizontalSizeClass) private var sizeClass
@@ -58,15 +61,15 @@ struct DonutGalleryGrid: View {
     
     var body: some View {
         LazyVGrid(columns: gridItems, spacing: 20) {
-            ForEach(donuts) { donut in
-                NavigationLink(value: donut.id) {
+            ForEach(donuts, id: \.self) { donutId in
+                NavigationLink(value: donutId) {
                     VStack {
-                        DonutView(donut: donut)
+                        DonutView(donut: donutsData[donutId])
                             .frame(width: thumbnailSize, height: thumbnailSize)
 
                         VStack {
-                            let flavor = donut.flavors.mostPotentFlavor
-                            Text(donut.name)
+                            let flavor = donutsData[donutId].flavors.mostPotentFlavor
+                            Text(donutsData[donutId].name)
                             HStack(spacing: 4) {
                                 flavor.image
                                 Text(flavor.name)
@@ -86,12 +89,15 @@ struct DonutGalleryGrid: View {
 
 struct DonutGalleryGrid_Previews: PreviewProvider {
     struct Preview: View {
-        @State private var donuts = Donut.all
-        
+        @State private var donuts = Donut.all.map { $0.id }
+
         var body: some View {
             GeometryReader { geometryProxy in
                 ScrollView {
-                    DonutGalleryGrid(donuts: donuts, width: geometryProxy.size.width)
+                    DonutGalleryGrid(
+                        donuts: donuts,
+                        width: geometryProxy.size.width
+                    )
                 }
             }
         }
@@ -99,5 +105,6 @@ struct DonutGalleryGrid_Previews: PreviewProvider {
     
     static var previews: some View {
         Preview()
+            .appEnvironment(.preview)
     }
 }

From be45a96da13ebb421de74e9c3624bb466dc1e296 Mon Sep 17 00:00:00 2001
From: Anton Kolchunov <garfeild.ubuntu@gmail.com>
Date: Tue, 22 Aug 2023 08:35:37 +0200
Subject: [PATCH 8/8] Bootstrapping data at app launch

Co-authored-by: Anton Kolchunov <anton.kolchunov@hellofresh.com>
---
 App/App.swift | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/App/App.swift b/App/App.swift
index d72e7ca..12a969d 100644
--- a/App/App.swift
+++ b/App/App.swift
@@ -7,6 +7,7 @@ The single entry point for the Food Truck app on iOS and macOS.
 
 import SwiftUI
 import FoodTruckKit
+import Decide
 
 /// The app's entry point.
 ///
@@ -18,7 +19,11 @@ struct FoodTruckApp: App {
     @StateObject private var model = FoodTruckModel()
     /// The in-app purchase store's state.
     @StateObject private var accountStore = AccountStore()
-    
+
+    init() {
+        ApplicationEnvironment.bootstrap(donuts: model.donuts)
+    }
+
     /// The app's body function.
     ///
     /// This app uses a [`WindowGroup`](https://developer.apple.com/documentation/swiftui/windowgroup) scene, which contains the root view of the app, ``ContentView``.