Skip to content

Instantly share code, notes, and snippets.

@fmo91
Created October 12, 2020 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fmo91/9149f815ffe5aa4f163ce190abc570c4 to your computer and use it in GitHub Desktop.
Save fmo91/9149f815ffe5aa4f163ce190abc570c4 to your computer and use it in GitHub Desktop.
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