Elixir-desktop - Android, Window, macOS, Linux LiveView Apps with Elixir

I would love to see the installers for desktop get flushed out. I’m currently building a PoC desktop app in electron (I feel like I need to shower when writing JS). This would make a perfect alternative for the MVP. It looks like dev may be stalled a bit @dominicletz ? Anything I can help with?

Additionally, I’m looking around to see what the Elixir approach to plugin/frameworks is, I’d like the idea I’m working on to be highly modular and extensible. Anyone have any thoughts or experiences with this type of architecture ni Elixir?

1 Like

Definitely stalling a bit, especially on the public facing front. Also help is always appreciated here some thoughts on current state:

On the positive:

  • Once chosen a certain OTP/Elixir version and have built binaries for it it’s a smooth ride. Code signing for windows+macos is also implemented in the desktop_deployment package.
  • Once setup the free GitHub CI can actually build all platform binaries on every commit. (you probably want to keep your code signing keys private though)

On the negative:

  • The wxWidgets WebView integration is not very feature rich / exposes relatively few Browser/OS features - e.g. you can’t enable recording features - this is to a level where I think it would be nicer to have an Electron runtime wrapper instead of using the wxWidgets WebView - but it’s a somewhat heavy lift
  • Similarly just getting the local environment setup to be able to develop + create binaries e.g. under Windows is tough. This is especially rough when doing mobile development as it requires a pre-built mobile OTP version that matches exactly the local version used for development.

Those are IMHO the bigger points, that said I also have a bunch of internal changes that I haven’t published yet.

2 Likes

I’ve created a large Python wxWindows app (via wxPython). You can guess the year by the windowing style :wink:

Maybe I can lend a hand:

4 Likes

Hey!

So, while building my app on Desktop, I ran into a few macOS-specific behaviours that don’t quite follow platform conventions. I’ve patched them locally in my fork and wanted to check if you’d be interested in another PR (I will ensure I remember to run the formatter), or if you’d prefer I just keep these in my app. … since I’m using your framework for a production grade product, and investing some time to improve the wxWidgets stuff, I was thinking it would help out the broader project.

my local changes currently include:

  1. App Menu items (About / Preferences)

macOS expects menu items tagged with specific roles to auto-move into the application menu (the bold-titled menu left of “File”). Currently, Desktop strips everything except Quit from the Apple menu. I added a role attribute on menu items (e.g., role=“about”, role=“preferences”) that maps to the standard wxID_ABOUT / wxID_PREFERENCES IDs, so wxWidgets handles the placement automatically. Without this, clicking “About” or “Preferences” in the app menu does nothing since the Window process doesn’t forward those events to the menu handler.

2. max_size window option

min_size already exists but there’s no max_size equivalent. I needed fixed-size windows (e.g., an About dialog) and added max_size to mirror the existing min_size pattern.

Both are fairly small, self-contained changes. Happy to clean them up into a proper PR with tests if you think they’d be useful upstream. Otherwise I’ll just keep them in my local fork - no worries either way.

R

2 Likes

Helper is definitely appreciated. Happy to merge stuff.

There is one bigger “boulder” that I would like to address in the architecture forthcoming (just so you are aware of the direction):

Allow to use Native (Swift/C#/C++) “Shell” Apps to start and wrap the Desktop App

Why?

  1. This is already done on Android and iOS and turns out to work quite well, so it would unify the approach.
  2. This would provide native access to APIs that are otherwise hard to get right such as: Icons, Systray, Startmenu, Dock, Startup, Inter-App-Communication, App-Permissions, Audio/Video and so on….
  3. Agent coding makes it so much easier to create and update these “Shells” that I think the benefit starts to outweigh the additional maintenance cost of these native wrappers.

Steps I see:

  • Merge the Desktop.Bridge (used for Android+iOS) into the main Desktop and instead of using :wx as main interface have it behind an adapter (but still the default)
  • Offer default native “Shells” (but keep the current wxWidgets)

And definitely happy to see those PRs

3 Likes

This sounds a lot like elixirkit (livebook) used to work (swift/c#). They’re currently in the process of switching to tauri for the wrapper though.

Yeah definitely, and I’m aware of the work of @wojtekmach has done here and we talked about that a couple of years? (time is flying) ago about this. I’ll have to review that again so thanks for reminding me, but I also want to keep the default wxWidgets based implementation the default – and improve if possible.

1 Like

Yeah our current Tauri-based solution is at:

Tauri has been a huge win for us, especially:

ElixirKit will be extracted out into a separate project soon.

7 Likes

A post was split to a new topic: Elixir Desktop starts then crashes in Android Studio

Is the goal to allow third-party plugins, or just to have your own codebase be very modular and reusable?

If the former, probably GenServers and Behaviours. Disclaimer: I haven’t built anything that allows third-party plugins, so I’m shooting from the hip. :slight_smile: I think this would work though. Defining a behaviour with the public interface you want plugins to provide solves the same problem as interface injection in OOP languages. Making each plugin a GenServer provides process isolation, so a faulty plugin can’t crash the rest of the app.

If it’s just modularity in your own codebase, I’ve found in my Elixir projects that fancy patterns and indirection are usually not that helpful for improving the modularity and maintainability of my code. Just separating functions into different modules with clearly defined purposes and ensuring the different layers of the app only access modules from lower layers is the most important factor in my experience. It’s remarkably easy to just move a function or three into a separate module when everything is immutable and I don’t have to worry about the function mutating some global state in the module its defined in. Behaviours are still useful for defining some kind of contract between subsystems or different GenServers in the same application, but I think using them for code reuse is an anti-pattern, or at least can potentially be an anti-pattern. Similar to using inheritance to reuse code instead of using it to model the domain in OOP languages. :slight_smile:

1 Like