Current section

Files

Jump to
live_view_native lib live_view_native.ex
Raw

lib/live_view_native.ex

defmodule LiveViewNative do
@moduledoc ~S'''
This module should be used for creating new LiveView Native Clients.
## Configuration
To use LiveView Native you must configure application
to register LiveView Native plugins and instruct
Phoenix on how to handle certain formats.
# registers each plugin for use
config :live_view_native, plugins: [
LiveViewNative.SwiftUI,
LiveViewNative.Jetpack
]
# configure the mimetype for each plugin's format
# refer to the plugin's documentation for each format
# in nearly all cases the content-type should be `"text/:format"`
config :mime, :types, %{
"text/swiftui" => [:swiftui],
"text/jetpack" => [:jetpack]
}
# configures the stylesheet for each format
# refer to the documentation of `LiveViewNative.Stylesheet`
# for more information on configuration options
config :live_view_native_stylesheet,
content: [
swiftui: [
"lib/**/*swiftui*"
],
jetpack: [
"lib/**/*jetpack*"
]
]
# instructs Phoenix on how to encode a given format
config :phoenix_template, :format_encoders, [
swiftui: Phoenix.HTML.Engine,
jetpack: Phoenix.HTML.Engine
]
# instructs Phoenix on which engine to
# use when compiling `neex` templates
config :phoenix, :template_engines,
neex: LiveViewNative.Engine
Next you should add `LiveViewNative.LiveReloader` to your application's endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
# add here to ensure it is only enabled
# when your application is configured for code reloading
plug LiveViewNative.LiveReloader
Finally you will need to configure your router's plugs
plug :accepts, ["html", "swiftui", "jetpack"]
plug :put_root_layout,
html: {MyAppWeb.Layouts, :root},
swiftui: {MyAppWeb.Layouts.SwiftUI, :root},
jetpack: {MyAppWeb.Layouts.Jetpack, :root}
Refer to the documentation in `LiveViewNative.Component` for more
information on native layouts.
You are now ready to create your first LiveView Native app. Refer to
the documentation in `LiveViewNative.LiveView` on how to configure
your live views for native.
## Enabling LiveView for Native
Now that your application is configured you will need to enable a LiveView for Native.
Assuming your application has the following LiveView route:
live "/", HomeLive
You'll need to add `use MyAppNative, :live_view` to that module:
defmodule MyAppWeb.HomeLive do
use MyAppWeb, :live_view
use MyAppNative, :live_view
Next you should use the `mix lvn.gen.live` task to generate
the LiveView Native render component and template.
> mix lvn.gen.live swiftui Home
This will generate the following files in your project:
* lib/my_app_web/live/home_live.swiftui.ex
* lib/my_app_web/live/swiftui/home_live.swiftui.neex
Just like a regular LiveView you can decide not to use the template and render
templates in-line inside the render component:
defmodule MyAppWeb.HomeLive.SwiftUI do
use MyAppNative, [:render_component, format: :swiftui]
def render(assigns, _interface) do
~LVN"""
<Text>Hello, LiveView Native!</Text>
"""
end
end
Note that unlike a regular LiveView that has `render/1` the
LiveView Native render component uses `render/2` as the 2nd argument
will include interface information about the client.
You can read more in `LiveViewNative.Component`
'''
import LiveViewNative.Utils, only: [
stringify: 1
]
@doc """
Uses LiveViewNative for creating new clients
defmodule GameBoy do
use LiveViewNative,
format: :gameboy,
component: GameBoy.Component,
module_suffix: GameBoy,
template_engine: LiveViewNative.Engine,
client: GameBoy.Client
end
## Options
* `:format` - the format that will be used by Phoenix to determine the request
and response encoding. Should be all lowercase and underscored
* `:component` - module name of the client's component module to be used within
render components
* `:module_suffix` - the module name that will be assumed to be appended to
liveviews for render components (i.e. MyAppWeb.HomeLive.GameBoy)
* `:template_engine` - the engine for compiling a client's template. In nearly
all cases `LiveViewNative.Engine` will be sufficient
"""
defmacro __using__(config) do
quote bind_quoted: [config: config] do
defstruct format: config[:format],
component: config[:component],
module_suffix: config[:module_suffix],
template_engine: config[:template_engine],
stylesheet: config[:stylesheet],
stylesheet_rules_parser: config[:stylesheet_rules_parser],
test_client: config[:test_client]
def __lvn_client__, do: true
end
end
@doc"""
Fetches a plugin based upon the format name
Follows the same return types as `Map.fetch/2`
"""
def fetch_plugin(format) do
Map.fetch(plugins(), stringify(format))
end
@doc"""
Fetches a plugin based upon the format name
If the format is not present `LiveViewNative.PluginError` is raised
"""
def fetch_plugin!(format) do
case fetch_plugin(format) do
{:ok, format} -> format
:error -> raise LiveViewNative.PluginError, format
end
end
@doc"""
Returns a list of all available LiveView Native plugins
Only the plugins that have been registered in your application
config will be returned in the list
"""
def plugins() do
case Application.fetch_env(:live_view_native, :plugins_map) do
{:ok, plugins} -> plugins
:error ->
plugins =
Application.fetch_env(:live_view_native, :plugins)
|> case do
{:ok, plugins} ->
Enum.map(plugins, fn(plugin) ->
Code.ensure_compiled(plugin)
struct(plugin)
end)
:error -> []
end
|> Enum.into(%{}, &({Atom.to_string(&1.format), &1}))
:ok = Application.put_env(:live_view_native, :plugins_map, plugins)
plugins
end
end
@doc"""
Returns a list of all available formats
The format list is derived from the plugins returned by `LiveViewNative.plugins/0`
"""
def available_formats() do
plugins()
|> Map.values()
|> Enum.map(&(&1.format))
end
end