How to: Sending flash messages to LiveViews from iex

Documenting one way to “broadcast” a flash message to LiveViews (mostly for fun and learning) using a rather unorthodox method.

Let’s say with no special preparation in your LiveView code – no PubSub, no callback handlers, no intention to send arbitrary flash messages to arbitrary users – you’d like to connect to a running system (e.g. iex -S mix phx.server or bin/myapp remote) and push some message onto connected browsers!

iex> search = "MyAppWeb.SomeLiveViewModule.mount"
iex> Phoenix.LiveDashboard.SystemInfo.fetch_processes(node(), search, :reductions, :desc, 10)
|> elem(0)
|> Enum.map(fn process ->
  process[:pid]
  |> then(fn pid ->
    :sys.replace_state(pid, fn state ->
       Map.update!(state, :socket, fn socket ->
        Phoenix.LiveView.put_flash(socket, :info, "Hello, #{socket.assigns.current_user.name}!")
      end)
    end) && send(pid, :ok) # Dummy message to trigger a call to `handle_changed/3` to flush new state to client 
  end)
end)

The main bits:

  • There are many ways to introspect processes, including Process.list/0 + Process.info/1, but above I went to see how Phoenix LiveDashboard implements search on the Processes tab. Thus, we’re able to find LiveViews based on name.
  • We could further filter the results, for example using :sys.get_state/1 to access the socket assigns and looking at the current logged in user to target our flash message to a specific user. Or pick a lucky LiveView process at random!
  • :sys.replace_state/2 is used to update the state of the LiveView, using the fact that it is a GenServer.
  • So far the socket was updated, but nothing happened on the browser… we need somehow to help the LiveView process to realize state has changed and needs to be sent to the client.
    There might be better ways to accomplish this, but, when the LiveView in question doesn’t implement its own handle_info callback, sending an arbitrary message works: Phoenix.LiveView.Channel.handle_info calls view_handle_info/2 which prints a warning, then eventually handle_result/3 calls handle_changed/3 and a diff with the flash message is sent to the client.

Of course, all this was just an exercise to try and demonstrate some of the introspection capabilities we have at our disposal. You should be using PubSub or other mechanisms to properly distribute messages / events.

I’m curious to learn though if my hacky steps could be improved, particularly the last bit on how to get the new state to be sent to the client. Looking forward to your comments!

4 Likes