Created
October 12, 2020 19:22
-
-
Save fmo91/9149f815ffe5aa4f163ce190abc570c4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
struct CreateToDoScreen: View { | |
@StateObject var viewModel = CreateToDoViewModel() | |
var body: some View { | |
VStack { | |
TextField("Title", text: $viewModel.title) | |
Toggle("Completed", isOn: $viewModel.completed) | |
Button("Create", action: viewModel.save) | |
viewModel.navigationControls | |
}.navigationTitle("Create To Do") | |
} | |
} | |
final class CreateToDoViewModel: ObservableObject { | |
@Published var title: String = "" | |
@Published var completed: Bool = false | |
var wireframe = CreateToDoWireframe() | |
var navigationControls: some View { | |
wireframe.navigationControls | |
} | |
func save() { | |
wireframe.navigates.toggle() | |
} | |
} | |
final class CreateToDoWireframe: SwiftUIWireframe { | |
@Published var navigates: Bool = false | |
@Published var verify: Bool = false | |
var states: [NavigationControls<CreateToDoWireframe>.StateSeed] = [ | |
.using( | |
property: \.navigates, | |
destination: AnyView(Text("Does it work?")) | |
), | |
.using( | |
property: \.verify, | |
destination: AnyView(Text("Just verifying")) | |
) | |
] | |
} | |
protocol SwiftUIWireframe: ObservableObject { | |
var states: [NavigationControls<Self>.StateSeed] { get } | |
} | |
extension SwiftUIWireframe { | |
var navigationControls: some View { | |
NavigationControls( | |
from: self, | |
states: states | |
) | |
} | |
} | |
struct NavigationControls<T: ObservableObject>: View { | |
typealias Base = ObservedObject<T>.Wrapper | |
typealias StateSeedCreator = () -> StateSeed | |
struct StateSeed { | |
let property: KeyPath<Base, Binding<Bool>> | |
let destination: AnyView | |
static func using( | |
property: KeyPath<Base, Binding<Bool>>, | |
destination: AnyView | |
) -> StateSeed { | |
return StateSeed(property: property, destination: destination) | |
} | |
} | |
@ObservedObject var wireframe: T | |
private let stateSeeds: [StateSeed] | |
init(from wireframe: T, states stateSeeds: [StateSeed]) { | |
self.wireframe = wireframe | |
self.stateSeeds = stateSeeds | |
} | |
var body: some View { | |
NavigationControlsRenderer<T>(navigationStates: stateSeeds.map { state in | |
return NavigationState( | |
from: $wireframe, | |
property: state.property, | |
destination: state.destination | |
) | |
}) | |
} | |
} | |
struct NavigationControlsRenderer<T: ObservableObject>: View { | |
let links: [Link] | |
init(navigationStates: [NavigationState<T>]) { | |
links = navigationStates.map { state in | |
state.link | |
} | |
} | |
var body: some View { | |
ForEach(links) { link in | |
NavigationLink( | |
destination: link.destination, | |
isActive: link.isActive, | |
label: { EmptyView() } | |
) | |
} | |
} | |
} | |
final class Link: Identifiable { | |
let id: String | |
var isActive: Binding<Bool> | |
let destination: AnyView | |
init( | |
isActive: Binding<Bool>, | |
destination: AnyView | |
) { | |
self.id = UUID().uuidString | |
self.isActive = isActive | |
self.destination = destination | |
} | |
} | |
struct NavigationState<T: ObservableObject> { | |
typealias Base = ObservedObject<T>.Wrapper | |
let base: Base | |
let property: KeyPath<Base, Binding<Bool>> | |
let destination: AnyView | |
init(from base: Base, property: KeyPath<Base, Binding<Bool>>, destination: AnyView) { | |
self.base = base | |
self.property = property | |
self.destination = destination | |
} | |
var link: Link { | |
Link( | |
isActive: base[keyPath: property], | |
destination: destination | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment