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.Rendered
struct - 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
Document
with 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
Rendered
struct, 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 NodeRefs
Update 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