Skip to content

Instantly share code, notes, and snippets.

@elkraneo
Last active September 4, 2022 12:53
Show Gist options
  • Save elkraneo/ae3749432864bcf0fe661855e317a525 to your computer and use it in GitHub Desktop.
Save elkraneo/ae3749432864bcf0fe661855e317a525 to your computer and use it in GitHub Desktop.
//: [Previous](@previous)
import Foundation
import PlaygroundSupport
import SwiftUI
/// https://www.pointfree.co/episodes/ep202-reducer-protocol-the-solution#t750
protocol EntityRepresentable {
associatedtype Body: EntityRepresentable
@EntityBuilder var body: Body { get }
}
extension Never: EntityRepresentable {
var body: Never { fatalError("We should never reach this") }
}
extension EntityRepresentable {
var body: Never { fatalError("This should never happen") }
}
struct EmptyEntity: EntityRepresentable {
typealias Body = Never
}
struct TupleEntity<T>: EntityRepresentable {
var value: T
init(_ value: T) {
self.value = value
}
typealias Body = Never
}
struct AnyEntity: Identifiable {
let id: AnyHashable
let content: any EntityRepresentable
init<Content: EntityRepresentable>(_ content: Content) where Content: Identifiable {
self.content = content
self.id = content.id
}
}
// MARK: - Builder
@resultBuilder
struct EntityBuilder {
static func buildBlock<C: EntityRepresentable>(_ content: C) -> C { content }
static func buildBlock<C0, C1>(
_ c0: C0,
_ c1: C1
) -> TupleEntity<(C0, C1)>
where
C0: EntityRepresentable,
C1: EntityRepresentable
{
TupleEntity((c0, c1))
}
static func buildBlock<C0, C1, C2>(
_ c0: C0,
_ c1: C1,
_ c2: C2
) -> TupleEntity<(C0, TupleEntity<(C1, C2)>)>
where
C0: EntityRepresentable,
C1: EntityRepresentable,
C2: EntityRepresentable
{
TupleEntity((c0, TupleEntity((c1, c2))))
}
static func buildBlock<C0, C1, C2, C3>(
_ c0: C0,
_ c1: C1,
_ c2: C2,
_ c3: C3
) -> TupleEntity<(C0, TupleEntity<(C1, TupleEntity<(C2, C3)>)>)>
where
C0: EntityRepresentable,
C1: EntityRepresentable,
C2: EntityRepresentable,
C3: EntityRepresentable
{
TupleEntity((c0, TupleEntity((c1, TupleEntity((c2, c3))))))
}
}
// MARK: - Reality
var previous: [AnyEntity] = []
struct Reality<Children: EntityRepresentable>: UIViewRepresentable {
let children: Children
init(@EntityBuilder _ builder: () -> Children) {
self.children = builder()
}
public typealias Body = Never
func makeUIView(context: Context) -> some UIView {
.init()
}
func updateUIView(_ uiView: UIViewType, context: Context) {
var current: [AnyEntity] = []
defer { previous = current }
Mirror.reflectProperties(of: self, recursively: true) {
(child: any EntityRepresentable & Identifiable) in
current.append(AnyEntity(child))
}
let diff = current.difference(from: previous, by: { $0.id == $1.id })
dump(diff)
}
}
// MARK: - Mirror
extension Mirror {
static func reflectProperties<T>(
of target: Any,
matchingType type: T.Type = T.self,
recursively: Bool = false,
using closure: (T) -> Void
) {
let mirror = Mirror(reflecting: target)
for child in mirror.children {
(child.value as? T).map(closure)
if recursively {
// To enable recursive reflection, all we have to do
// is to call our own method again, using the value
// of each child, and using the same closure.
Mirror.reflectProperties(
of: child.value,
recursively: true,
using: closure
)
}
}
}
}
// MARK: - Conformances
struct Anchor<Children: EntityRepresentable>: EntityRepresentable {
let children: Children
init(
@EntityBuilder _ builder: () -> Children = { EmptyEntity() }
) {
self.children = builder()
}
}
struct Model<Children: EntityRepresentable>: EntityRepresentable, Identifiable {
let id: String
let children: Children
init(
_ name: String,
@EntityBuilder _ builder: () -> Children = { EmptyEntity() }
) {
self.children = builder()
self.id = "\(type(of: self))" + "-" + name
}
}
struct AsyncModel<Children: EntityRepresentable>: EntityRepresentable, Identifiable {
let id: String
let children: Children
init(
_ name: String,
@EntityBuilder _ builder: () -> Children = { EmptyEntity() }
) {
self.children = builder()
self.id = "\(type(of: self))" + "-" + name
}
}
// MARK: - Play
struct ContentView: View {
@State private var redraw: Bool = false
var body: some View {
VStack {
Button("Redraw: \(redraw ? "true" : "false")") { redraw.toggle() }
.buttonStyle(.borderedProminent)
Reality {
Anchor {
Model("robot")
Model(redraw ? "boat" : "teapot")
AsyncModel("biplane")
Model("toy")
}
}
}
.padding()
}
}
PlaygroundPage.current.setLiveView(ContentView())
//: [Next](@next)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment