This content originally appeared on DEV Community and was authored by Rushikesh Pandit
Hi all,
This week, I have come up with the exciting new blog in which I am going to explain you how to handle state in mobile application which we are building with the help of LiveView Native by Dockyard.
In my earlier blog, I have explained how to setup the LiveView Native project, We are going to continue from the same step from where we have left it.
If you have not gone through that blog, please check it out here in the following link.
https://dev.to/rushikeshpandit/mobile-app-development-with-liveview-native-and-elixir-4f79
Let's start writing the actual code for counter example.
Navigate to lib/native_demo_web/live/
directory and change the code of home_live.swiftui.ex as shown below.
home_live.swiftui.ex
defmodule NativeDemoWeb.HomeLive.SwiftUI do
use NativeDemoNative, [:render_component, format: :swiftui]
def render(assigns, _interface) do
~LVN"""
<VStack id="hello-ios">
<HStack>
<Text class={["bold(true)"]} >Hello iOS!</Text>
</HStack>
<HStack>
<.link navigate={"/counter"} >
<Text>Counter Demo</Text>
</.link>
</HStack>
</VStack>
"""
end
end
Also, replace the render
method of home_live.ex
with below code.
def render(assigns) do
~H"""
<div>
Hello from Web <br />
<br />
<button
phx-click="navigate"
class="text-stone-100 bg-indigo-600 font-semibold rounded py-2.5 px-3 border border-indigo-600 transition hover:bg-indigo-700"
>
<.link href={~p"/counter"}>Go to counter example</.link>
</button>
</div>
"""
end
In above code, we have added link
component in mobile app and button
component in web app, which will navigate user to /counter
route.
Now, navigate to lib/native_demo/
and create a new file with name counter.ex
and paste the following content.
counter.ex
defmodule NativeDemo.Counter do
use GenServer
alias __MODULE__, as: Counter
@initial_state %{count: 0, subscribers: []}
# Client
def start_link(_initial_state) do
GenServer.start_link(Counter, @initial_state, name: Counter)
end
def increment_count do
GenServer.call(Counter, :increment_count)
end
def get_count do
GenServer.call(Counter, :get_count)
end
def join(pid) do
GenServer.call(Counter, {:join, pid})
end
def leave(pid) do
GenServer.call(Counter, {:leave, pid})
end
# Server (callbacks)
def init(initial_state) do
{:ok, initial_state}
end
def handle_call(:increment_count, _from, %{subscribers: subscribers} = state) do
new_count = state.count + 1
new_state = %{state | count: new_count}
notify_subscribers(subscribers, new_count)
{:reply, :ok, new_state}
end
def handle_call(:get_count, _from, state) do
{:reply, state.count, state}
end
def handle_call({:join, pid}, _from, state) do
Process.monitor(pid)
{:reply, :ok, %{state | subscribers: [pid | state.subscribers]}}
end
def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
{:noreply, %{state | subscribers: Enum.reject(state.subscribers, &(&1 == pid))}}
end
# Private functions
defp notify_subscribers(subscribers, count) do
Enum.each(subscribers, fn pid -> send(pid, {:count_changed, count}) end)
end
end
In above file, we are starting a GenServer with the initial state as %{count: 0}
. Also we are have written couple of methods such as increment_count
, get_count
which eventually gets handled by GenServer and changes will be notified to all the subscribers.
You can read more about GenServer in the following link.
https://dev.to/rushikeshpandit/demystifying-elixir-genservers-building-resilient-concurrency-in-elixir-9jm
As we have written GenServer, next step is to start it. To do this, we have to navigate to application.ex
which is inside same directory and over there we need to find def start(_type, _args)
method and add NativeDemo.Counter
to the children's array. Start method should look something like this.
Now, navigate back to lib/native_demo_web/live/
and create 2 files with name counter_live.ex
and counter_live.swiftui.ex
counter_live.ex
defmodule NativeDemoWeb.CounterLive do
use NativeDemoWeb, :live_view
use LiveViewNative.LiveView,
formats: [:swiftui],
layouts: [
swiftui: {NativeDemoWeb.Layouts.SwiftUI, :app}
]
alias NativeDemo.Counter
@impl true
def render(assigns) do
~H"""
<div>
<div class="text-slate-800 bg-slate-50 content-center items-center text-center">
<.back navigate={~p"/home"}>Back to Home</.back>
<div class="mb-2.5">This button has been clicked <%= @count %> times.</div>
<div>
<button
phx-click="increment-count"
class="text-stone-100 bg-indigo-600 font-semibold rounded py-2.5 px-3 border border-indigo-600 transition hover:bg-indigo-700"
>
<span>Click me</span>
</button>
</div>
</div>
</div>
"""
end
@impl true
def mount(_params, _session, socket) do
Counter.join(self())
{:ok, assign(socket, :count, Counter.get_count())}
end
@impl true
def handle_info({:count_changed, count}, socket) do
{:noreply, assign(socket, :count, count)}
end
@impl true
def handle_event("increment-count", _params, socket) do
NativeDemo.Counter.increment_count()
{:noreply, socket}
end
end
counter_live.swiftui.ex
defmodule NativeDemoWeb.CounterLive.SwiftUI do
use NativeDemoNative, [:render_component, format: :swiftui]
def render(assigns, _interface) do
~LVN"""
<.header>
Counter
</.header>
<HStack>
<Text class={["bold(true)"]}>This button has been pressed <%= @count %> times.</Text>
</HStack>
<HStack>
<Button phx-click="increment-count">
<Text >Press me</Text>
</Button>
</HStack>
"""
end
end
Explanation for above code.
In the mount
method of counter_live.ex
, we are adding our live view process to GenServer and getting the value of count. and in the render(assigns)
method, we have a button with the phx-click
which sends an event with name increment-count
. In the handle_event
method, we are incrementing the count by one. Our GenServer then notify the all subscriber with the event count_changed
. Once our Live view gets this event, handle_info
comes in actions and update the count which was incremented by the server. This happens in case of live view web.
Mobile app is pretty straightforward. In counter_live.swiftui.ex
mobile app is showing only value of count which is added into socket by webview and there is one button with the phx-click
which sends an event with name increment-count
. That's it. Rest all is taken by live view.
Once you done with all the changes mentioned above, head to the router.ex
which is inside lib/native_demo_web
directory and add following line.
live "/counter", CounterLive
Your router file should look something like this.
Now, run the application using iex -S mix phx.server
and hit http://localhost:4000/counter
on the browser.
You will see the following.
Now, open native/swiftui/NativeDemo.xcodeproj
using xcode and try to run the application,
you should be able to see the following.
Tap on Counter Demo
button. It will navigate you to next page as shown below.
If you are able to see this, then Congratulations!!!
Now, lets see the live view native in action.
Congratulations!!!
You have successfully created and setup GenServer utilize it into mobile app to manage the state.
You can find sample code on GitHub
In case if you face any issue, try deleting _build
directory and compile code again using mix compile
command.
If you have any suggestions/doubts or stuck somewhere in this process, feel free to reach to me via one of the following method.
LinkedIn : https://www.linkedin.com/in/rushikesh-pandit-646834100/
GitHub : https://github.com/rushikeshpandit
Portfolio : https://www.rushikeshpandit.in
In my next blog, I will be try to add some styles to the mobile app and also try to cover some more concepts.
Stay tuned!!!
#myelixirstatus , #liveviewnative , #dockyard , #elixir , #phoenixframework
This content originally appeared on DEV Community and was authored by Rushikesh Pandit
Rushikesh Pandit | Sciencx (2024-07-01T13:37:42+00:00) Mobile app development with LiveView Native and Elixir. Part – 2. Retrieved from https://www.scien.cx/2024/07/01/mobile-app-development-with-liveview-native-and-elixir-part-2/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.