Initial Commit

This commit is contained in:
Signal 2026-08-03 15:05:47 -04:00
commit e1463eb256
4 changed files with 146 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

24
Package.swift Normal file
View file

@ -0,0 +1,24 @@
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "ArtifactState",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "ArtifactState",
targets: ["ArtifactState"]),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "ArtifactState"),
.testTarget(
name: "ArtifactStateTests",
dependencies: ["ArtifactState"]
),
]
)

View file

@ -0,0 +1,102 @@
actor ObservationState {
public static var observers: [[any BindingProtocol]] = []
}
public func observing<T>(into: inout [any BindingProtocol], body: () -> T) -> T {
ObservationState.observers.append([])
defer { into = ObservationState.observers.popLast()! }
return body()
}
public protocol BindingProtocol {
func notify()
func subscribe(key: ObjectIdentifier, handler: @escaping () -> Void)
func unsubscribe(key: ObjectIdentifier)
}
public class Binding<T: Equatable>: BindingProtocol {
public private(set) var listeners: [ObjectIdentifier: () -> Void] = [:]
public var wrappedValue: T { fatalError() }
public func notify() {
listeners.forEach { $1() }
}
public func subscribe(key: ObjectIdentifier, handler: @escaping () -> Void) {
listeners[key] = handler
}
public func unsubscribe(key: ObjectIdentifier) {
listeners.removeValue(forKey: key)
}
}
@propertyWrapper
public class State<T: Equatable>: Binding<T> {
public var projectedValue: State<T> { self }
private var value: T
public override var wrappedValue: T {
get {
if ObservationState.observers.count > 0 {
ObservationState.observers[ObservationState.observers.count - 1].append(self)
}
return value
}
set {
if value != newValue {
value = newValue
notify()
}
}
}
public init(wrappedValue: T) {
self.value = wrappedValue
}
}
public class DerivedState<T: Equatable>: Binding<T> {
private var value: T
public override var wrappedValue: T {
get {
if ObservationState.observers.count > 0 {
ObservationState.observers[ObservationState.observers.count - 1].append(self)
}
return value
}
}
var deps: [any BindingProtocol] = []
var derivation: () -> T
private var id: ObjectIdentifier!
public init(_ derivation: @escaping () -> T) {
self.derivation = derivation
value = observing(into: &deps) {
derivation()
}
super.init()
id = ObjectIdentifier(self)
deps.forEach {
$0.subscribe(key: id) { [self] in
recompute()
}
}
}
deinit {
deps.forEach { $0.unsubscribe(key: id) }
}
func recompute() {
deps.forEach { $0.unsubscribe(key: id) }
value = observing(into: &deps) {
derivation()
}
deps.forEach {
$0.subscribe(key: id) { [self] in
recompute()
}
}
}
}

View file

@ -0,0 +1,12 @@
import XCTest
@testable import ArtifactState
final class ArtifactStateTests: XCTestCase {
func testExample() throws {
// XCTest Documentation
// https://developer.apple.com/documentation/xctest
// Defining Test Cases and Test Methods
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
}
}