How to broadcast to one socket_id using phoenix

618 views Asked by At

In the docs there is this example but it only works for disconnects https://hexdocs.pm/phoenix/Phoenix.Socket.html#module-examples

  use Phoenix.Socket

  channel "room:*", MyAppWeb.RoomChannel

  def connect(params, socket, _connect_info) do
    {:ok, assign(socket, :user_id, params["user_id"])}
  end

  def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end

# Disconnect all user's socket connections and their multiplexed channels
MyAppWeb.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})

I have tried that and it only works for disconnects.

I've read in places that you can create a topic specific for that user but I could use a practical example. I am having trouble grokking the complexity and could use a practical example.

Thank you.

I was thinking if you could get the underlying socket in the ets table you can just push/4 to that socket but I don't know how to do that.

1

There are 1 answers

5
Peaceful James On BEST ANSWER

You need to actually subscribe to a topic in order to receive the broadcast.

The "disconnect" message is special because it doesn't require subscription.

Inside the Phoenix.Socket source code, there exists these three lines:

  def __info__(%Broadcast{event: "disconnect"}, state) do
    {:stop, {:shutdown, :disconnected}, state}
  end

and they are the reason the socket state changes when the "disconnect" broadcast is received.

The "fallback" function looks like this:

  def __info__(_, state) do
    {:ok, state}
  end

which means for any and all "other" topics, the socket state is unchanged.

You need to manually subscribe to "other" topics.

It should look something like this:

socket.endpoint.subscribe("some_topic:" <> socket.assigns.user_id)

Then you can broadcast messages like this:

MyAppWeb.Endpoint.broadcast("some_topic:" <> user.id, "some_event", %{foo: bar})

If you are doing this with LiveView then the easiest way to get started is to put this in your mount function:

if connected?(socket) do
  socket.endpoint.subscribe("some_topic:" <> socket.assigns.user_id)
end

and to then have a handle_info like this:

  @impl true
  def handle_info(%{event: "some_event"} = info, socket) do
    IO.inspect info, label: "INFO"
    {:noreply, socket}
  end