Skip to content

Instantly share code, notes, and snippets.

@jghankins
Created April 2, 2026 19:42
Show Gist options
  • Select an option

  • Save jghankins/8e499a7fd145bb2158acf7e832985aa4 to your computer and use it in GitHub Desktop.

Select an option

Save jghankins/8e499a7fd145bb2158acf7e832985aa4 to your computer and use it in GitHub Desktop.
Real-Time Community Feed - Architecture & Flow Diagrams

Real-Time Community Feed Architecture

Technical documentation for the real-time community feed feature spanning the iOS client and Phoenix backend.


1. Architecture Overview

graph LR
    subgraph iOS App
        A[CommunityTabView] --> B[CommunityViewModel]
        B --> C[CommunityChannelService]
        B --> D[APIClient]
    end

    subgraph Phoenix Backend
        E[CommunitySocket] --> F[CommunityChannel]
        G[CommunityController] --> H[Community Context]
        H --> I[PubSub]
        I --> F
    end

    C <-->|WebSocket<br/>wss://.../community/websocket| E
    D -->|REST API<br/>GET/POST /api/community/*| G
Loading

2. WebSocket Connection Flow

sequenceDiagram
    participant User
    participant CommunityTabView
    participant CommunityViewModel
    participant CommunityChannelService
    participant Keychain
    participant CommunitySocket
    participant Accounts
    participant CommunityChannel
    participant PubSub

    User->>CommunityTabView: Opens Community tab
    CommunityTabView->>CommunityViewModel: .onAppear
    CommunityViewModel->>CommunityChannelService: connect()

    CommunityChannelService->>Keychain: Read API token
    Keychain-->>CommunityChannelService: api_token

    CommunityChannelService->>CommunitySocket: wss://{domain}/community/websocket?token={api_token}

    Note over CommunitySocket: connect/3 callback
    CommunitySocket->>CommunitySocket: SHA256 hash token
    CommunitySocket->>Accounts: get_user_by_api_token(hashed_token)
    Accounts-->>CommunitySocket: %User{id: user_id}
    CommunitySocket->>CommunitySocket: assign(:user_id, user_id)
    CommunitySocket-->>CommunityChannelService: Socket connected

    CommunityChannelService->>CommunityChannel: join("community:feed")

    Note over CommunityChannel: join/3 callback
    CommunityChannel->>CommunityChannel: user_joined_community_by_id?(user_id)
    CommunityChannel->>PubSub: subscribe("community:feed")
    CommunityChannel-->>CommunityChannelService: {:ok, reply}

    CommunityChannelService-->>CommunityViewModel: Connected & joined
Loading

3. Real-Time Event Flow (New Post)

sequenceDiagram
    participant UserB as User B (Author)
    participant APIClient as APIClient (User B)
    participant CommunityController
    participant Community as Community Context
    participant PubSub
    participant CommunityChannel
    participant CommunityChannelService as CommunityChannelService (User A)
    participant CommunityViewModel as CommunityViewModel (User A)
    participant SwiftUI as SwiftUI (User A)

    UserB->>APIClient: Create post
    APIClient->>CommunityController: POST /api/community/posts
    CommunityController->>Community: create_post(attrs)
    Community->>Community: Insert post into DB
    Community->>PubSub: broadcast("community:feed", {:post_created, post})
    Community-->>CommunityController: {:ok, post}
    CommunityController-->>APIClient: 201 Created (JSON)

    PubSub->>CommunityChannel: handle_info({:post_created, post})
    CommunityChannel->>CommunityChannel: Serialize post to JSON payload
    CommunityChannel->>CommunityChannelService: push("new_post", payload)

    CommunityChannelService->>CommunityChannelService: Decode JSON → CommunityPost
    CommunityChannelService->>CommunityViewModel: onNewPost(post)
    CommunityViewModel->>CommunityViewModel: Deduplicate by post.id
    CommunityViewModel->>CommunityViewModel: Prepend post to posts array
    CommunityViewModel->>SwiftUI: @Observable triggers re-render
    SwiftUI->>SwiftUI: Feed updates with new post
Loading

4. Event Types

graph LR
    subgraph Phoenix PubSub Broadcasts
        A[post_created] -->|"new_post"| W[WebSocket Push]
        B[post_updated] -->|"post_updated"| W
        C[post_deleted] -->|"post_deleted"| W
        D[like_toggled] -->|"post_likes_updated"| W
        E[comment_created<br/>comment_deleted] -->|"post_comments_updated"| W
    end
Loading
WebSocket Event Trigger Payload Client Action
new_post Post creation Full serialized post Prepend to feed
post_updated Post edit Full serialized post Replace post in-place
post_deleted Post deletion {id: post_id} Remove post from feed
post_likes_updated Like/unlike toggle {id, likes_count, liked_by_user_ids} Update like state on post
post_comments_updated Comment create/delete {id, comments_count} Update comment count on post

5. Lifecycle Management

stateDiagram-v2
    [*] --> Disconnected

    Disconnected --> Connecting : Tab appears
    Connecting --> Connected : Socket opened + channel joined
    Connected --> Disconnected : Tab disappears

    Connected --> Disconnected : App backgrounds
    Disconnected --> Connecting : App foregrounds

    Connected --> Reconnecting : Socket error / drop
    Reconnecting --> Connected : Auto-reconnect success
    Reconnecting --> Reconnecting : Retry with backoff

    Connected --> Disconnected : User logs out

    note right of Reconnecting
        SwiftPhoenixClient handles
        reconnection automatically
        with exponential backoff
    end note

    note right of Disconnected
        Channel reference is
        cleaned up on disconnect
    end note
Loading

Connection Lifecycle Triggers (iOS)

Event Source Action
Tab appears .onAppear on CommunityTabView CommunityChannelService.connect()
Tab disappears .onDisappear on CommunityTabView CommunityChannelService.disconnect()
App backgrounds UIApplication.didEnterBackgroundNotification CommunityChannelService.disconnect()
App foregrounds UIApplication.willEnterForegroundNotification CommunityChannelService.connect()
Socket error SwiftPhoenixClient internal Auto-reconnect with exponential backoff
Logout Auth flow CommunityChannelService.disconnect()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment