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

  1. When the page loads, Phoenix sends a JSON representation of the Phoenix.LiveView.Rendered struct
  2. The client parses this data, and forms a complete HTML document. It passes this HTML string to core to be parsed.
  3. Core sends back a Document with parsed nodes and elements, which the client renders to native elements
  4. The client sends an event in response to some user interaction
  5. The LiveView computes the changes, and sends a fragment diff to the client.
  6. The client merges this fragment diff with the previous Rendered struct, and sends the complete HTML to core to parse.
  7. Core returns a parsed document.
  8. The client asks core to merge the new document into the old document so they match.
  9. 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