|
27 | 27 | - [KeepAlive](#keepalive) |
28 | 28 | - [Suspense](#suspense) |
29 | 29 | - [Testing](#testing) |
| 30 | + - [Debugging](#debugging) |
30 | 31 | - [Preview](#preview) |
31 | | - - [Observability](#observability) |
32 | 32 | - [Advanced Usage](#advanced-usage) |
33 | 33 | - [Dealing with Known SwiftUI Bugs](#dealing-with-known-swiftui-bugs) |
34 | 34 | - [Contributing](#contributing) |
@@ -1212,57 +1212,75 @@ class FetchBookTests: XCTestCase { |
1212 | 1212 |
|
1213 | 1213 | --- |
1214 | 1214 |
|
1215 | | -### Preview |
| 1215 | +### Debugging |
1216 | 1216 |
|
1217 | | -Even in SwiftUI previews, the view must have an `AtomRoot` somewhere in the ancestor. However, since This library offers the new solution for dependency injection, you don't need to do painful DI each time you create previews anymore. You can to override the atoms that you really want to inject substitutions. |
| 1217 | +This library defines a Directed Acyclic Graph (DAG) internally to centrally manage atom states, making it easy to analyze its dependencies and where they are (or are not) being used. |
| 1218 | +There are the following two ways to get a [Snapshot](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/snapshot) of the dependency graph at a given point in time. |
| 1219 | + |
| 1220 | +The first is to get `Snapshot` through [@ViewContext](#atomviewcontext). This API is suitable for obtaining and analyzing debugging information on demand. |
1218 | 1221 |
|
1219 | 1222 | ```swift |
1220 | | -struct NewsList_Preview: PreviewProvider { |
1221 | | - static var previews: some View { |
1222 | | - AtomRoot { |
1223 | | - NewsList() |
1224 | | - } |
1225 | | - .override(APIClientAtom()) { _ in |
1226 | | - StubAPIClient() |
1227 | | - } |
| 1223 | +@ViewContext |
| 1224 | +var context |
| 1225 | + |
| 1226 | +var debugButton: some View { |
| 1227 | + Button("Dump dependency graph") { |
| 1228 | + let snapshot = context.snapshot() |
| 1229 | + print(snapshot.graphDescription()) |
1228 | 1230 | } |
1229 | 1231 | } |
1230 | 1232 | ``` |
1231 | 1233 |
|
1232 | | ---- |
| 1234 | +Or, you can observe all updates of atoms and always continue to receive `Snapshots` at that point in time through `observe(_:)` modifier of [AtomRoot](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/atomroot) or [AtomRelay](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/atomrelay). |
| 1235 | +Note that observing in `AtomRoot` will receive all atom updates that happened in the whole app, but observing in `AtomRelay` will only receive atoms used in the descendant views. |
1233 | 1236 |
|
1234 | | -### Observability |
| 1237 | +```swift |
| 1238 | +AtomRoot { |
| 1239 | + HomeScreen() |
| 1240 | +} |
| 1241 | +.observe { snapshot in |
| 1242 | + print(snapshot.graphDescription()) |
| 1243 | +} |
| 1244 | +``` |
1235 | 1245 |
|
1236 | | -For debugging, you can observe updates with a snapshot that captures a specific set of values of atoms through the `observe(_:)` function in [AtomRoot](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/atomroot) or [AtomRelay](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/atomrelay). |
1237 | | -Observing in `AtomRoot` will receive all atom updates that happened in the whole app, but observing in `AtomRelay` will only receive atoms used in the descendant views. |
| 1246 | +Calling the [restore()](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/snapshot/restore()) method of the obtained `Snapshot` will roll back to the states and dependency graph at that point in time to see what happened. |
| 1247 | +The debugging technique is called [time travel debugging](https://en.wikipedia.org/wiki/Time_travel_debugging), and the example application [here](Examples/Packages/iOS/Sources/ExampleTimeTravel) demonstrates how it works. |
1238 | 1248 |
|
1239 | | -The [Snapshot](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/snapshot) passed to `observe(:_)` has a [restore()](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/snapshot/restore()) function that can be executed to restore a specific set of atom values. `Snapshot` can also be obtained on-demand through [AtomViewContext](#atomviewcontext). |
1240 | | -This observability API can be applied to do [time travel debugging](https://en.wikipedia.org/wiki/Time_travel_debugging) and is demonstrated in one of the [examples](Examples). |
| 1249 | +In addition, [graphDescription()](https://ra1028.github.io/swiftui-atom-properties/documentation/atoms/snapshot/graphdescription()) method returns a string, that represents the dependencies graph and where they are used, as a String in [graph description language DOT](https://graphviz.org/doc/info/lang.html). |
| 1250 | +This can be converted to an image using [Graphviz](https://graphviz.org), a graph visualization tool, to visually analyze information about the state of the application, as shown below. |
1241 | 1251 |
|
1242 | | -```swift |
1243 | | -@main |
1244 | | -struct ExampleApp: App { |
1245 | | - var body: some Scene { |
1246 | | - WindowGroup { |
1247 | | - AtomRoot { |
1248 | | - VStack { |
1249 | | - NavigationLink("Home") { |
1250 | | - Home() |
1251 | | - } |
| 1252 | +<img src="assets/dependency_graph.png" alt="Dependency Graph" width="50%" align="right"> |
1252 | 1253 |
|
1253 | | - NavigationLink("Setting") { |
1254 | | - AtomRelay { |
1255 | | - Setting() |
1256 | | - } |
1257 | | - .observe { snapshot in // Observes setting related atoms only. |
1258 | | - print(snapshot) |
1259 | | - } |
1260 | | - } |
1261 | | - } |
1262 | | - } |
1263 | | - .observe { snapshot in // Observes all atoms used in the app. |
1264 | | - print(snapshot) |
1265 | | - } |
| 1254 | +```dot |
| 1255 | +digraph { |
| 1256 | + node [shape=box] |
| 1257 | + "FilterAtom" |
| 1258 | + "FilterAtom" -> "TodoApp/FilterPicker.swift" [label="line:3"] |
| 1259 | + "FilterAtom" -> "FilteredTodosAtom" |
| 1260 | + "TodosAtom" |
| 1261 | + "TodosAtom" -> "FilteredTodosAtom" |
| 1262 | + "TodosAtom" -> "StatsAtom" |
| 1263 | + "FilteredTodosAtom" |
| 1264 | + "FilteredTodosAtom" -> "TodoApp/TodoList.swift" [label="line:5"] |
| 1265 | + "TodoApp/TodoList.swift" [style=filled] |
| 1266 | + "TodoApp/FilterPicker.swift" [style=filled] |
| 1267 | +} |
| 1268 | +``` |
| 1269 | + |
| 1270 | +--- |
| 1271 | + |
| 1272 | +### Preview |
| 1273 | + |
| 1274 | +Even in SwiftUI previews, the view must have an `AtomRoot` somewhere in the ancestor. However, since This library offers the new solution for dependency injection, you don't need to do painful DI each time you create previews anymore. You can to override the atoms that you really want to inject substitutions. |
| 1275 | + |
| 1276 | +```swift |
| 1277 | +struct NewsList_Preview: PreviewProvider { |
| 1278 | + static var previews: some View { |
| 1279 | + AtomRoot { |
| 1280 | + NewsList() |
| 1281 | + } |
| 1282 | + .override(APIClientAtom()) { _ in |
| 1283 | + StubAPIClient() |
1266 | 1284 | } |
1267 | 1285 | } |
1268 | 1286 | } |
|
0 commit comments