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?

Where did you get that impression from? MyApp.Application.start/2 is exactly the place to trigger things, which need to start when the application is started. The only consideration is if you do it inline like in your example or if you do it as part of a process being started by the root supervisor.

My concern here is that I am opening the browser’s windows at basically the same time I am launching the Phoenix App. This means that sometimes I get a “Server Not Found” message in my browser because the Phoenix App is not yet fully launched.

Is there a way to avoid this?

I thought about using Process.sleep but I think this is a rather terrible solution.

That’s not going to be the case, given Supervisor.start_link is called before the opening and the endpoint therefore is started.

1 Like