[039.2] Building Native Mobile Apps with Elm

Elm and React Native in Holy Matrimony, via elm-native.

Subscribe now

Building Native Mobile Apps with Elm [03.10.2017]

Today we're going to start a series covering elm-native-ui. It's a way to build native mobile apps in Elm, by leveraging the React Native ecosystem. Let's get started.

Project

We'll start out by cloning elm-native-ui. We'll make a directory to hold it and any elm-native-ui projects that we'll work on.

mkdir ~/elm/elm-native-ui
cd ~/elm/elm-native-ui
git clone git@github.com:ohanhi/elm-native-ui.git

Right now the repo has a couple of examples in it. There's a README in the Counter example, but we'll just go through it.

cd elm-native-ui/examples

We'll make a new React Native app named Counter - this will place the app inside of the existing Elm application's folder.

react-native init Counter

This overwrote a few files that we want to revert. We'll cd into the directory and revert the files with git:

cd Counter
git checkout -- index.ios.js index.android.js package.json

Now we'll compile the Elm application using an npm script:

npm run compile

And now we can just run the app:

react-native run-ios

Now it's spinning up the iOS emulator and running the packager to turn our JavaScript application into native code. After the emulator comes up, we can see the counter application running. Let's have a look at the code for this.

vim Main.elm
module Main exposing (..)

-- Here we import all of the `NativeUi` modules that we need to use functions
-- from - this provices us with equivalents to the `Html` versions, but for an
-- entirely not-HTML application.
import NativeUi as Ui exposing (Node)
import NativeUi.Style as Style exposing (defaultTransform)
import NativeUi.Elements as Elements exposing (..)
import NativeUi.Properties exposing (..)
import NativeUi.Events exposing (..)


-- MODEL


-- Our model looks just like we would expect from a counter
type alias Model =
    Int


model : Model
model =
    9000



-- UPDATE


-- Our `Msg` is just what we'd normally see
type Msg
    = Increment
    | Decrement


-- As is our update
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Increment ->
            ( model + 1, Cmd.none )

        Decrement ->
            ( model - 1, Cmd.none )

So aside from different imports, this feels like a very typical Elm application so far. Let's have a look at the view. We'll be producing a Node Msg instead of an Html Msg, and we use functions that are native-specific, but otherwise it should feel very much like a traditional HTML Elm application:

-- VIEW


view : Model -> Node Msg
view count =
    -- We start with a `view`, which is equivalent to a `<View>` in JSX
    -- Think of this like a `div`
    Elements.view
        -- We create a style attribute that centers its items
        [ Ui.style [ Style.alignItems "center" ]
        ]
        -- Then we have the child nodes, just like we would in HTML
        -- There's an image node with a few styles
        [ image
            [ Ui.style
                [ Style.height 64
                , Style.width 64
                , Style.marginBottom 30
                ]
            -- And we specify the sourceUri parameter for this node.
            , sourceUri "https://raw.githubusercontent.com/futurice/spiceprogram/master/assets/img/logo/chilicorn_no_text-128.png"
            ]
            [] -- The image has no children, of course
        -- Then some text to show the current state of the counter
        , text
            [ Ui.style
                [ Style.textAlign "center"
                , Style.marginBottom 30
                ]
            ]
            -- In Html, we'd use `text` here, but in native `text` is a node
            -- with childen so we use a function named `string` instead
            [ Ui.string ("Counter: " ++ toString count)
            ]
        -- Then we have another `view` to hold the buttons
        , Elements.view
            -- native uses flexbox for layout
            [ Ui.style
                [ Style.width 80
                , Style.flexDirection "row"
                , Style.justifyContent "space-between"
                ]
            ]
            -- Then there's a `button` function we'll see momentarily - it takes
            -- the `Msg` to emit when pressed, a string specifying the color,
            -- and a string specifying the contents of the button
            [ button Decrement "#d33" "-"
            , button Increment "#3d3" "+"
            ]
        ]


-- Here's the button function's definition - it's actually just a `text` node
-- with certain button-like styles and an `onPress` attribute
button : Msg -> String -> String -> Node Msg
button msg color content =
    text
        [ Ui.style
            [ Style.color "white"
            , Style.textAlign "center"
            , Style.backgroundColor color
            , Style.paddingTop 5
            , Style.paddingBottom 5
            , Style.width 30
            , Style.fontWeight "bold"
            , Style.shadowColor "#000"
            , Style.shadowOpacity 0.25
            , Style.shadowOffset 1 1
            , Style.shadowRadius 5
            , Style.transform { defaultTransform | rotate = Just "10deg" }
            ]
        , onPress msg
        ]
        [ Ui.string content ]



-- PROGRAM


-- Then our `main` is just a `Ui.Program` type, wired together like a typical
-- `Html.program`.
main : Program Never Model Msg
main =
    Ui.program
        { init = ( model, Cmd.none )
        , view = view
        , update = update
        , subscriptions = \_ -> Sub.none
        }

This feels very natural, but we're making native iOS and Android apps! We'll look at elm-native-ui further, but this wraps up our introduction.

Summary

Today we looked at building native apps in Elm with elm-native-ui. It's still very early in this package's life, so I wouldn't be surprised if things change rapidly. Still, it's exciting how easy it is to build out native applications with Elm, with very little effort. I hope you enjoyed it. See you soon!

Resources