Skip to content

Instantly share code, notes, and snippets.

@markmals
Last active June 15, 2023 03:02
Show Gist options
  • Save markmals/6106c1e77b3d012057331339d0a0d6c9 to your computer and use it in GitHub Desktop.
Save markmals/6106c1e77b3d012057331339d0a0d6c9 to your computer and use it in GitHub Desktop.
My ideal Swift Macro version of a Solid.js-like renderer for UIKit via Swift
import SwiftSignal
import FineGrainedViews
// Macro usage:
final class CounterViewController: UIViewController {
private let count = Signal(0)
override func viewDidLoad() {
super.viewDidLoad()
self.view = #FineGrainedUIView {
UIStackView(axis: .vertical) {
UIButton(String(count.value)) {
count.value += 1
}
.tintColor(count.value % 2 ? .systemRed : nil)
}
}
}
}
// Generated extensions using a Swift package plugin transforms
// the official UIKit API into its declarative counterpart
// as non-working extensions with the goal API.
// These APIs never actually get called in compiled code.
extension UIStackView {
init(axis: NSLayoutConstraint.Axis, @FineGrainedUIViewBuilder builder: () -> [UIView]) {
fatalError()
}
}
extension UIButton {
init(_ title: String, _ action: () -> Void) {
fatalError()
}
func tintColor(_ color: UIColor) -> Self {
fatalError()
}
}
// The macro generates:
final class CounterViewController: UIViewController {
private let count = Signal(0)
override func viewDidLoad() {
super.viewDidLoad()
self.view = {
let button = UIButton(
primaryAction: UIAction { [unowned self] _ in
self.count.value += 1
}
)
let stackView = UIStackView(arrangedSubviews: [button])
stackView.axis = .vertical
effect {
button.setTitle(String(count.value))
button.tintColor = count.value % 2 ? .systemRed : nil
}
return stackView
}()
}
}
// Macro usage:
@Component
class MyComponent {
let count = Signal(0)
var view: UIView {
UIStackView(axis: .vertical) {
UIButton(String(count.get())) {
count.update { $0 + 1 }
}
.tintColor(count.get() % 2 ? .systemRed : nil)
}
}
}
// Generated extensions using a Swift package plugin transforms
// the official UIKit API into its declarative counterpart
// as non-working extensions with the goal API.
// These APIs never actually get called in compiled code.
extension UIStackView {
init(axis: NSLayoutConstraint.Axis, @ComponentBuilder builder: () -> [UIView]) {
fatalError()
}
}
extension UIButton {
init(_ title: String, _ action: () -> Void) {
fatalError()
}
func tintColor(_ color: UIColor) -> Self {
fatalError()
}
}
// The macro generates:
class MyComponent {
let ref: EffectRef?
let count = Signal(0)
var view: UIView {
let button = UIButton(
primaryAction: UIAction { [unowned self] _ in
self.count.update { $0 + 1 }
}
)
let stackView = UIStackView(arrangedSubviews: [button])
stackView.axis = .vertical
ref = effect {
button.setTitle(String(count.get()))
button.tintColor = count.get() % 2 ? .systemRed : nil
}
return stackView
}
deinit() {
ref?.destroy()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment