Skip to content

Commit

Permalink
Merge pull request #11 from johnpatrickmorgan/feature/codable
Browse files Browse the repository at this point in the history
Support for NBNavigationPath.CodableRepresentation
  • Loading branch information
johnpatrickmorgan authored Aug 1, 2022
2 parents f8d2385 + 3f4afa4 commit fc44a59
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
529673D2286BB50200C01BCF /* ArrayBindingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529673D0286BB50200C01BCF /* ArrayBindingView.swift */; };
529673D4286BC48600C01BCF /* NoBindingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529673D3286BC48600C01BCF /* NoBindingView.swift */; };
529673D5286BC48600C01BCF /* NoBindingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529673D3286BC48600C01BCF /* NoBindingView.swift */; };
52DD85F82895C984004B5344 /* NavigationBackport in Frameworks */ = {isa = PBXBuildFile; productRef = 52DD85F72895C984004B5344 /* NavigationBackport */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -48,6 +49,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
52DD85F82895C984004B5344 /* NavigationBackport in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -61,6 +63,7 @@
52925EAE28549A60001B9190 /* Shared */,
52925EBD28549A62001B9190 /* macOS */,
52925EB728549A62001B9190 /* Products */,
52DD85F62895C984004B5344 /* Frameworks */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -102,6 +105,13 @@
name = Packages;
sourceTree = "<group>";
};
52DD85F62895C984004B5344 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -138,6 +148,9 @@
dependencies = (
);
name = "NavigationBackportApp (macOS)";
packageProductDependencies = (
52DD85F72895C984004B5344 /* NavigationBackport */,
);
productName = "NavigationBackportApp (macOS)";
productReference = 52925EBC28549A62001B9190 /* NavigationBackportApp.app */;
productType = "com.apple.product-type.application";
Expand Down Expand Up @@ -347,7 +360,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJAU94J65Z;
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down Expand Up @@ -377,7 +390,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJAU94J65Z;
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down Expand Up @@ -407,10 +420,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJAU94J65Z;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -435,10 +449,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJAU94J65Z;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand Down Expand Up @@ -506,6 +521,10 @@
package = 52925ECD28549A84001B9190 /* XCRemoteSwiftPackageReference "NavigationBackport" */;
productName = NavigationBackport;
};
52DD85F72895C984004B5344 /* NavigationBackport */ = {
isa = XCSwiftPackageProductDependency;
productName = NavigationBackport;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 52925EAA28549A60001B9190 /* Project object */;
Expand Down
46 changes: 33 additions & 13 deletions NavigationBackportApp/Shared/ArrayBindingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,41 @@ enum Screen: Hashable {
}

struct ArrayBindingView: View {
@State var savedPath: [Screen]?
@State var path: [Screen] = []

var body: some View {
NBNavigationStack(path: $path) {
HomeView(show99RedBalloons: show99RedBalloons)
.nbNavigationDestination(for: Screen.self, destination: { screen in
switch screen {
case .numberList(let numberList):
NumberListView(numberList: numberList)
case .number(let number):
NumberView(number: number, goBackToRoot: { path.removeLast(path.count) })
case .visualisation(let visualisation):
EmojiView(visualisation: visualisation)
}
})
VStack {
HStack {
Button("Save", action: savePath)
.disabled(savedPath == path)
Button("Restore", action: restorePath)
.disabled(savedPath == nil)
}
NBNavigationStack(path: $path) {
HomeView(show99RedBalloons: show99RedBalloons)
.nbNavigationDestination(for: Screen.self, destination: { screen in
switch screen {
case .numberList(let numberList):
NumberListView(numberList: numberList)
case .number(let number):
NumberView(number: number, goBackToRoot: { path.removeLast(path.count) })
case .visualisation(let visualisation):
EmojiView(visualisation: visualisation)
}
})
}
}
}

func savePath() {
savedPath = path
}

func restorePath() {
guard let savedPath = savedPath else { return }
$path.withDelaysIfUnsupported {
$0 = savedPath
}
}

Expand Down Expand Up @@ -97,7 +117,7 @@ private struct EmojiView: View {
let visualisation: EmojiVisualisation

var body: some View {
Text(String(Array(repeating: visualisation.emoji, count: visualisation.count)))
Text(visualisation.text)
.navigationTitle("Visualise \(visualisation.count)")
}
}
10 changes: 7 additions & 3 deletions NavigationBackportApp/Shared/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import NavigationBackport
import SwiftUI

struct EmojiVisualisation: Hashable {
let emoji: Character
struct EmojiVisualisation: Hashable, Codable {
let emoji: String
let count: Int

var text: String {
Array(repeating: emoji, count: count).joined()
}
}

struct NumberList: Hashable {
struct NumberList: Hashable, Codable {
let range: Range<Int>
}

Expand Down
50 changes: 38 additions & 12 deletions NavigationBackportApp/Shared/NBNavigationPathView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,46 @@ import NavigationBackport
import SwiftUI

struct NBNavigationPathView: View {
@State var encodedPathData: Data?
@State var path = NBNavigationPath()

var body: some View {
NBNavigationStack(path: $path) {
HomeView(show99RedBalloons: show99RedBalloons)
.nbNavigationDestination(for: NumberList.self, destination: { numberList in
NumberListView(numberList: numberList)
})
.nbNavigationDestination(for: Int.self, destination: { number in
NumberView(number: number, goBackToRoot: { path.removeLast(path.count) })
})
.nbNavigationDestination(for: EmojiVisualisation.self, destination: { visualisation in
EmojiView(visualisation: visualisation)
})
VStack {
HStack {
Button("Encode", action: encodePath)
.disabled(try! encodedPathData == JSONEncoder().encode(path.codable))
Button("Decode", action: decodePath)
.disabled(encodedPathData == nil)
}
NBNavigationStack(path: $path) {
HomeView(show99RedBalloons: show99RedBalloons)
.nbNavigationDestination(for: NumberList.self, destination: { numberList in
NumberListView(numberList: numberList)
})
.nbNavigationDestination(for: Int.self, destination: { number in
NumberView(number: number, goBackToRoot: { path.removeLast(path.count) })
})
.nbNavigationDestination(for: EmojiVisualisation.self, destination: { visualisation in
EmojiView(visualisation: visualisation)
})
}
}
}

func encodePath() {
guard let codable = path.codable else {
return
}
encodedPathData = try! JSONEncoder().encode(codable)
}

func decodePath() {
guard let encodedPathData = encodedPathData else {
return
}
let codable = try! JSONDecoder().decode(NBNavigationPath.CodableRepresentation.self, from: encodedPathData)
$path.withDelaysIfUnsupported {
$0 = NBNavigationPath(codable)
}
}

Expand Down Expand Up @@ -90,7 +116,7 @@ private struct EmojiView: View {
let visualisation: EmojiVisualisation

var body: some View {
Text(String(Array(repeating: visualisation.emoji, count: visualisation.count)))
Text(visualisation.text)
.navigationTitle("Visualise \(visualisation.count)")
}
}
2 changes: 1 addition & 1 deletion NavigationBackportApp/Shared/NoBindingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private struct EmojiView: View {
let visualisation: EmojiVisualisation

var body: some View {
Text(String(Array(repeating: visualisation.emoji, count: visualisation.count)))
Text(visualisation.text)
.navigationTitle("Visualise \(visualisation.count)")
}
}
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ This package uses the navigation APIs available in older SwiftUI versions (such

`navigationDestination` -> `nbNavigationDestination`

You can migrate to these APIs now, and when you eventually bump your deployment target to iOS 16, you can remove this library and easily migrate to its SwiftUI equivalent. You can initialise `NBNavigationStack` with a binding to an `Array`, a `NBNavigationPath` binding, or neither.
`NavigationPath.CodableRepresentation` -> `NBNavigationPath.CodableRepresentation`


You can migrate to these APIs now, and when you eventually bump your deployment target to iOS 16, you can remove this library and easily migrate to its SwiftUI equivalent. `NavigationStack`'s full API is replicated, so you can initialise an `NBNavigationStack` with a binding to an `Array`, with a binding to a `NBNavigationPath` binding, or with no binding at all.

## Example

Expand Down Expand Up @@ -81,19 +84,22 @@ struct NumberView: View {
}

struct EmojiVisualisation: Hashable {
let emoji: Character
let emoji: String
let count: Int

var description: String {
Array(repeating: emoji, count: count).joined()
}
}

struct EmojiView: View {
let visualisation: EmojiVisualisation

var body: some View {
Text(String(Array(repeating: visualisation.emoji, count: visualisation.count)))
Text(visualisation.text)
.navigationTitle("Visualise \(visualisation.count)")
}
}

```

## Deep-linking
Expand All @@ -106,7 +112,7 @@ struct EmojiView: View {
path.append(Screen.confirmChanges(orderId: id))
```

However, the following code will successfully push all three screens, one after another:
However, the amended code below will successfully push all three screens, one after another:

```swift
$path.withDelaysIfUnsupported {
Expand All @@ -117,12 +123,3 @@ $path.withDelaysIfUnsupported {
```

You can make any changes to the path passed into the `withDelaysIfUnsupported` closure, and the library will calculate the minimal number of state updates required to successfully update the UI.

## To do

The package is not yet fully complete. Here are some outstanding tasks:

- [ ] Codable support for NavigationPath
- [ ] Codable support for NavigationLink
- [ ] Backport NavigationSplitView
- [ ] Conditionally use SwiftUI Navigation API if available?
Loading

0 comments on commit fc44a59

Please sign in to comment.