Pure helpers for mix dala.enable — extracted for testability.
LiveView bridge architecture
Enabling LiveView mode involves three coordinated patches. Understanding why all three are necessary prevents subtle bugs when setting up projects manually.
The two bridges
The native WebView (iOS WKWebView / Android WebView) injects a window.dala
JavaScript object into every page it loads. This object routes calls through
the NIF bridge:
window.dala.send(data) // JS → NIF → Elixir handle_info
window.dala.onMessage(fn) // registers handler for NIF → JS messages
window.dala._dispatch(json) // called by the NIF to deliver messages to JSIn LiveView mode you want a different routing: JS messages should travel over
the LiveView WebSocket so that handle_event/3 in your LiveView receives them
and push_event/3 delivers server messages to JS. The DalaHook replaces
window.dala with a LiveView-backed version on mount:
window.dala.send(data) // JS → pushEvent("dala_message") → handle_event/3
window.dala.onMessage(fn) // registers handler for handleEvent("dala_push")
window.dala._dispatch // no-op: server messages arrive via handleEventWhy a DOM element is required (the non-obvious part)
Phoenix LiveView hooks only execute their mounted() callback when an element
carrying phx-hook="DalaHook" is present in the rendered HTML and the
LiveView WebSocket has connected. Registering DalaHook in the hooks: map in
app.js is necessary but not sufficient — the hook is dormant until LiveView
finds a matching DOM element.
Without the element:
- DalaHook never mounts
window.dalais never replaced with the LiveView versionwindow.dala.send()routes through the native NIF bridge instead of LiveViewhandle_event/3never fires; your LiveView cannot receive JS messages
The element is a hidden <div> placed immediately after the opening <body>
tag in root.html.heex:
<div id="dala-bridge" phx-hook="DalaHook" style="display:none"></div>Placing it at the top of <body> ensures the hook mounts as early as possible,
so window.dala is overridden before any page-specific JS runs.
Android timing note
iOS injects the native window.dala shim via WKUserScript at
.atDocumentStart — before any page JS runs. Android injects it via
evaluateJavascript in onPageFinished — after the page has loaded. Between
page load and onPageFinished on Android, window.dala is undefined. In
practice LiveView connects after onPageFinished, so both shims are available
by the time the DalaHook mounts. If you call window.dala during
DOMContentLoaded, guard with if (window.dala).
Summary
Functions
Builds a plist <key>/<value> entry for Info.plist injection.
Returns the hidden bridge <div> element that must appear in root.html.heex.
Returns the DalaHook JS constant to inject into app.js.
Finds root.html.heex in a Phoenix project rooted at project_dir.
Adds android:networkSecurityConfig="@xml/network_security_config" to the
<application> tag in an AndroidManifest.xml string.
Injects the hidden bridge <div> into content (a root.html.heex file).
Injects the DalaHook definition and registration into content (the full
text of assets/js/app.js).
Returns the XML content for the Android network security config.
Reads the app: atom from the given mix.exs path and returns the app
name as a string, or raises.
Functions
Builds a plist <key>/<value> entry for Info.plist injection.
Options:
type: :bool— emits<true/>or<false/>instead of<string>
@spec dala_bridge_element() :: String.t()
Returns the hidden bridge <div> element that must appear in root.html.heex.
See the module doc for why this element is required.
@spec dala_hook_js() :: String.t()
Returns the DalaHook JS constant to inject into app.js.
Finds root.html.heex in a Phoenix project rooted at project_dir.
Checks both the Phoenix 1.7+ convention:
lib/<app_name>_web/components/layouts/root.html.heexand the pre-1.7 convention:
lib/<app_name>_web/templates/layout/root.html.heexReturns the path string or nil if neither file exists.
Adds android:networkSecurityConfig="@xml/network_security_config" to the
<application> tag in an AndroidManifest.xml string.
Idempotent — returns the content unchanged if the attribute is already present.
Injects the hidden bridge <div> into content (a root.html.heex file).
The element is placed immediately after the opening <body> tag. This is
the mount point for DalaHook — without it the hook never executes and
window.dala is never replaced with the LiveView version. See the module doc
for the full explanation.
Returns the patched HTML string unchanged if id="dala-bridge" is already
present.
Injects the DalaHook definition and registration into content (the full
text of assets/js/app.js).
- Inserts the hook constant after the last top-level
importline. - Registers
DalaHookin thehooks:option passed toLiveSocket.
Returns the patched JS string. Idempotency (skip if already present) is handled by the calling task, not by this function.
@spec network_security_config_xml() :: String.t()
Returns the XML content for the Android network security config.
Reads the app: atom from the given mix.exs path and returns the app
name as a string, or raises.