Skip to content

Instantly share code, notes, and snippets.

@carlynorama
Last active August 6, 2023 18:24
Show Gist options
  • Save carlynorama/3e6765d4a87aaaf3fe2f69abb14764ca to your computer and use it in GitHub Desktop.
Save carlynorama/3e6765d4a87aaaf3fe2f69abb14764ca to your computer and use it in GitHub Desktop.
//
// SwiftUIStyle.swift
//
//
// Created by Carlyn Maw on 8/1/23.
//
import Foundation
//https://talk.objc.io/episodes/S01E225-view-protocols-and-shapes
//https://talk.objc.io/episodes/S01E343-swiftui-style-backend-library
//https://forums.swift.org/t/swiftui-viewbuilder-result-is-a-tupleview-how-is-apple-using-it-and-able-to-avoid-turning-things-into-anyview/28181
//https://github.com/apple/swift-certificates/blob/8debe3f20df931a29d0e5834fd8101fb49feea42/Sources/X509/Verifier/AnyPolicy.swift#L41
//https://forums.swift.org/t/resultbuilder-buildpartialblock-reducing-and-buildarray/66563/18
//Public type "Branch" type
public protocol Layer {
associatedtype Content:Layer
var content: Content { get }
}
//https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself
extension Layer {
//usage:
//let testResult:MyDesiredType = someLayer.testType()
func testType<T>() -> T? {
content as? T
}
}
//Actually a private type, the "Leaf", the type with something to "say" to the renderer
public protocol RenderableLayer {
var id:String { get }
func render()
typealias Content = Never
}
//Default conformance
extension RenderableLayer {
func render() {
print("\(self.id)")
}
}
//Uh-oh. Not passing through correctly in one of the RenderableLayers
public extension Layer where Content == Never {
var content: Never { fatalError("This should never be called.") }
}
//Never as Layer so RenderableLayers can be Layers.
extension Never: Layer {
public var id:String { "Never" }
public typealias Content = Never
}
//"Is it a passthrough or opinionated/leaf type" checker.
public extension Layer {
func _render() {
if let bottom = self as? RenderableLayer {
//print("Found a bottom \(id)")
bottom.render()
} else {
//print("Not yet. \(id)")
content._render()
}
}
}
//-------------------------------------------------------------------------
//MARK: The base builder
@resultBuilder
public enum LayerBuilder {
//Can't be "some Layer" for for loops to work.
// public static func buildPartialBlock<L: Layer>(first: L) -> some Layer {
// first
// }
public static func buildPartialBlock<L: Layer>(first: L) -> L {
first
}
//HAVE TO HAVE THIS if have any other buildExpressions or everything,
//everything gets wrapped as an optional, or array or some such nonsense.
//must be L and not some Layer
static func buildExpression<L:Layer>(_ component: L) -> L {
component
}
public static func buildPartialBlock<L0: Layer, L1: Layer>(accumulated: L0, next: L1) -> some Layer {
Tuple2Layer(first: accumulated, second: next)
}
}
//Data storage type.
struct Tuple2Layer<First:Layer, Second:Layer>: Layer, RenderableLayer {
var id:String { "Tuple" }
var first: First
var second: Second
init(first:First, second:Second) {
self.first = first
self.second = second
}
func render() {
first._render()
second._render()
}
}
//-------------------------------------------------------------------------
//End user types
struct Assembly<Content:Layer>:Layer {
var content: Content
public init(@LayerBuilder content: () -> Content) {
self.content = content()
}
}
struct Circle:Layer, RenderableLayer {
var id:String { "Circle" }
}
struct CircleWithParam:Layer, RenderableLayer {
let radius:Int
var id:String { "Circle with radius \(radius)" }
}
struct Square:Layer, RenderableLayer {
var id:String { "Square" }
}
struct SquareWithParam:Layer, RenderableLayer {
let side:Int
var id:String { "Square with side \(side)" }
}
struct Triangle:Layer, RenderableLayer {
var id:String { "Triangle" }
}
struct Trapezoid:Layer, RenderableLayer {
var id:String { "Trapezoid" }
}
let insert = Assembly {
Triangle()
Triangle()
Triangle()
}
let test = Assembly {
Circle()
Square()
insert
Circle()
Circle()
}
//MARK: Hello World
print(test)
test._render()
//-------------------------------------------------------------------------
//If statement without an else.
extension LayerBuilder {
// buildOptional is only for if's not for actual optionals.
public static func buildOptional<L:Layer>(_ component: L?) -> some Layer {
WrappedOptional(component)
}
//TODO: Test this further. May not have worked in SketchPad.
// public static func buildOptional<L: Layer>(_ component: L?) -> some Layer {
// ArrayLayer(from: component.map { [$0] } ?? [])
// }
}
struct WrappedOptional<Wrapped:Layer>:Layer, RenderableLayer {
var wrapped: Wrapped?
init(_ wrapped: Wrapped?) {
self.wrapped = wrapped
}
var id:String {
if let wrapped {
if let named = wrapped as? RenderableLayer {
return named.id
}
}
return "empty wrapper"
}
func render() {
if let wrapped { wrapped._render() }
}
}
let showCircle = false
let maybe = Assembly {
Square()
if showCircle {
Circle()
Triangle() //See two works.
}
Square()
}
//MARK: If, no else
print(maybe)
maybe._render()
//-------------------------------------------------------------------------
// Function returns a Layer? type.
extension LayerBuilder {
// buildOptional is only for if's not for actual optionals.
static func buildExpression<L:Layer>(_ component: L?) -> ArrayLayer<L> {
ArrayLayer(from: component.map { [$0] } ?? [])
}
}
func returnCircleMaybe() -> Circle? {
let tossUp = false
if tossUp { return Circle() }
else { return nil }
}
//MARK: Optional expression
let depends = Assembly {
Square()
returnCircleMaybe()
Square()
}
//-------------------------------------------------------------------------
// If-Else implementation
extension LayerBuilder {
static func buildEither<First: Layer, Second: Layer>(first component: First) -> _Either<First, Second> {
_Either<First, Second>(storage: .first(component))
}
static func buildEither<First: Layer, Second: Layer>(second component: Second) -> _Either<First, Second> {
_Either<First, Second>(storage: .second(component))
}
}
public struct _Either<First: Layer, Second: Layer>: Layer, RenderableLayer {
enum Storage {
case first(First)
case second(Second)
}
var storage: Storage
init(storage: Storage) {
self.storage = storage
}
public var id: String {
switch storage {
case .first(let storedLayer):
if let named = storedLayer as? RenderableLayer {
return named.id
}
case .second(let storedLayer):
if let named = storedLayer as? RenderableLayer {
return named.id
}
}
return "either"
}
public func render() {
switch storage {
case .first(let storedLayer):
storedLayer._render()
case .second(let storedLayer):
storedLayer._render()
}
}
}
let which = Assembly {
Square()
if showCircle {
Circle()
Triangle()
} else {
Triangle()
Circle()
}
Square()
}
//MARK: If with else
print(which)
which._render()
//-------------------------------------------------------------------------
// Basic for loop with ONE AND ONLY ONE Layer in it.
struct ArrayLayer<Element:Layer>:Layer, RenderableLayer {
var id: String { "ArrayLayer" }
var elements: [Element]
public init(from elements:[Element]) {
self.elements = elements
}
func render() {
for element in elements {
element._render()
}
}
}
extension LayerBuilder {
static func buildArray<L:Layer>(_ components: [L]) -> ArrayLayer<L> {
ArrayLayer(from: components)
}
}
let singularType = Assembly {
Square()
for i in 0...20 {
Circle()
//if i > 5 { break }
}
//ERROR that shows up when builder gets confused:
//Generic parameter 'τ_1_0' could not be inferred,
//beta 15.5: Underlying type for opaque result type 'some Layer' could not be inferred from return expression
Square()
}
////MARK: Loop Attempt
print(singularType)
singularType._render()
//-------------------------------------------------------------------------
// Accept arrays not in for-loops.
extension LayerBuilder {
//These make an array work.
static func buildExpression<L:Layer>(_ expression: [L]) -> ArrayLayer<L> {
ArrayLayer(from: expression)
}
}
//
func spewCircles() -> [Circle] {
var array:[Circle] = []
for _ in 0...3 {
array.append(Circle())
}
return array
}
let singularTypeArray = Assembly {
Square()
[Circle(), Circle(), Circle()]
spewCircles()
Square()
}
//
//MARK: Explicit Arrays
print(singularTypeArray)
singularTypeArray._render()
//
//
//-------------------------------------------------------------------------
//Heterogeneous Loop
//TODO: Is there a way, and is it desirable, to have the init instead make a TupleStack so that the storage is the same texture as other storage?
struct Repeating<Content:Layer>:Layer, RenderableLayer {
var id: String { "Repeating" }
//var count:Int
var elements: [Content]
public init(count:Int, @LayerBuilder content: (Int) -> Content) {
self.elements = []
for index in 0..<count {
self.elements.append(content(index))
}
}
func render() {
for element in elements {
element._render()
}
}
}
let multiType = Assembly {
Square()
Repeating(count: 3) { index in
let adjusted = index+2
CircleWithParam(radius: adjusted*3)
SquareWithParam(side: adjusted*2)
}
Circle()
}
//MARK: Heterogeneous Loop Attempt
print(multiType)
multiType._render()
//TODO: Write using sequence?
//https://stackoverflow.com/questions/60461796/swift-way-to-c-style-for-loop-that-alters-initial-variable
struct IndexLoop<Content:Layer>:Layer, RenderableLayer {
var id: String { "Repeating" }
//var count:Int
var elements: [Content]
public init(from start:Int = 0, to limit:Int, by increment:Int = 1, @LayerBuilder content: (Int) -> Content) {
self.elements = []
for index in stride(from: start, to: limit, by: increment) {
self.elements.append(content(index))
}
}
func render() {
for element in elements {
element._render()
}
}
}
let multiType2 = Assembly {
Square()
IndexLoop(to: 3) { index in
let adjusted = index+2
CircleWithParam(radius: adjusted*3)
SquareWithParam(side: adjusted*2)
}
Circle()
}
//MARK: For Loop <3
print(multiType2)
multiType2._render()
//-------------------------------------------------------------------------
// AnyLayer
//TODO: Actually make sure really needed before move to SketchPad
//Super class that does not have a generic dependency
class AnyLayerBase: RenderableLayer {
var id: String { fatalError("AnyLayerBase should never call id") }
func render() { fatalError("AnyLayerBase should never call render") }
}
//Subclass that does know what it's generic is.
final class AnyLayerImpl<L: Layer> : AnyLayerBase {
let layer: L
init(_ layer: L) {
self.layer = layer
}
override var id: String {
if let tested = layer as? RenderableLayer {
return tested.id
} else {
return "AnyLayerImpl"
}
}
override func render() {
if let tested = layer as? RenderableLayer {
tested
} else {
layer._render()
}
}
}
//struct that can use the features of the superclass/Protocol without
//having a generic as part of its static type
struct AnyLayer: Layer, RenderableLayer {
let wrapped: AnyLayerBase
init<L: Layer>(_ layer: L) {
self.wrapped = AnyLayerImpl(layer)
}
var id:String { wrapped.id }
//Breaks it. Leave out and it works.
//func render() { wrapped.render() }
}
let erased = Assembly {
AnyLayer(Circle())
AnyLayer(Trapezoid())
}
//MARK: AnyLayer
print(erased)
erased._render()
//MARK: Still not used yet
//struct EmptyLayer:Layer, RenderableLayer {
//var id: String = "Empty"
//func render() { return }
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment