View Source Updates
Whenever the template changes, a diff event is sent to the client. liveview-native-core handles most of this process.
Update Process
- When the page loads, Phoenix sends a JSON representation of the
Phoenix.LiveView.Renderedstruct - The client parses this data, and forms a complete HTML document. It passes this HTML string to core to be parsed.
- Core sends back a
Documentwith parsed nodes and elements, which the client renders to native elements - The client sends an event in response to some user interaction
- The LiveView computes the changes, and sends a fragment diff to the client.
- The client merges this fragment diff with the previous
Renderedstruct, and sends the complete HTML to core to parse. - Core returns a parsed document.
- The client asks core to merge the new document into the old document so they match.
- Core sends the ID of any changed nodes to the client to update.
sequenceDiagram
LiveView ->> Client: 1. Rendered JSON
Client ->> Core: 2. Parse HTML
Core ->> Client: 3. Document
Client ->> LiveView: 4. Event "increment"
LiveView ->> Client: 5. Rendered Diff
Client ->> Core: 6. Parse New HTML
Client ->> Core: 7. New Document
Client ->> Core: 8. Merge Documents
Core ->> Client: 9. Changed NodeRefsUpdate Isolation
To keep updates fast, individual Views are updated based on the elements that change between a diff.
The LiveViewCoordinator stores a dictionary that maps a NodeRef to a Combine publisher.
Whenever core sends an update event for this NodeRef, a signal is sent through the corresponding publisher to all of its subscribers.
@ObservedElement
The ObservedElement property wrapper watches for changes to the node, and triggers a View update when change events are received.
This is done through the Observer class. It subscribes to the Combine publisher for the matching NodeRef.
One Observer instance is created for each View to avoid redundant subscriptions in the case of multiple ObservedElement wrappers (common when using @Attribute).
flowchart TD
subgraph LiveViewCoordinator
LVC[LiveViewCoordinator]
LVC --> |node #1 changed| VP1[Node #1 Publisher]
LVC --> VP2[Node #2 Publisher]
LVC --> VP3[Node #... Publisher]
end
VP1 -.-> |node #1 changed| O
subgraph Environment
O[Observer]
end
subgraph Node #1 SwiftUI View
OE1["@ObservedElement"]
A1["@Attribute"]
A2["@Attribute"]
A1 --> OE2["@ObservedElement"]
A2 --> OE3["@ObservedElement"]
end
O -.-> OE1
O -.-> OE2
O -.-> OE3