About This Doc

This tutorial will get you started developing native mobile apps in Clojure using Vee. It’s written for getting started with iOS development, but should work very similarly for other platforms. If you’d like to port this doc to Android, Windows Phone, etc, please add it here.

There are many approaches to building apps with Vee, and what’s outlined in this document is just one approach to keeping your codebase maintainable as it scales. The best approaches are still out there to discover, so start exploring and report your findings.

About Vee

Vee is a set of abstractions, tools, and library code built on Appcelerator’s Titanium, which embeds a JavaScript interpreter inside a mobile application and allows you to create native UI elements and interfaces with native APIs. Titanium supports the iOS, Android, Windows Phone, Blackberry, and Tizen platforms out of the box.

Vee adds a ClojureScript layer on top of Titanium. Building mobile apps using Vee feels a lot like building web apps in ClojureScript. You manage application state, react to events, and create / modify / destroy UI elements (interacting with the platform’s native UI elements instead of the DOM).

Vee is alpha-quality software; it’s a starting point for how apps should be built with this type of approach. We’ll need better abstractions than the ones provided in alpha to make apps made with Vee understandable and maintainable. The best thing you can do to help is to share your experiences (good and bad) on the Mailing List.

The Titanium documentation is a great resource to reference as you’re building your apps. In many cases options are passed directly through to Titanium APIs, and documentation for these options can be found on the corresponding doc pages.

Installation

Follow the instructions in the Vee starter repo. Come back here once you’ve got a simulator running.

Here’s a picture to help mark your place:

Ok! Now that you have the simulator up, first let’s make sure everything seems to be working. Pull up src/vs/ios.cljs and make a change to the file. Chaging the title of the button found around line 20 from "Tap Me" to something else, save the file, and you should see the button title updated in the simulator in about a second.

Didn’t work? Open up a ticket with some detailed info about your os / JVM / etc versions and we’ll try to get it sorted.

App Entry Point

In Vee, the entry point into your app is defined in Resources/app.js. If you take a look at the end of the file, you’ll see a block that looks like this:

if ("iPhone OS" == Ti.Platform.name) {
  goog.require("vs.ios");
} else if ("android" == Ti.Platform.name) {
  goog.require("vs.android");
}

This is the entry point into ClojureScript, on iOS the vs.ios namespace is loaded. You’ll find this namspace in src/vs/ios.cljs. You’ll see we’ve got an app skeleton there for you that does some basic setup like creating a main window and initializing the code reloader.

Structuring your app in a way that makes it easy to start and stop makes it more maintainable, and allows you to take advantage of the code reloading facilities provided by Vee.

The reloader expect you to pass in start and stop functions for your app. Your start-app function should do data initialization, set up mutable state, create event loops, and create the initial views that your users will see. Your stop-app function should be a reverse of this process: closing views, closing database connections, stopping event loops, etc. When source code changes are detected it will call your stop-app function, load any new / changed code, and call the start app function.

You can see an example of this in the Vee starter repo at vs.ios/start-app and vs.ios/stop-app.

Core Concepts

UI Elements

Vee provides wrappers for Titanium UI elements. vee.ti/win will create a Ti.UI.Window, vee.ti/label will create a Ti.UI.Label, etc. In ios.cljs, for example, you’ll see we create a window with a button in it.

Windows are the basic top-level container for your UI elements. Most of the time you can’t put anything on the screen without first putting it in a window. They also have some special lifecycle events like close.

Generally the Vee UI element wrappers take a config map as their first argument. This map gets passed onto the respective Titanium UI element creation function. This map is transformed in a way to allow you to make config more idiomatic, but it’s essentially one-to-one (there’s some stuff in there to set up event and state binding). The following calls are fairly equivalent:

;; cljs
(ti/win {:background-color "#f77"})

;; js
Titanium.UI.createWindow({backgroundColor: "#f77"});

Many Vee UI elements allow you to pass in children as subsequent arguments, or as a collection:

(ti/win {:background-color "white"
         :layout "vertical"}
  (ti/label {:text "Hello 1"})
  (ti/label {:text "Hello 2"})
  (ti/label {:text "Hello 3"})
  (ti/label {:text "Hello 4"})
  (ti/label {:text "Hello 5"}))

;; Or

(ti/win {:background-color "white"
         :layout "vertical"}
  (for [i (range 5)]
    (ti/label {:text (str "Hello " (inc i))})))

Events

Reacting to interactions in your app is accomplished in one of two ways. On creation, most UI elements allow you to specify event handlers by setting the :events key-value pair on the options map, like so:

(ti/button
  {:text "Tap Me"
   :events
   {:click (fn [e]
             (println "Tapped"))}})

The keys of this map correspond to event names. See the Titanium documentation for events triggered by specific elements.

Layout

Titanium has a great intro to the layout system, go check it out.

Network Comm

Vee comes with an HTTP client in callback (vee.http/request) and core.async channel (vee.http/request-ch) flavors. These behave pretty much as you would expect an HTTP client to behave, and are configured with an options map where you specify things like HTTP method, URL, headers, and (in the case of vee.http/request), success and error callbacks.

This options map takes an :encoding key-value pair of possible values :text (default), :json, or :transit. The latter two will automatically serialize whatever’s found at the :data KVP into the specified format.

Additionally, the response body is deserialized automatically based on the response’s Content-Type header. JSON and Transit are supported at this time.

Here’s the schema for a response passed into the success / error callbacks (request) or received via the returned channel (request-ch):

{:success? Bool
 :status Number
 :body String or Clj Value (if decoded)
 :xhr Raw JS XHR object
 :event Event passed to 'onload' callback}

Here are a few examples:

;; Print the page source for google.com
(http/request
  {:method :get
   :url "https://google.com"
   :success (fn [e]
              (println (:body e)))})


;; Transit-encode and post
(go
  (println
    (<! (http/request-ch
          {:method :post
           :url "https://example.com/save-user"
           :encoding :transit
           :headers {"User-Agent" "it's me"}
           :data {:username "zk"
                  ;; I was having lunch when I wrote this
                  :favorite-soups ["Tomato"
                                   "Clam Chowder"
                                   "French Onion"]}
           :success (fn [e]
                      (println (:body e)))}))))

Basic App Structure

Building apps using Vee feels a lot like building browser apps in ClojureScript. Your app loads, you create some UI and set up processing loops. You respond to UI events like button taps and text input changes. You contact the server and update the UI with data from the response. Stuff like that.

Centralized Mutable App State

You’ll want to contain your app state to a few (or one) Usually you’ll want to contain your app state in a small set of State-Paths allow you to compose updates.

Central App Loop a.k.a vee.op(eration)s

Major holes here (like how to debounce messages, magic around inclusion into namespaces) that will need to be fixed, but it’s handy. Events send messages to central dispatch with a combo of global context and local state.

Isolation comes in the form of namespacing keywords.

Outline

  • About
    • Starting point, better abstractions, better tooling, better bindings.
    • Obsolete warning (may become obsolete very soon, updated accordingly)
  • Getting Started
  • Basic App Structure (iOS vs Android where appropriate)
  • Core Concepts
    • Ti Docs
  • Basic Views
    • Windows
    • Views
    • Labels, Buttons, Images
    • Layout
    • Animations
  • Events
  • Network
  • Contributing