Merge pull request #117 from dh-ecc/tca-nav-skeleton

Tca nav skeleton
This commit is contained in:
Francisco Gindre 2021-11-16 12:52:04 -03:00 committed by GitHub
commit 883ce52011
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 622 additions and 3 deletions

View File

@ -89,6 +89,16 @@
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; };
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; };
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; };
F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; };
F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -196,6 +206,16 @@
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = "<group>"; };
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.swift; sourceTree = "<group>"; };
F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = "<group>"; };
F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = "<group>"; };
F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithStateBinding.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -525,6 +545,11 @@
children = (
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */,
F96B41EA273B50520021B49A /* Strings.swift */,
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
F9C165B3274031F600592F76 /* Bindings.swift */,
F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */,
F93673D52742CB840099C6AF /* Previews.swift */,
);
path = Util;
sourceTree = "<group>";
@ -581,6 +606,8 @@
6654C73B2715A3F000901167 /* Features */ = {
isa = PBXGroup;
children = (
F93874EC273C4DE200F0E875 /* Home */,
F96B41E2273B501F0021B49A /* TransactionHistory */,
6654C73C2715A3FA00901167 /* Onboarding */,
);
path = Features;
@ -637,6 +664,41 @@
path = CircularFrame;
sourceTree = "<group>";
};
F93874EC273C4DE200F0E875 /* Home */ = {
isa = PBXGroup;
children = (
F93874ED273C4DE200F0E875 /* HomeStore.swift */,
F93874EE273C4DE200F0E875 /* Views */,
);
path = Home;
sourceTree = "<group>";
};
F93874EE273C4DE200F0E875 /* Views */ = {
isa = PBXGroup;
children = (
F93874EF273C4DE200F0E875 /* HomeView.swift */,
);
path = Views;
sourceTree = "<group>";
};
F96B41E2273B501F0021B49A /* TransactionHistory */ = {
isa = PBXGroup;
children = (
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */,
F96B41E4273B501F0021B49A /* Views */,
);
path = TransactionHistory;
sourceTree = "<group>";
};
F96B41E4273B501F0021B49A /* Views */ = {
isa = PBXGroup;
children = (
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */,
F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */,
);
path = Views;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -835,9 +897,11 @@
buildActionMask = 2147483647;
files = (
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */,
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */,
0D32281F26C5867D00262533 /* ScanQrScreenViewModel.swift in Sources */,
669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */,
0D32282E26C5870B00262533 /* SendScreenViewModel.swift in Sources */,
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
0D32282D26C5870B00262533 /* SendScreen.swift in Sources */,
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
0D1922ED26BDE0C600052649 /* AppRouter.swift in Sources */,
@ -848,15 +912,20 @@
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */,
0D32281E26C5867D00262533 /* ScanQrScreen.swift in Sources */,
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */,
669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */,
0D864A0E26E1583000A61879 /* LoadingScreen.swift in Sources */,
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */,
0DA13C9826C186FF00E3B610 /* RestoreWalletScreenViewModel.swift in Sources */,
F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */,
F93673D62742CB840099C6AF /* Previews.swift in Sources */,
0D32283326C5877A00262533 /* BalanceScreenViewModel.swift in Sources */,
0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */,
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */,
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */,
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
@ -866,6 +935,7 @@
0D170A7226BC802800EB6A46 /* Router.swift in Sources */,
0D354A0926D5A9D000315F45 /* Services.swift in Sources */,
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */,
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */,
0DA13C9726C186FF00E3B610 /* RestoreWalletScreen.swift in Sources */,
0DACFA8127208D940039EEA5 /* UInt+SuperscriptText.swift in Sources */,
0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */,
@ -885,7 +955,9 @@
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */,
F9C165B4274031F600592F76 /* Bindings.swift in Sources */,
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */,
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */,
0D354A0A26D5A9D000315F45 /* KeyStoring.swift in Sources */,
0DA13CA226C1955600E3B610 /* HomeScreenViewModel.swift in Sources */,

View File

@ -0,0 +1,92 @@
import ComposableArchitecture
import SwiftUI
struct HomeState: Equatable {
enum Route: Equatable {
case history
case send
}
var transactionHistoryState: TransactionHistoryState
var route: Route?
}
enum HomeAction: Equatable {
case updateRoute(HomeState.Route?)
case transactionHistory(TransactionHistoryAction)
}
// MARK: - HomeReducer
typealias HomeReducer = Reducer<HomeState, HomeAction, Void>
extension HomeReducer {
static let `default` = HomeReducer { state, action, _ in
switch action {
case let .updateRoute(route):
state.route = route
return .none
case let .transactionHistory(transactionHistoryAction):
return TransactionHistoryReducer
.default
.run(&state.transactionHistoryState, transactionHistoryAction, ())
.map(HomeAction.transactionHistory)
}
}
}
// MARK: - HomeStore
typealias HomeStore = Store<HomeState, HomeAction>
extension HomeStore {
func historyStore() -> TransactionHistoryStore {
self.scope(
state: \.transactionHistoryState,
action: HomeAction.transactionHistory
)
}
}
// MARK: - HomeViewStore
typealias HomeViewStore = ViewStore<HomeState, HomeAction>
extension HomeViewStore {
func historyToggleString() -> String {
let hideShowString = isHistoryActive ? "HIDE" : "SHOW"
let selectedString = selectedTranactionID.map { "selected id: \($0)" } ?? "NONE selected"
let parts = [hideShowString, "History", selectedString]
return parts.joined(separator: " ")
}
func toggleShowingHistory() {
send(.updateRoute(isHistoryActive ? nil : .history))
}
func toggleSelectedTransaction() {
let isAlreadySelected = (self.selectedTranactionID != nil)
let transcation = self.transactionHistoryState.transactions[5]
let newRoute = isAlreadySelected ? nil : TransactionHistoryState.Route.showTransaction(transcation)
send(.transactionHistory(.setRoute(newRoute)))
}
var isHistoryActive: Bool {
self.route == .history
}
var selectedTranactionID: Int? {
self.transactionHistoryState
.route
.flatMap(/TransactionHistoryState.Route.showTransaction)
.map(\.id)
}
var showHistoryBinding: Binding<Bool> {
self.binding(
get: { $0.route == .history },
send: { isActive in
return .updateRoute(isActive ? .history : nil)
}
)
}
}

View File

@ -0,0 +1,82 @@
import SwiftUI
import ComposableArchitecture
struct HomeView: View {
let store: Store<HomeState, HomeAction>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
Button(
action: { viewStore.toggleShowingHistory() },
label: { Text(viewStore.historyToggleString()) }
)
.primaryButtonStyle
.frame(height: 50)
Button(
action: { viewStore.toggleSelectedTransaction() },
label: { Text("Toggle Selected Transaction") }
)
.primaryButtonStyle
.frame(height: 50)
Spacer()
HStack {
VStack(alignment: .leading) {
Text("Route: \(String(dumping: viewStore.route))")
Text("SelectedTransaction: \(String(dumping: viewStore.transactionHistoryState.route.map(/TransactionHistoryState.Route.showTransaction)))")
}
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
}
.padding(.horizontal, 30)
.navigationBarTitle("Home", displayMode: .inline)
.fullScreenCover(
isPresented: viewStore.showHistoryBinding,
content: {
NavigationView {
TransactionHistoryView(store: store.historyStore())
.toolbar {
ToolbarItem {
Button("Done") { viewStore.send(.updateRoute(nil)) }
}
}
}
}
)
}
}
}
// MARK: - Previews
#if DEBUG
extension HomeStore {
static var demo: HomeStore {
HomeStore(
initialState: HomeState(
transactionHistoryState: .init(
transactions: .demo,
route: nil
),
route: nil
),
reducer: .default.debug(),
environment: ()
)
}
}
#endif
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
HomeView(store: .demo)
}
}
}

View File

@ -0,0 +1,58 @@
import ComposableArchitecture
import SwiftUI
struct Transaction: Identifiable, Equatable, Hashable {
var id: Int
var amount: UInt
var memo: String
var toAddress: String
var fromAddress: String
}
struct TransactionHistoryState: Equatable {
enum Route: Equatable {
case showTransaction(Transaction)
}
var transactions: IdentifiedArrayOf<Transaction>
var route: Route?
}
enum TransactionHistoryAction: Equatable {
case setRoute(TransactionHistoryState.Route?)
}
// MARK: - TransactionHistoryReducer
typealias TransactionHistoryReducer = Reducer<TransactionHistoryState, TransactionHistoryAction, Void>
extension TransactionHistoryReducer {
static let `default` = TransactionHistoryReducer { state, action, _ in
switch action {
case let .setRoute(route):
state.route = route
return .none
}
}
}
// MARK: - TransactionHistoryStore
typealias TransactionHistoryStore = Store<TransactionHistoryState, TransactionHistoryAction>
// MARK: - TransactionHistoryViewStore
typealias TransactionHistoryViewStore = ViewStore<TransactionHistoryState, TransactionHistoryAction>
extension TransactionHistoryViewStore {
private typealias Route = TransactionHistoryState.Route
func bindingForSelectingTransaction(_ transaction: Transaction) -> Binding<Bool> {
self.binding(
get: { $0.route.map(/TransactionHistoryState.Route.showTransaction) == transaction },
send: { isActive in
TransactionHistoryAction.setRoute( isActive ? TransactionHistoryState.Route.showTransaction(transaction) : nil)
}
)
}
}

View File

@ -0,0 +1,32 @@
import SwiftUI
struct TransactionDetailView: View {
var transaction: Transaction
var body: some View {
Text(String(dumping: transaction))
.padding()
.navigationTitle("Transaction: \(transaction.id)")
}
}
struct TransactionDetail_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
TransactionDetailView(transaction: .demo)
}
}
}
#if DEBUG
extension Transaction {
static var demo: Self {
.init(
id: 2,
amount: 123,
memo: "defaultMemo",
toAddress: "ToAddress",
fromAddress: "FromAddress"
)
}
}
#endif

View File

@ -0,0 +1,80 @@
import SwiftUI
import ComposableArchitecture
struct TransactionHistoryView: View {
let store: Store<TransactionHistoryState, TransactionHistoryAction>
var body: some View {
WithViewStore(store) { viewStore in
List {
ForEach(viewStore.transactions) { transaction in
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) {
Text("Show Transaction \(transaction.id)")
.navigationLink(
isActive: $0,
destination: { TransactionDetailView(transaction: transaction) }
)
}
}
}
.navigationTitle(Text("Transactions"))
}
}
}
struct TransactionView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
TransactionHistoryView(store: .demo)
.navigationBarTitleDisplayMode(.inline)
}
NavigationView {
TransactionHistoryView(store: .demoWithSelectedTransaction)
.navigationBarTitleDisplayMode(.inline)
}
}
}
#if DEBUG
extension TransactionHistoryStore {
static var demo: Store<TransactionHistoryState, TransactionHistoryAction> {
return Store(
initialState: TransactionHistoryState(
transactions: .demo,
route: nil
),
reducer: .default,
environment: ()
)
}
static var demoWithSelectedTransaction: Store<TransactionHistoryState, TransactionHistoryAction> {
let transactions = IdentifiedArrayOf<Transaction>.demo
return Store(
initialState: TransactionHistoryState(
transactions: transactions,
route: .showTransaction(transactions[3])
),
reducer: .default.debug(),
environment: ()
)
}
}
extension IdentifiedArrayOf where Element == Transaction {
static var demo: IdentifiedArrayOf<Transaction> {
return .init(
uniqueElements: (0..<10).map {
Transaction(
id: $0,
amount: 25,
memo: "defaultMemo",
toAddress: "ToAddress",
fromAddress: "FromAddress"
)
}
)
}
}
#endif

View File

@ -9,11 +9,12 @@ import SwiftUI
@main
struct SecantApp: App {
@StateObject var appRouter = AppRouter(services: MockServices())
var homeStore: HomeStore = .demo
var body: some Scene {
WindowGroup {
appRouter.rootView()
NavigationView {
HomeView(store: homeStore)
}
}
}
}

View File

@ -0,0 +1,88 @@
import SwiftUI
import CasePaths
/// taken largely from: https://github.com/pointfreeco/episode-code-samples/blob/main/0167-navigation-pt8/SwiftUINavigation/SwiftUINavigation/SwiftUIHelpers.swift
extension Binding {
func isPresent<Wrapped>() -> Binding<Bool>
where Value == Wrapped? {
.init(
get: { self.wrappedValue != nil },
set: { isPresented in
if !isPresented {
self.wrappedValue = nil
}
}
)
}
func isPresent<Enum, Case>(_ casePath: CasePath<Enum, Case>) -> Binding<Bool>
where Value == Enum? {
Binding<Bool>(
get: {
if let wrappedValue = self.wrappedValue, casePath.extract(from: wrappedValue) != nil {
return true
} else {
return false
}
},
set: { isPresented in
if !isPresented {
self.wrappedValue = nil
}
}
)
}
func `case`<Enum, Case>(_ casePath: CasePath<Enum, Case>) -> Binding<Case?>
where Value == Enum? {
Binding<Case?>(
get: {
guard
let wrappedValue = self.wrappedValue,
let `case` = casePath.extract(from: wrappedValue)
else { return nil }
return `case`
},
// swiftlint:disable:next unused_closure_parameter
set: { `case` in
if let `case` = `case` {
self.wrappedValue = casePath.embed(`case`)
} else {
self.wrappedValue = nil
}
}
)
}
func didSet(_ callback: @escaping (Value) -> Void) -> Self {
.init(
get: { self.wrappedValue },
set: {
self.wrappedValue = $0
callback($0)
}
)
}
init?(unwrap binding: Binding<Value?>) {
guard let wrappedValue = binding.wrappedValue
else { return nil }
self.init(
get: { wrappedValue },
set: { binding.wrappedValue = $0 }
)
}
func map<T>(extract: @escaping (Value) -> T, embed: @escaping (T) -> Value?) -> Binding<T> {
Binding<T>(
get: { extract(wrappedValue) },
set: {
guard let value = embed($0) else {
return
}
wrappedValue = value
}
)
}
}

View File

@ -0,0 +1,27 @@
import SwiftUI
extension View {
func navigationLink<Destination: View>(
isActive: Binding<Bool>,
destination: @escaping () -> Destination
) -> some View {
NavigationLink<Self, Destination>(
isActive: isActive,
destination: destination,
label: { self }
)
}
func navigationLinkEmpty<Destination: View>(
isActive: Binding<Bool>,
destination: @escaping () -> Destination
) -> some View {
return self.overlay(
NavigationLink<EmptyView, Destination>(
isActive: isActive,
destination: destination,
label: { EmptyView() }
)
)
}
}

View File

@ -0,0 +1,17 @@
import SwiftUI
#if DEBUG
struct StateContainer<T, Content: View>: View {
@State private var state: T
private var content: (Binding<T>) -> Content
init(initialState: T, content: @escaping (Binding<T>) -> Content) {
self._state = State(initialValue: initialState)
self.content = content
}
var body: some View {
content($state)
}
}
#endif

11
secant/Util/Strings.swift Normal file
View File

@ -0,0 +1,11 @@
import Foundation
#if DEBUG
extension String {
init<T>(dumping value: T) {
var output = String()
dump(value, to: &output)
self.init(stringLiteral: output)
}
}
#endif

View File

@ -0,0 +1,59 @@
import SwiftUI
struct WithStateBinding<T: Equatable, Content: View>: View {
@State var localState: T
@Binding private var externalBindng: T
private var content: (Binding<T>) -> Content
init(binding: Binding<T>, content: @escaping (Binding<T>) -> Content) {
_externalBindng = binding
_localState = State(initialValue: binding.wrappedValue)
self.content = content
}
var body: some View {
content($localState)
.onChange(of: localState) { externalBindng = $0 }
}
}
// MARK: - Previews
struct WithStateBinding_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
StateContainer(initialState: (false, false, false)) { (binding: Binding<(Bool, Bool, Bool)>) in
List {
NavigationLink(
isActive: binding.0,
destination: { Text("Standard State Binding") },
label: { Text("Standard State Binding") }
)
NavigationLink(
isActive: Binding(
get: { binding.1.wrappedValue },
set: { binding.1.wrappedValue = $0 }
),
destination: { Text("Custom Binding") },
label: { Text("Custom Binding") }
)
WithStateBinding(
binding: Binding(
get: { binding.2.wrappedValue },
set: { binding.2.wrappedValue = $0 }
),
content: {
NavigationLink(
isActive:$0,
destination: { Text("Wrapped Custom Binding") },
label: { Text("Wrapped Custom Binding") }
)
}
)
}
}
}
}
}