Créer une application Windows avec Elixir-Desktop

Maintenant que nous avons installé notre environnement Windows avec les prérequis, nous allons construire notre première application Desktop avec Elixir-Desktop.

L’idée est de valider le concept de création d’écran réalisé avec Phoenix, écrans qui seront affichés et intégré à l’application Desktop. L’application Windows dispose ainsi d’écran sans faire appel à un navigateur extérieur. Cela reprend le concept proposé par l’outil Electron qui est lui, réalisé en javascript.

Création d’une application phoenix et Elixir hello_desktop

Vérifions les outils installés

Nous allons créer l’application pour l’environnement Windows.

PS C:\Users\berou> elixir -v
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.14.5 (compiled with Erlang/OTP 25)
PS C:\Users\berou>

Pour mettre à jour Erlang et Elixir nous suivons les étapes définies dans notre article.

C:\Users\broussel>elixir -v
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.15.5 (compiled with Erlang/OTP 26)

C:\Users\broussel>

Création de l’application mix phx.new

L’application Phoenix est créé sous le nom hello_desktop. Nous souhaitons une application sans base de données. Nous regardons la documentation avec la liste des options pour phx.new :

  • mix phx.new hello_desktop --no-ecto

Sur Windows-10 : mix phx.new hello_desktop –no-ecto

C:\Users\broussel>elixir -v
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.15.5 (compiled with Erlang/OTP 26)

C:\Users\broussel>cd C:\CarbonX1\Phoenix\Projets

C:\CarbonX1\Phoenix\Projets>mix phx.new hello_desktop --no-ecto
* creating hello_desktop/config/config.exs
* creating hello_desktop/config/dev.exs
* creating hello_desktop/config/prod.exs
* creating hello_desktop/config/runtime.exs
* creating hello_desktop/config/test.exs
* creating hello_desktop/lib/hello_desktop/application.ex
* creating hello_desktop/lib/hello_desktop.ex
* creating hello_desktop/lib/hello_desktop_web/controllers/error_json.ex
* creating hello_desktop/lib/hello_desktop_web/endpoint.ex
* creating hello_desktop/lib/hello_desktop_web/router.ex
* creating hello_desktop/lib/hello_desktop_web/telemetry.ex
* creating hello_desktop/lib/hello_desktop_web.ex
* creating hello_desktop/mix.exs
* creating hello_desktop/README.md
* creating hello_desktop/.formatter.exs
* creating hello_desktop/.gitignore
* creating hello_desktop/test/support/conn_case.ex
* creating hello_desktop/test/test_helper.exs
* creating hello_desktop/test/hello_desktop_web/controllers/error_json_test.exs
* creating hello_desktop/lib/hello_desktop_web/controllers/error_html.ex
* creating hello_desktop/test/hello_desktop_web/controllers/error_html_test.exs
* creating hello_desktop/lib/hello_desktop_web/components/core_components.ex
* creating hello_desktop/lib/hello_desktop_web/controllers/page_controller.ex
* creating hello_desktop/lib/hello_desktop_web/controllers/page_html.ex
* creating hello_desktop/lib/hello_desktop_web/controllers/page_html/home.html.heex
* creating hello_desktop/test/hello_desktop_web/controllers/page_controller_test.exs
* creating hello_desktop/lib/hello_desktop_web/components/layouts/root.html.heex
* creating hello_desktop/lib/hello_desktop_web/components/layouts/app.html.heex
* creating hello_desktop/lib/hello_desktop_web/components/layouts.ex
* creating hello_desktop/priv/static/images/logo.svg
* creating hello_desktop/lib/hello_desktop/mailer.ex
* creating hello_desktop/lib/hello_desktop_web/gettext.ex
* creating hello_desktop/priv/gettext/en/LC_MESSAGES/errors.po
* creating hello_desktop/priv/gettext/errors.pot
* creating hello_desktop/priv/static/robots.txt
* creating hello_desktop/priv/static/favicon.ico
* creating hello_desktop/assets/js/app.js
* creating hello_desktop/assets/vendor/topbar.js
* creating hello_desktop/assets/css/app.css
* creating hello_desktop/assets/tailwind.config.js
* creating hello_desktop/assets/vendor/heroicons/LICENSE.md
* creating hello_desktop/assets/vendor/heroicons/UPGRADE.md
* extracting hello_desktop/assets/vendor/heroicons/optimized

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix assets.setup
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd hello_desktop

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server


C:\CarbonX1\Phoenix\Projets>

Le projet hello_desktop a été créé sur Windows_10. Nous pouvons maintentant réaliser les modifications permettant de transformer le projet en version Desktop. Cela se fait avec VS Code.

Sur Windows_11, nous créons le projet hello :

  • dossier : C:\Surface\Phoenix\Projets
  • création avec mix : mix phx.new hello --no-ecto
C:\Users\berou>elixir -v
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.15.5 (compiled with Erlang/OTP 26)

C:\Users\berou>cd C:\Surface\Phoenix\Projets

C:\Surface\Phoenix\Projets>mix phx.new hello --no-ecto
* creating hello/config/config.exs
* creating hello/config/dev.exs
* creating hello/config/prod.exs
* creating hello/config/runtime.exs
* creating hello/config/test.exs
* creating hello/lib/hello/application.ex
* creating hello/lib/hello.ex
* creating hello/lib/hello_web/controllers/error_json.ex
* creating hello/lib/hello_web/endpoint.ex
* creating hello/lib/hello_web/router.ex
* creating hello/lib/hello_web/telemetry.ex
* creating hello/lib/hello_web.ex
* creating hello/mix.exs
* creating hello/README.md
* creating hello/.formatter.exs
* creating hello/.gitignore
* creating hello/test/support/conn_case.ex
* creating hello/test/test_helper.exs
* creating hello/test/hello_web/controllers/error_json_test.exs
* creating hello/lib/hello_web/controllers/error_html.ex
* creating hello/test/hello_web/controllers/error_html_test.exs
* creating hello/lib/hello_web/components/core_components.ex
* creating hello/lib/hello_web/controllers/page_controller.ex
* creating hello/lib/hello_web/controllers/page_html.ex
* creating hello/lib/hello_web/controllers/page_html/home.html.heex
* creating hello/test/hello_web/controllers/page_controller_test.exs
* creating hello/lib/hello_web/components/layouts/root.html.heex
* creating hello/lib/hello_web/components/layouts/app.html.heex
* creating hello/lib/hello_web/components/layouts.ex
* creating hello/priv/static/images/logo.svg
* creating hello/lib/hello/mailer.ex
* creating hello/lib/hello_web/gettext.ex
* creating hello/priv/gettext/en/LC_MESSAGES/errors.po
* creating hello/priv/gettext/errors.pot
* creating hello/priv/static/robots.txt
* creating hello/priv/static/favicon.ico
* creating hello/assets/js/app.js
* creating hello/assets/vendor/topbar.js
* creating hello/assets/css/app.css
* creating hello/assets/tailwind.config.js
* creating hello/assets/vendor/heroicons/LICENSE.md
* creating hello/assets/vendor/heroicons/UPGRADE.md
* extracting hello/assets/vendor/heroicons/optimized

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix assets.setup
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd hello

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server


C:\Surface\Phoenix\Projets>

Vérification de l’application en mode serveur web

pour lancer le serveur :

  • se placer dans le répertoire du projet : cd hello_desktop
  • lancer le serveur : mix phx.server
  • visualiser le projet dans le navigateur : http://localhost:4000/
Création de l'application hello Desktop sur Windows#1-la page phoenix en mode web
Création de l’application hello Desktop sur Windows#1-la page phoenix en mode web

Nous pouvons maintenant modifier cette application pour la transformer en mode Desktop.

Adaptation de l’application avec Elixir Desktop

L’application que nous venons de créer est transformée en version Desktop pour Windows.

Le travail à réaliser pour cette transformation est décrit dans la préconisation données par Elixir-Desktop.

Les modifications sont réalisées dans VS Code. Pour ouvrir VS Code :

  • Se placer dans le dossier du projet : cd hello_desktop
  • Ouvrir VS Code et afficher le projet dans VS Code : code .

Ajout de la dépendance à Desktop

Nous modifions le fichier mix.exs :

mix.exs :

 def deps do
 [
     {:desktop, "~> 1.5"}
 ]
 end

On réalise l’ajout de desktop en ligne (6). Noter la virgule à la fin de la ligne ajoutée.

mix.exs :

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:desktop, "~> 1.5"},
      {:phoenix, "~> 1.7.7"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.19.0"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.8.0"},
      {:esbuild, "~> 0.7", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.2.0", runtime: Mix.env() == :dev},
      {:swoosh, "~> 1.3"},
      {:finch, "~> 0.13"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.20"},
      {:jason, "~> 1.2"},
      {:plug_cowboy, "~> 2.5"}
    ]
  end

Ajout de la fenêtre desktop dans l’application

On ajoute la fenêtre Dektop qui affichera le contenu de l’application.

application.exs :

 children = [{
     # After your other children
     # Starting Desktop.Windows
     Desktop.Window,
     [
         app: :your_app,
         id: YourAppWindow,
         url: &YourAppWeb.Endpoint.url/0
     ]
 }]
 Supervisor.start_link()

Il convient de remplacer :

  • ligne (6), your_app par l’identifiant de l’application. On trouve celui-ci dans mix.exs en ligne (6),
  • ligne (7), YourAppWindow par HelloDesktopWindow, YourApp étant HelloDesktop,
  • ligne (8), YourAppWeb par HelloDektopWeb,

Dans mix.exs nous avons def project et en ligne (6) : app: suivi du nom de l’application :hello_desktop

mix.exs :

defmodule HelloDesktop.MixProject do
  use Mix.Project

  def project do
    [
      app: :hello_desktop,
      version: "0.1.0",
      elixir: "~> 1.14",
      elixirc_paths: elixirc_paths(Mix.env()),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

lib/hello_desktop/application.ex :

  @impl true
  def start(_type, _args) do
    children = [
      # Start the Telemetry supervisor
      HelloDesktopWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: HelloDesktop.PubSub},
      # Start Finch
      {Finch, name: HelloDesktop.Finch},
      # Start the Endpoint (http/https)
      HelloDesktopWeb.Endpoint,
      # Start a worker by calling: HelloDesktop.Worker.start_link(arg)
      # {HelloDesktop.Worker, arg}
      
      # After your other children
      # Starting Desktop.Windows
      {Desktop.Window,
      [
         app: :hello_desktop,
         id: HelloDesktop,
         url: &HelloDesktopWeb.Endpoint.url/0
      ]}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: HelloDesktop.Supervisor]
    Supervisor.start_link(children, opts)
  end

Noter l’ajout de la virgule en ligne (11), et les accolades ligne (17) et (22).

Modification du Endpoint pour utiliser Desktop

On remplace Phoenix.Endpoint par Desktop.Endpoint.

endpoint.ex :

 defmodule YourAppWeb.Endpoint do
 use Desktop.Endpoint, otp_app: :your_app

lib/hello_desktop_web/endpoint.ex :

defmodule HelloDesktopWeb.Endpoint do
  # use Phoenix.Endpoint, otp_app: :hello_desktop
  use Desktop.Endpoint, otp_app: :hello_desktop

On met en commentaire la ligne initiale

Vérification de la configuration http/port

On change la configuration du serveur pour être en local avec un port à 0.

config.exs :

 # Configures the endpoint
 config :your_app, YourAppWeb.Endpoint,
     http: [ip: {127, 0, 0, 1}, port: 0],
     server: true,
 ...

Nous regardons la configuration du projet exemple sur github

Nous ajoutons ci-dessous les lignes (4) et (11)

config.exs :

# Configures the endpoint
config :hello_desktop, HelloDesktopWeb.Endpoint,
  # url: [host: "localhost"],
  http: [ip: {127, 0, 0, 1}, port: 0],
  render_errors: [
    formats: [html: HelloDesktopWeb.ErrorHTML, json: HelloDesktopWeb.ErrorJSON],
    layout: false
  ],
  pubsub_server: HelloDesktop.PubSub,
  live_view: [signing_salt: "oy3Zqaaz"],
  server: true

Utilisation de la locale du poste qui utilisera l’application

On peut adapter l’application à la locale du poste.

application.ex :

 def start(_type, args) do 
     Desktop.identify_default_locale(YourWebApp.Gettext)
     children = [
         ...

On ajoute cette option. YourWebApp est remplacé par HelloDesktopWeb :

application.ex

  @impl true
  def start(_type, _args) do
    Desktop.identify_default_locale(HelloDesktopWeb.Gettext)
    children = [
      # Start the Telemetry supervisor
      HelloDesktopWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: HelloDesktop.PubSub},
      # Start Finch
      {Finch, name: HelloDesktop.Finch},
      # Start the Endpoint (http/https)
      HelloDesktopWeb.Endpoint,
      # Start a worker by calling: HelloDesktop.Worker.start_link(arg)
      # {HelloDesktop.Worker, arg}

      # After your other children
      # Starting Desktop.Windows
      {Desktop.Window,
      [
         app: :hello_desktop,
         id: HelloDesktop,
         title: "HelloDesktop",
         url: &HelloDesktopWeb.Endpoint.url/0
      ]}
    ]

Noter les lignes 20, 21 et 23. et nous ajoutons title : « HelloDesktop » en ligne 22, qui sera le titre de notre fenêtre.

Compilation et exécution de l’application

nous pouvons maintenant compiler et executer l’application.

C:\CarbonX1\Phoenix\Projets\hello_desktop>mix phx.server
Unchecked dependencies for environment dev:
* desktop (Hex package)
  the dependency is not available, run "mix deps.get"
** (Mix) Can't continue due to errors on dependencies

C:\CarbonX1\Phoenix\Projets\hello_desktop>

On execute mix deps get :

C:\CarbonX1\Phoenix\Projets\hello_desktop>mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.316s
New:
  dbus 0.8.0
  debouncer 0.1.7
  desktop 1.5.2
  ex_dbus 0.1.4
  ex_sni 0.2.9
  oncrash 0.1.0
  saxy 1.4.0
Unchanged:
  castore 1.0.3
  cowboy 2.10.0
  cowboy_telemetry 0.4.0
  cowlib 2.12.1
  esbuild 0.7.1
  expo 0.4.1
  file_system 0.2.10
  finch 0.16.0
  floki 0.34.3
  gettext 0.23.1
  hpax 0.1.2
  jason 1.4.1
  mime 2.0.5
  mint 1.5.1
  nimble_options 1.0.2
  nimble_pool 1.0.0
  phoenix 1.7.7
  phoenix_html 3.3.2
  phoenix_live_dashboard 0.8.1
  phoenix_live_reload 1.4.1
  phoenix_live_view 0.19.5
  phoenix_pubsub 2.1.3
  phoenix_template 1.0.3
  plug 1.14.2
  plug_cowboy 2.6.1
  plug_crypto 1.2.5
  ranch 1.8.0
  swoosh 1.11.5
  tailwind 0.2.1
  telemetry 1.2.1
  telemetry_metrics 0.6.1
  telemetry_poller 1.0.0
  websock 0.5.3
  websock_adapter 0.5.4
* Getting desktop (Hex package)
* Getting debouncer (Hex package)
* Getting ex_sni (Hex package)
* Getting oncrash (Hex package)
* Getting ex_dbus (Hex package)
* Getting saxy (Hex package)
* Getting dbus (Hex package)

C:\CarbonX1\Phoenix\Projets\hello_desktop>

Nous executons l’application :

  • cd hello_desktop
  • mix phx.server

l’application s’ouvre lentement.

C:\CarbonX1\Phoenix\Projets\hello_desktop>mix phx.server
Compiling 1 file (.ex)
[warning] Phoenix is unable to create symlinks. Phoenix' code reloader will run considerably faster if symlinks are allowed. On Windows, the lack of symlinks may even cause empty assets to be served. Luckily, you can address this issue by starting your Windows terminal at least once with "Run as Administrator" and then running your Phoenix application.
[info] Running HelloDesktopWeb.Endpoint with cowboy 2.10.0 at 127.0.0.1:4000 (http)
[info] Access HelloDesktopWeb.Endpoint at http://localhost:4000
[watch] build finished, watching for changes...
[info] Showing http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA
[info] Rebuilding WebView on Windows with url: "http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"

Rebuilding...

Done in 874ms.
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 927ms
[info] Showing http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 614µs
[info] Rebuilding WebView on Windows with url: "http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 512µs
[info] Showing http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 819µs
[info] Rebuilding WebView on Windows with url: "http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"
[info] Showing http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 716µs
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 614µs
[info] Rebuilding WebView on Windows with url: "http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"
[info] Showing http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 819µs
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 921µs
[info] Rebuilding WebView on Windows with url: "http://localhost:4000?k=ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"
[info] GET /
[debug] Processing with HelloDesktopWeb.PageController.home/2
  Parameters: %{"k" => "ATRMWXWNAQ4KYSGTA3Q5BX2UURXL4BK2DQIF32P2I2DXQM6NKTAA"}
  Pipelines: [:browser]
[info] Sent 200 in 819µs
[info] Shutting down 5 sockets in 1 rounds of 2000ms
[notice] Application hello_desktop exited: shutdown

L’application ouvre la page Phoenix :

Une application Windows Hello Desktop#1-La fenêtre de l'application Hello Desktop
Une application Windows Hello Desktop#1-La fenêtre de l’application Hello Desktop

La fenêtre s’ouvre directement, sans avoir besoin de passer par un navigateur.

Nous avons eu l’impression qu’il était difficile de fermer l’application avec la croix en haut à droite. Nous devons refaire un essai.

Ajout des fichiers statiques

Nous pouvons ajouter les fichiers images dans l’application, en particulier les icônes de l’application.

Distribution de l’application

Pour distribuer l’application, nous devons créer un exécutable.

Conclusion

Nous avons réussi à créer l’application minimale HelloDesktop. L’application se lance avec mix phx.server

Nous devons encore :

  • regarder comment se distribue l’application.
  • voir la création d’une application avec base de données intégrées.
  • créer la même application sur Mac, Android et ios

Cela fera l’objet de nouveaux articles.

Si vous avez aimé l'article vous êtes libre de le partager :-)

Laisser un commentaire