Realtime native apps made easy with Fuse tools and Phoenix Channels

Back to Show and Tell

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:

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>

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.

Hi Aleme,

and thanks for sharing. Top stuff!

Would you consider making a ready-to-run example and put it somewhere on github?

Uldis wrote:

Hi Aleme,

and thanks for sharing. Top stuff!

Would you consider making a ready-to-run example and put it somewhere on github?

Hi Uldis,

I have tried this example and I am facing one problem, which can be related to latest Fuse. When ever I just include .js script file my app is crashing on Android preview, but works on emulator. I have tried socket.io 1.4.5 - 2.0.0 and also this phoenix-common script. But when ever I just call js file using this below, my app starts crashing.

var myvar = require("phoenix-common");
// same thing when just call socket.io library, or ANY js library
// var myvar = require("socket.io-1.4.5");

In the Problems log screen I see this error:

Timer.delete(): The parameter is not a valid timer handle

I did also tried different js libraries and socket.io versions. But I think that it is related to something else. I'v tested this on two different android devices. Its not related to device.

I am also using this:

{
  "RootNamespace":"",
  "Packages": [
      "Fuse.Maps",
      "Fuse.Launcher.Maps",
      "Fuse",
      "FuseJS",
      "Fuse.ImageTools",
      "Uno.Net.Sockets"
  ],

  "Projects": [],
    "Android": {
    "Package": "com.myappname.android",
    "Geo": {
        "ApiKey": "my-key"
    }
},
  "Includes": [
    "*",
    "socket.io-1.4.5.js:Bundle"
  ]
}

Please if someone have any idea let me know. More details here: https://www.fusetools.com/community/forums/show_and_tell/socketio?page=1&highlight=f12eb417-3b37-4ce7-9d87-748262617396#post-f12eb417-3b37-4ce7-9d87-748262617396

Thanks in advance.

Post Stats
  • 4
    replies
  • 472
    views
  • 3
    users
Frequent Posters