Skip to content

Commit 9cd2654

Browse files
authored
Add comprehensive swift-docc documentation and architecture guide (#183)
1 parent 75309ed commit 9cd2654

33 files changed

+1209
-7
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ TODO.md
1111
.ag_template/
1212
build/
1313
.ag_repo/
14-
.og_repo/
14+
.oag_repo/
15+
.claude

.spi.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
version: 1
22
builder:
33
configs:
4-
- swift_version: 6.0
5-
documentation_targets: [OpenAttributeGraph]
4+
- documentation_targets: [OpenAttributeGraph]

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
[![codecov](https://codecov.io/gh/OpenSwiftUIProject/OpenAttributeGraph/graph/badge.svg?token=W1KDSUMWJW)](https://codecov.io/gh/OpenSwiftUIProject/OpenAttributeGraph)
88

9-
OpenAttributeGraph is an open source implementation of Apple's Private framework - AttributeGraph
9+
OpenAttributeGraph is an open source implementation of Apple's Private framework - AttributeGraph which is a high performance computing engine written in C++ and Swift.
1010

11-
AttributeGraph is a high performance computing engine written in C++ and Swift.
12-
13-
And it powers the underlying computing and diffing of SwiftUI.
11+
And it powers the underlying computing and diffing of [OpenSwiftUI](https://github.com/OpenSwiftUIProject/OpenSwiftUI).
1412

1513
| **CI Status** |
1614
|---|
@@ -25,6 +23,8 @@ The project is for the following purposes:
2523

2624
Currently, this project is in early development.
2725

26+
Please refer to the [documentation](https://swiftpackageindex.com/OpenSwiftUIProject/OpenAttributeGraph/main/documentation/openattributegraph) for more information on it.
27+
2828
## Usage
2929

3030
### Via Swift Package Manager

Sources/OpenAttributeGraph/Attribute/Attribute/Attribute.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,60 @@
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
6+
/// property with `@Attribute`, it becomes reactive and can automatically track dependencies and
7+
/// propagate changes.
8+
///
9+
/// @Attribute var count: Int = 0
10+
/// @Attribute var doubledCount: Int = count * 2
11+
///
12+
/// count = 5 // doubledCount automatically becomes 10
13+
///
14+
/// ## Key Features
15+
///
16+
/// - Automatic dependency tracking: Attributes automatically discover their dependencies
17+
/// - Efficient updates: Only affected attributes are recomputed when changes occur
18+
/// - Type safety: Full Swift type safety with compile-time checking
19+
/// - Dynamic member lookup: Access nested properties as reactive attributes
20+
/// - Property wrapper syntax: Clean, declarative syntax using `@Attribute`
21+
///
22+
/// ## Property Wrapper Usage
23+
///
24+
/// Use `@Attribute` to make any Swift value reactive:
25+
///
26+
/// struct CounterView {
27+
/// @Attribute var count: Int = 0
28+
///
29+
/// var body: some View {
30+
/// Button("Count: \(count)") {
31+
/// count += 1
32+
/// }
33+
/// }
34+
/// }
35+
///
36+
/// ## Dynamic Member Lookup
37+
///
38+
/// Access nested properties as separate attributes:
39+
///
40+
/// @Attribute var person: Person = Person(name: "Alice", age: 30)
41+
/// let nameAttribute: Attribute<String> = person.name
42+
/// let ageAttribute: Attribute<Int> = person.age
43+
///
44+
/// ## Integration with Rules
45+
///
46+
/// Create computed attributes using ``Rule`` or ``StatefulRule``:
47+
///
48+
/// struct DoubledRule: Rule {
49+
/// typealias Value = Int
50+
/// let source: Attribute<Int>
51+
///
52+
/// func value() -> Int {
53+
/// source.wrappedValue * 2
54+
/// }
55+
/// }
56+
///
57+
/// let doubled = Attribute(DoubledRule(source: count))
358
@frozen
459
@propertyWrapper
560
@dynamicMemberLookup
@@ -8,14 +63,23 @@ public struct Attribute<Value> {
863

964
// MARK: - Initializer
1065

66+
/// Creates an attribute from a type-erased identifier.
67+
///
68+
/// - Parameter identifier: The type-erased attribute identifier
1169
public init(identifier: AnyAttribute) {
1270
self.identifier = identifier
1371
}
1472

73+
/// Creates an attribute by copying another attribute.
74+
///
75+
/// - Parameter attribute: The attribute to copy
1576
public init(_ attribute: Attribute<Value>) {
1677
self = attribute
1778
}
1879

80+
/// Creates an attribute with an initial value.
81+
///
82+
/// - Parameter value: The initial value for the attribute
1983
public init(value: Value) {
2084
self = withUnsafePointer(to: value) { valuePointer in
2185
withUnsafePointer(to: External<Value>()) { bodyPointer in
@@ -61,6 +125,7 @@ public struct Attribute<Value> {
61125

62126
// MARK: - propertyWrapper
63127

128+
/// The current value of the attribute.
64129
public var wrappedValue: Value {
65130
unsafeAddress {
66131
OAGGraphGetValue(identifier, type: Value.self)
@@ -70,6 +135,7 @@ public struct Attribute<Value> {
70135
nonmutating set { _ = setValue(newValue) }
71136
}
72137

138+
/// The attribute itself when accessed with the `$` prefix.
73139
public var projectedValue: Attribute<Value> {
74140
get { self }
75141
set { self = newValue }

Sources/OpenAttributeGraph/Attribute/Optional/OptionalAttribute.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Audited for RELEASE_2021
66
// Status: Complete
77

8+
/// An optional attribute wrapper that may or may not contain a value.
89
@frozen
910
@propertyWrapper
1011
@dynamicMemberLookup

Sources/OpenAttributeGraph/Attribute/Rule/Focus.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,35 @@
55
// Audited for RELEASE_2021
66
// Status: Complete
77

8+
/// A rule that focuses on a specific property of another attribute using KeyPath.
9+
///
10+
/// `Focus` provides a way to create attributes that automatically track a specific property of another attribute. It uses Swift's KeyPath system to create a focused view of part of a larger data structure.
11+
///
12+
/// struct Person {
13+
/// let name: String
14+
/// let age: Int
15+
/// }
16+
///
17+
/// @Attribute var person = Person(name: "Alice", age: 30)
18+
/// let nameAttribute = Attribute(Focus(root: $person, keyPath: \.name))
19+
/// // nameAttribute automatically updates when person.name changes
20+
///
21+
/// Focus is commonly used internally by OpenAttributeGraph's dynamic member lookup system to create property-specific attributes.
22+
///
23+
/// ## Key Features
24+
///
25+
/// - KeyPath-based focusing: Use any Swift KeyPath to focus on specific properties
26+
/// - Automatic updates: Changes to the focused property automatically propagate
27+
/// - Type safety: Full compile-time type checking for focused properties
28+
/// - Efficient tracking: Only tracks changes to the specific focused property
29+
///
30+
/// ## Usage Pattern
31+
///
32+
/// Focus is ideal for:
33+
/// - Creating focused views of complex data structures
34+
/// - Implementing dynamic member lookup for attributes
35+
/// - Breaking down large objects into smaller, more manageable attribute pieces
36+
/// - Selective observation of specific properties
837
@frozen
938
public struct Focus<Root, Value>: Rule, CustomStringConvertible {
1039
public var root: Attribute<Root>

Sources/OpenAttributeGraph/Attribute/Rule/Rule.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,53 @@
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+
/// struct DoubledRule: Rule {
16+
/// typealias Value = Int
17+
/// let source: Attribute<Int>
18+
///
19+
/// var value: Int {
20+
/// source.wrappedValue * 2
21+
/// }
22+
/// }
23+
///
24+
/// @Attribute var count: Int = 5
25+
/// let doubled = Attribute(DoubledRule(source: $count))
26+
/// // doubled.wrappedValue == 10
27+
///
28+
/// count = 10
29+
/// // doubled.wrappedValue automatically becomes 20
30+
///
31+
/// ## Key Features
32+
///
33+
/// - Automatic dependency tracking: Dependencies are discovered automatically when accessed
34+
/// - Lazy evaluation: Values are only computed when needed
35+
/// - Caching: Results are cached until dependencies change
36+
/// - Efficient updates: Only recomputes when dependencies actually change
37+
///
38+
/// ## Implementation Requirements
39+
///
40+
/// Types conforming to `Rule` must provide:
41+
/// - `Value`: The type of value produced by the rule
42+
/// - `value`: A computed property that returns the current value
43+
/// - `initialValue`: An optional initial value (defaults to `nil`)
44+
///
45+
/// ## Advanced Usage
46+
///
47+
/// For rules that need to maintain state between evaluations, see ``StatefulRule``.
48+
/// For rules that can be cached based on their content, make your rule type conform to `Hashable`.
1049
public protocol Rule: _AttributeBody {
50+
/// The type of value produced by this rule.
1151
associatedtype Value
52+
53+
/// An optional initial value to use before the rule is first evaluated.
1254
static var initialValue: Value? { get }
55+
56+
/// Computes and returns the current value of the rule.
1357
var value: Value { get }
1458
}
1559

Sources/OpenAttributeGraph/Attribute/Rule/StatefulRule.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@
77

88
public import OpenAttributeGraphCxx
99

10+
/// A protocol for defining computed attributes that maintain state between evaluations.
11+
///
12+
/// `StatefulRule` extends the basic `Rule` concept by allowing rules to maintain mutable state between updates. This is useful for rules that need to track changes over time or maintain internal state.
13+
///
14+
/// struct CounterRule: StatefulRule {
15+
/// typealias Value = Int
16+
/// private var counter = 0
17+
///
18+
/// mutating func updateValue() {
19+
/// counter += 1
20+
/// value = counter
21+
/// }
22+
/// }
23+
///
24+
/// Unlike ``Rule``, `StatefulRule` allows mutation through the `updateValue()` method and provides direct access to the current value through the `value` property.
25+
///
26+
/// ## Key Features
27+
///
28+
/// - Mutable state: Rules can maintain and modify internal state
29+
/// - Direct value access: Read and write the current value directly
30+
/// - Context access: Access to rule evaluation context and attribute
31+
/// - Lifecycle control: Manual control over when and how values update
32+
///
33+
/// ## Usage Pattern
34+
///
35+
/// StatefulRule is ideal for scenarios where you need to:
36+
/// - Accumulate values over time
37+
/// - Maintain counters or timers
38+
/// - Implement complex state machines
39+
/// - Cache expensive computations with custom invalidation
1040
public protocol StatefulRule: _AttributeBody {
1141
associatedtype Value
1242
static var initialValue: Value? { get }

Sources/OpenAttributeGraph/Attribute/RuleContext/RuleContext.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
public import OpenAttributeGraphCxx
99

10+
/// Context object providing access to rule evaluation state and input values.
1011
@frozen
1112
public struct RuleContext<Value>: Equatable {
1213
public var attribute: Attribute<Value>

Sources/OpenAttributeGraph/Attribute/Weak/WeakAttribute.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@
77

88
public import OpenAttributeGraphCxx
99

10+
/// A weak reference property wrapper for attributes that prevents retain cycles.
11+
///
12+
/// `WeakAttribute` provides a way to hold weak references to attributes, preventing strong reference cycles in the attribute graph while still allowing access to reactive values.
13+
///
14+
/// @WeakAttribute var parentAttribute: SomeType?
15+
///
16+
/// // Safe access to potentially deallocated attribute
17+
/// if let value = parentAttribute {
18+
/// print("Parent value: \(value)")
19+
/// }
20+
///
21+
/// The weak attribute automatically becomes `nil` when the referenced attribute is deallocated, providing memory-safe access to optional attribute references.
22+
///
23+
/// ## Key Features
24+
///
25+
/// - Weak references: Prevents retain cycles in attribute relationships
26+
/// - Automatic nil assignment: Referenced attributes become nil when deallocated
27+
/// - Dynamic member lookup: Access nested properties through weak references
28+
/// - Optional semantics: All values are optional since references may be deallocated
29+
///
30+
/// ## Usage Pattern
31+
///
32+
/// WeakAttribute is essential for:
33+
/// - Parent-child attribute relationships
34+
/// - Observer patterns that don't own the observed attribute
35+
/// - Breaking potential retain cycles in complex attribute graphs
36+
/// - Optional attribute references in data structures
1037
@frozen
1138
@propertyWrapper
1239
@dynamicMemberLookup

0 commit comments

Comments
 (0)