Search code examples
elixirphoenix-frameworkphoenix-live-view

Where to do tasks after the Phoenix app is launched?


Background

I am trying to use Phoenix LiveView to create a desktop application. Now, the only way to do this in a somewhat user friendly way (at least the only one I know off) is to launch my Phoenix app/server in and then open browser window in the client's machine.

And this is the trick. Opening a browser's window in the client's machine.

Code

By looking into some github repositories of other formidable Elixir programmers, I was able to fetch a piece of code that somewhat does the job.

demo/lib/demo/application.ex

defmodule Demo.Application do
  @moduledoc false

  use Application

  def start(_type, _args) do
    children = [
      DemoWeb.Telemetry,
      {Phoenix.PubSub, name: Demo.PubSub},
      DemoWeb.Endpoint
    ]

    opts = [strategy: :one_for_one, name: Demo.Supervisor]
    result = Supervisor.start_link(children, opts)

    start_browser_command =
      case :os.type do
        {:win32, _} ->
          "start"
        {:unix, :darwin} ->
          "open"
        {:unix, _} ->
          "xdg-open"
      end

    if System.find_executable(start_browser_command) do
      System.cmd(start_browser_command, ["http://localhost:4000"])
    else
      Mix.raise "Command not found: #{start_browser_command}"
    end

    result
  end

  def config_change(changed, _new, removed) do
    DemoWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

This is supposed to first start the Phoenix App and then open the window.

Questions

So, this code makes me feel a little uncomfortable for a specific reason: I was under the impression that the start function should be only for starting my phoenix server and not for anything else.

But then the question arises:

  • If the start function's job is only to launch the app, then what is the correct/best place to do other tasks, like opening a browser's window or performing some preparation after the app is launched?

Solution

  • Typically, we use Application.start_phase/3 invented exactly for that purpose.

    If you want to run it asynchronously, just spawn another supervised GenServer that would implement handle_continue/2 and {:stop, :normal, _} gracefully from there after having its job done.