LiveView Native Documentation
repository·main·Indexed 21 days ago
https://github.com/liveview-native/live_view_nativeA platform for building native applications using Elixir and Phoenix LiveView. It allows developers to share business logic and state management between web and native clients by transforming LiveView templates into native UI components using the ~LVN sigil (NEEx). It supports multiple UI frameworks including SwiftUI for Apple devices and Jetpack Compose for Android.
What's inside LiveView Native
- LiveView Native is a platform that allows you to build native applications using Elixir and Phoenix LiveView. It enables a single LiveView to serve both web and non-web clients by transforming platform-specific template code into native UIs. This allows developers to share business logic and state management between web browsers and native mobile/desktop applications.
How LiveView Native works
mainLiveView Native extends Phoenix LiveView to serve platform-specific server-rendered markup to native clients. It operates by delegating requests from a standard LiveView to specialized markup processors based on query parameters.
The Request Flow
- Querying: The client sends a request with specific query parameters to denote the platform and device type.
- Delegation: LiveView Native matches the request to the appropriate LiveView route and delegates rendering to a platform-specific module.
- Processing: The platform-specific module uses the
~LVNsigil to return native UI representations (NEEx templates) instead of HTML. - Client Execution: A native client (like SwiftUI or Jetpack Compose) receives the data and renders the native UI components.
NEEx vs HEEx templates
mainLiveView Native uses NEEx (Native+EEx) via the
~LVNsigil. While similar to HEEx, there are critical differences for native rendering:- Tag Casing: Tag name casing is preserved. For example,
<Text>will not be converted to<text>as it would be in HTML. - Explicit Booleans: Boolean attributes are not permitted in their shorthand form (e.g.,
<Text on>). Because native clients handle truthy/falsy values differently, you must be explicit:<Text on={true}>. - Special Attributes: NEEx supports all standard HEEx special attributes plus LiveView Native specific ones defined in
LiveViewNative.Component.sigil_LVN/2.
- Tag Casing: Tag name casing is preserved. For example,
Install LiveView Native in a Phoenix Application
mainTo use LiveView Native, you must have an existing Phoenix application. Add
live_view_nativeto yourmix.exsdependencies. Depending on your target platform, you may also want to include specialized libraries for stylesheets, SwiftUI, or live forms.After updating your dependencies, run the setup command to complete the installation process.
# In mix.exs {:live_view_native, "~> 0.4.0-rc.1"}, {:live_view_native_stylesheet, "~> 0.4.0-rc.1"}, {:live_view_native_swiftui, "~> 0.4.0-rc.1"}, {:live_view_native_live_form, "~> 0.4.0-rc.1"}$ mix lvn.setupImplement platform-specific rendering with LiveView Native
mainTo support multiple platforms, you should separate your logic into a base LiveView for event handling and specialized modules for rendering.
- Base LiveView: Use
MyAppNative, :live_viewto handlemountand events. This module acts as the entry point for all platforms. - Platform Modules: Create modules for specific formats (e.g.,
:swift_uior:jetpack). These modules use a two-arityrender/2function to match on client information and metadata. - NEEx Templates: Use the
~LVNsigil to define templates. These are similar to HEEx but optimized for native UI.
# lib/my_app_web/live/hello_live.ex defmodule MyAppWeb.HelloLive do use MyAppWeb, :live_view use MyAppNative, :live_view def mount(_params, _session, socket) do {:ok, socket} end end # lib/my_app_web/live/hello_live_swiftui.ex defmodule MyAppWeb.HelloLive.SwiftUI do use MyAppNative, :live_view def render(assigns, %{"target" => "watchos"}) do ~LVN""" <VStack> <Text>Hello WatchOS!</Text> </VStack> """ end def render(assigns, _interface) do ~LVN""" <VStack> <Text>Hello SwiftUI!</Text> </VStack> """ end end- Base LiveView: Use
How to define a LiveView for Native Clients
mainTo make a LiveView compatible with native clients, use
MyAppNative, :live_view. To provide platform-specific rendering (e.g., for SwiftUI or specific watchOS targets), you can useMyAppNative, [:render_component, format: :swiftui]and implement arender/2function that pattern matches on the interface or target.LiveView Native uses the
~LVNsigil to define native UI templates.# Standard LiveView with Native support defmodule MyAppWeb.HelloLive do use MyAppWeb, :live_view use MyAppNative, :live_view end # Platform-specific rendering for SwiftUI defmodule MyAppWeb.HelloLive.SwiftUI do use MyAppNative, [:render_component, format: :swiftui] def render(assigns, %{"target" => "watchos"}) do ~LVN""" <VStack> <Text> Hello WatchOS! </Text> </VStack> """ end def render(assigns, _interface) do ~LVN""" <VStack> <Text> Hello SwiftUI! </Text> </VStack> """ end endUse the ~LVN sigil for native templates
mainThe
~LVNsigil is used to write composable markup directly inside Elixir source files. It is similar to Phoenix's~Hsigil but optimized for LiveView Native clients.Key Differences from HTML/XML:
- Casing:
~LVNdoes not enforce lowercase tag names.<Text>is a valid and common tag. - Special Attributes: Supports standard HEEx attributes plus the
:interface-prefix.
The
:interface-attributeYou can conditionally render elements based on the client
interfacemap using the:interface-<key>syntax. This is a shorthand for checkingget_in(assigns, [:_interface, "<key>"]).<Text :interface-target="mobile">This is a phone</Text> <Text :interface-target="watch">This is a watch</Text><Text :interface-target="mobile">This is a phone</Text>- Casing:
How LiveView Native Components work
mainLiveView Native Components differ from standard Phoenix Components in their function arity. While Phoenix components typically use a single-argument function (
component/1), LiveView Native components use a two-argument function:component(assigns, interface).assigns: The standard map of data for the component.interface: A map containing metadata about the connecting native client. This allows you to tailor UI based on the device or client version.
The Interface Map
Each native client can send an
interfacemap containing:"target": The specific device target (e.g.,ios,ipados,watchos,macos,tvos)."version": The application version set in the native client build."client_version": The version of the client library connecting to the server.
def render(assigns, %{"target" => "watchos"}) do ~LVN""" <Text>Hello, from WatchOS!</Text> """ endHandle multiple targets for the same template
mainLiveView Native supports multiple targets for a single template name using a
+suffix in the filename. When usingembed_templates/2, the renderer generates specialized functions for each target and a fallback function for the general case. The fallback is always sorted to appear last, allowing for greedy matching.Example File Pattern:
home_live.swiftui+watchos.neexhome_live.swiftui+tvos.neexhome_live.swiftui.neex(Fallback)
Generated Function Signatures:
def render(assigns, %{"target" => "watchos"} = interface) def render(assigns, %{"target" => "tvos"} = interface) def render(assigns, interface) # FallbackDefine a LiveView Native Render Component
mainTo create a format-specific render component (e.g., for SwiftUI), use
LiveViewNative.Componentwith the:formatand:asoptions. The:asoption allows you to specify the name of the injected rendering function (defaulting torender).defmodule MyAppWeb.HomeLive.SwiftUI do use LiveViewNative.Component, format: :swiftui, as: :render endIf you have a file at
swiftui/home_live.swiftui.neex, theusemacro will automatically embed it asrender/2.defmodule MyAppWeb.HomeLive.SwiftUI do use LiveViewNative.Component, format: :swiftui, as: :render endEnable LiveView Native in a Phoenix LiveView
mainTo delegate rendering requests from a native client to format-specific components, use the
LiveViewNative.LiveViewmacro within your Phoenix LiveView module.Key Responsibilities:
- Event Handling: All event handling logic should remain in the original LiveView module.
- Rendering: All rendering concerns for a specific format (e.g., SwiftUI, Jetpack) are delegated to the components identified in the
:layoutsand:formatsoptions.
When a native client requests a specific format, LiveView Native will use the configured
dispatch_tofunction to find the appropriate module and call itsrender/1function.defmodule MyAppWeb.HomeLive do use MyAppWeb, :live_view use LiveViewNative.LiveView, formats: [:swiftui, :jetpack], layouts: [ swiftui: {MyAppWeb.Layouts.SwiftUI, :app}, jetpack: {MyAppWeb.Layouts.Jetpack, :app} ], dispatch_to: &Module.concat/2 endConfigure LiveView Native within a Phoenix application
mainUse the
mix lvn.setup.configcommand to automatically configure your Phoenix LiveView application to support LiveView Native. This task patches several key Phoenix configuration files to integrate LiveView Native plugins, MIME types, format encoders, template engines, and live reload patterns.Important Requirements:
- This command must be invoked from within your
*_webapplication root directory (not the umbrella root if using an umbrella project). - After running this, you should typically run
mix lvn.setup.gento generate the necessary files.
mix lvn.setup.config- This command must be invoked from within your