Skip to content

Instantly share code, notes, and snippets.

@egaleme
Created June 19, 2017 01:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egaleme/bf708208a443e530134a536eb982ad6a to your computer and use it in GitHub Desktop.
Save egaleme/bf708208a443e530134a536eb982ad6a to your computer and use it in GitHub Desktop.
Realtime native apps made easy with Fuse tools and Phoenix Channels

Realtime native apps made easy with Fuse tools and Phoenix Channels

Elixir’s Phoenix framework is an ideal choice for building real-time applications and Fuse tools is even easier for building native mobile apps.

In this tutorial i'll show how to build an app using Elixir's Phoenix framework channels and Fuse tools.We won't worry about persistance or authentication.

Installation and Setup

This tutorial will assume the following language and framework versions:

  • Erlang 19.0
  • Elixir 1.4
  • Phoenix 1.2
  • Fuse 1.0

If you need to insall Erlang, Elixir and Fuse tools, i recommend checking out their websites.

To install Phoenix Framework and its dependencies, follow the Phoenix installation docs. Once you are able to install Phoenix withIn the same file, we’ll also need to uncomment line 5, which I’ll explain next:

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

you should be ready to proceed.

Create New Phoenix App

I'will call it "MessageGram", and create it with

$ mix phoenix.new message_gram

When asked “Fetch and install dependencies?” enter “Y”. Finally, when the new app is generated, you should see the following message:

We are all set! Run your Phoenix application:

    $ cd message_gram
    $ mix phoenix.server

Before moving on, configure your database in config/de.exs and run:

    $ mix ecto.create 

Next, we’ll need to modify the default MessageGram.UserSocket module. In the same file, we’ll also need to uncomment line 5, which I’ll explain next:

## Channels
channel "room:*", MessageGram.RoomChannel

Of course, our MessageGram.RoomChannel module doesn’t exist yet. Let’s fix that

defmodule MessageGram.RoomChannel do
  use MessageGram.Web, :channel
	def join("room:lobby", payload, socket) do
		{:ok, socket}
	end

	def join("room:" <> _private_room_id, _params, _socket) do
		{:error, %{reason: "Unauthorized"}}
	end

	def handle_in("new_msg", %{"message" => message, "at" => at}, socket) do
		broadcast! socket, "new_msg", %{message: message, at: at}
		{:noreply,  socket}
	end

	def handle_out("new_msg", payload, socket) do
		push socket, "new_msg", payload
		{:noreply, socket}
	end
end

It's finally time to write our Fuse app.

First we'll need the Phoenix Channel javascript client to enable our Fuse app communicate with our Phoenix realtime backend. Since the client that comes with Phoenix by default is written in Es6, we'll need an Es5 version beacuse Fuse tools only support Es5. Go ahead and download a Phoenix Channel javascript client Es5 version

We are all set! Create our Fuse app:

$ fuse create app MessageGram

Let's add the phoenix-common.js file to the root of our app and modify our MessageGram.unproj file:

{
  "RootNamespace":"",
  "Packages": [
    "Fuse",
    "FuseJS"
  ],
  "Includes": [
    "*",
    "phoenix-common.js:Bundle",
  ]
}

This will bundle the phoenix javascript client with our app to enable us use require("phoenix-common") in our ux file.

Lets build our message app in our MainView.ux file:

<App>
	<JavaScript>

	var Observable = require("FuseJS/Observable");
	var messages= Observable();
	var txt = Observable("");
	var Phoenix = require("phoenix-common");
	var channel = null;

	var socket = new Phoenix.Socket("ws://localhost:4000/socket")

	fetch("http://localhost:4000", {
		method: "get"
	}).then(function(resp) {
		
			socket.connect();
		
	}).catch(function(err) {
		console.log("Error connecting to server")
	})
	
	
	channel = socket.channel("room:lobby", {});
	channel.on("new_msg", function(payload) {
		console.log("Main mesage: "+payload.message)
		 
		messages.add(payload)
	})
	channel.join().receive("ok", function(resp) {
		console.log("Joined successfully")
	}).receive("error", function(resp) {
		return console.log("Unable to join", resp)
	})

	function sendClick() {
		channel.push("new_msg", {message: txt.value, at: new Date().toDateString()})
		txt.value = ""

	} 

	module.exports = {
		sendClick,
		txt,
		messages
	};

	</JavaScript>
	<Panel Background="#183f">
	<Grid Rows="1*,1*">
		<ScrollView>
			<StackPanel ItemSpacing="10">
				<Each Items="{messages}">
					<StackPanel Orientation="Horizontal" ItemSpacing="5">
						<Text TextColor="White" Value="{message}"/>
						
						<Text TextColor="White" Value="{at}"/>
				   </StackPanel>
				</Each>
			</StackPanel>
		</ScrollView>
		<StackPanel HitTestMode="LocalBoundsAndChildren">
			<TextInput PlaceholderText="text" PlaceholderColor="#ffffff80" TextColor="White" Width="250" Value="{txt}" />
			 <Rectangle Color="#127799" Height="40" Margin="20" Width="100" CornerRadius="80">
              <Text  Value="Send" Alignment="Center" TextColor="White" Clicked="{sendClick}"/>
            </Rectangle>
		</StackPanel>
	</Grid>
</Panel>
</App>

Then run our app with:

$ fuse preview

And that’s it! We can test this by opening our app in multiple browser tabs to simulate multiple users talking to each other in real-time and with our fuse native app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment