Skip to content

Commit 5fedbf7

Browse files
committed
Add comprehensive swift-docc documentation and architecture guide
- Update README.md with detailed architecture explanation covering Attribute, Graph, and Runtime components - Create swift-docc documentation catalog with main module documentation - Add in-depth Architecture.md article explaining the three-layer system - Document key public APIs including: * Attribute<Value> struct with property wrapper documentation * Rule protocol for computed attributes * Metadata extensions for runtime type introspection - Reference wickwirew/Runtime repo for Swift runtime techniques - Include code examples and usage patterns throughout documentation
1 parent 9792105 commit 5fedbf7

File tree

6 files changed

+435
-0
lines changed

6 files changed

+435
-0
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@ AttributeGraph is a high performance computing engine written in C++ and Swift.
1212

1313
And it powers the underlying computing and diffing of SwiftUI.
1414

15+
## Architecture
16+
17+
OpenAttributeGraph consists of three main components:
18+
19+
### Attribute Part
20+
The **Attribute** system provides a reactive property wrapper that automatically tracks dependencies and manages value updates. Key features include:
21+
- `Attribute<Value>` - A property wrapper for reactive values with automatic dependency tracking
22+
- `AnyAttribute` - Type-erased attribute for runtime flexibility
23+
- **Rules** - Transform attributes through `Rule` and `StatefulRule` protocols
24+
- **Weak/Optional** - Support for optional and weak attribute references
25+
- **Body system** - Efficient value computation and caching mechanisms
26+
27+
### Graph Part
28+
The **Graph** manages the dependency network and orchestrates updates across attributes. Core functionality includes:
29+
- `Graph` - Central coordinator for attribute relationships and update cycles
30+
- `Subgraph` - Scoped computation contexts for isolated attribute groups
31+
- **Invalidation tracking** - Efficient change propagation through the dependency graph
32+
- **Update scheduling** - Optimized batch processing of attribute changes
33+
- **Profiling support** - Performance monitoring and debugging capabilities
34+
35+
### Runtime Part
36+
The **Runtime** provides low-level type introspection and memory management utilities. This includes:
37+
- `Metadata` - Swift runtime type information and reflection capabilities
38+
- **Type comparison** - Efficient value equality checking across different types
39+
- **Memory layout** - Safe pointer manipulation and offset calculations
40+
- **Tuple handling** - Runtime support for tuple types and field access
41+
42+
> **Note**: The Runtime part leverages techniques similar to those found in [wickwirew/Runtime](https://github.com/wickwirew/Runtime) for Swift runtime introspection.
43+
1544
| **CI Status** |
1645
|---|
1746
|[![Compatibility tests](https://github.com/OpenSwiftUIProject/OpenAttributeGraph/actions/workflows/compatibility_tests.yml/badge.svg)](https://github.com/OpenSwiftUIProject/OpenAttributeGraph/actions/workflows/compatibility_tests.yml)|

Sources/OpenAttributeGraph/Attribute/Attribute/Attribute.swift

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,67 @@
11
public import OpenAttributeGraphCxx
22

3+
/// A reactive property wrapper that automatically tracks dependencies and manages value updates.
4+
///
5+
/// `Attribute` is the core building block of the OpenAttributeGraph reactive system. When you wrap a property
6+
/// with `@Attribute`, it becomes reactive and can automatically track dependencies and propagate changes.
7+
///
8+
/// ```swift
9+
/// @Attribute var count: Int = 0
10+
/// @Attribute var doubledCount: Int = count * 2
11+
///
12+
/// count = 5 // doubledCount automatically becomes 10
13+
/// ```
14+
///
15+
/// ## Key Features
16+
///
17+
/// - **Automatic dependency tracking**: Attributes automatically discover their dependencies
18+
/// - **Efficient updates**: Only affected attributes are recomputed when changes occur
19+
/// - **Type safety**: Full Swift type safety with compile-time checking
20+
/// - **Dynamic member lookup**: Access nested properties as reactive attributes
21+
/// - **Property wrapper syntax**: Clean, declarative syntax using `@Attribute`
22+
///
23+
/// ## Property Wrapper Usage
24+
///
25+
/// Use `@Attribute` to make any Swift value reactive:
26+
///
27+
/// ```swift
28+
/// struct CounterView {
29+
/// @Attribute var count: Int = 0
30+
///
31+
/// var body: some View {
32+
/// Button("Count: \(count)") {
33+
/// count += 1
34+
/// }
35+
/// }
36+
/// }
37+
/// ```
38+
///
39+
/// ## Dynamic Member Lookup
40+
///
41+
/// Access nested properties as separate attributes:
42+
///
43+
/// ```swift
44+
/// @Attribute var person: Person = Person(name: "Alice", age: 30)
45+
/// let nameAttribute: Attribute<String> = person.name
46+
/// let ageAttribute: Attribute<Int> = person.age
47+
/// ```
48+
///
49+
/// ## Integration with Rules
50+
///
51+
/// Create computed attributes using ``Rule`` or ``StatefulRule``:
52+
///
53+
/// ```swift
54+
/// struct DoubledRule: Rule {
55+
/// typealias Value = Int
56+
/// let source: Attribute<Int>
57+
///
58+
/// func value() -> Int {
59+
/// source.wrappedValue * 2
60+
/// }
61+
/// }
62+
///
63+
/// let doubled = Attribute(DoubledRule(source: count))
64+
/// ```
365
@frozen
466
@propertyWrapper
567
@dynamicMemberLookup
@@ -8,14 +70,30 @@ public struct Attribute<Value> {
870

971
// MARK: - Initializer
1072

73+
/// Creates an attribute from a type-erased identifier.
74+
///
75+
/// - Parameter identifier: The type-erased attribute identifier
1176
public init(identifier: AnyAttribute) {
1277
self.identifier = identifier
1378
}
1479

80+
/// Creates an attribute by copying another attribute.
81+
///
82+
/// - Parameter attribute: The attribute to copy
1583
public init(_ attribute: Attribute<Value>) {
1684
self = attribute
1785
}
1886

87+
/// Creates an attribute with an initial value.
88+
///
89+
/// This initializer creates an external attribute that holds the provided value.
90+
/// External attributes are typically used for storing user input or initial state.
91+
///
92+
/// ```swift
93+
/// let count = Attribute(value: 42)
94+
/// ```
95+
///
96+
/// - Parameter value: The initial value for the attribute
1997
public init(value: Value) {
2098
self = withUnsafePointer(to: value) { valuePointer in
2199
withUnsafePointer(to: External<Value>()) { bodyPointer in
@@ -61,6 +139,18 @@ public struct Attribute<Value> {
61139

62140
// MARK: - propertyWrapper
63141

142+
/// The current value of the attribute.
143+
///
144+
/// When used as a property wrapper with `@Attribute`, this provides the underlying value.
145+
/// Getting this value may trigger dependency tracking in the current evaluation context.
146+
/// Setting this value will update the attribute and invalidate any dependents.
147+
///
148+
/// ```swift
149+
/// @Attribute var count: Int = 0
150+
///
151+
/// print(count) // Gets wrappedValue, returns 0
152+
/// count = 5 // Sets wrappedValue, triggers updates
153+
/// ```
64154
public var wrappedValue: Value {
65155
unsafeAddress {
66156
OAGGraphGetValue(identifier, type: Value.self)
@@ -70,6 +160,18 @@ public struct Attribute<Value> {
70160
nonmutating set { _ = setValue(newValue) }
71161
}
72162

163+
/// The attribute itself when accessed with the `$` prefix.
164+
///
165+
/// This provides access to the attribute object itself rather than its value,
166+
/// allowing you to pass the reactive attribute to other functions or create
167+
/// derived attributes.
168+
///
169+
/// ```swift
170+
/// @Attribute var count: Int = 0
171+
///
172+
/// let countAttribute = $count // Gets the Attribute<Int> object
173+
/// let doubled = countAttribute.map { $0 * 2 }
174+
/// ```
73175
public var projectedValue: Attribute<Value> {
74176
get { self }
75177
set { self = newValue }

Sources/OpenAttributeGraph/Attribute/Rule/Rule.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,70 @@
77

88
public import OpenAttributeGraphCxx
99

10+
/// A protocol for defining computed attributes that automatically update when dependencies change.
11+
///
12+
/// Rules provide a way to create derived attributes that compute their values based on other attributes.
13+
/// When any dependency changes, the rule will automatically recompute its value.
14+
///
15+
/// ```swift
16+
/// struct DoubledRule: Rule {
17+
/// typealias Value = Int
18+
/// let source: Attribute<Int>
19+
///
20+
/// var value: Int {
21+
/// source.wrappedValue * 2
22+
/// }
23+
/// }
24+
///
25+
/// @Attribute var count: Int = 5
26+
/// let doubled = Attribute(DoubledRule(source: $count))
27+
/// // doubled.wrappedValue == 10
28+
///
29+
/// count = 10
30+
/// // doubled.wrappedValue automatically becomes 20
31+
/// ```
32+
///
33+
/// ## Key Features
34+
///
35+
/// - **Automatic dependency tracking**: Dependencies are discovered automatically when accessed
36+
/// - **Lazy evaluation**: Values are only computed when needed
37+
/// - **Caching**: Results are cached until dependencies change
38+
/// - **Efficient updates**: Only recomputes when dependencies actually change
39+
///
40+
/// ## Implementation Requirements
41+
///
42+
/// Types conforming to `Rule` must provide:
43+
/// - `Value`: The type of value produced by the rule
44+
/// - `value`: A computed property that returns the current value
45+
/// - `initialValue`: An optional initial value (defaults to `nil`)
46+
///
47+
/// ## Advanced Usage
48+
///
49+
/// For rules that need to maintain state between evaluations, see ``StatefulRule``.
50+
/// For rules that can be cached based on their content, make your rule type conform to `Hashable`.
1051
public protocol Rule: _AttributeBody {
52+
/// The type of value produced by this rule.
1153
associatedtype Value
54+
55+
/// An optional initial value to use before the rule is first evaluated.
56+
///
57+
/// If `nil`, the rule will be evaluated immediately when the attribute is created.
58+
/// If non-`nil`, this value will be used initially, and the rule will be evaluated
59+
/// on the next update cycle.
1260
static var initialValue: Value? { get }
61+
62+
/// Computes and returns the current value of the rule.
63+
///
64+
/// This property should access any attributes or other dependencies needed to compute
65+
/// the result. The attribute graph will automatically track these dependencies and
66+
/// invalidate this rule when any dependency changes.
67+
///
68+
/// ```swift
69+
/// var value: Int {
70+
/// // Dependencies are automatically tracked
71+
/// return source1.wrappedValue + source2.wrappedValue
72+
/// }
73+
/// ```
1374
var value: Value { get }
1475
}
1576

0 commit comments

Comments
 (0)