ClojureScript Ejecta

April 29, 2017

Ejecta is a fast browserless implementation of HTML canvas for iOS and tvOS.

It's very easy to set things up so that you can drive Ejecta using ClojureScript.

First, download Ejecta, which ships as an Xcode project. Also, ensure that you have CocoaPods installed.

We'll make a few minor modifications to the Ejecta project which will allow us to establish a ClojureScript REPL into iOS and drive Ejecta:

Create a Podfile file in the top of the Ejecta project tree:

platform :ios, '10.0'

target 'Ejecta' do
  pod 'Ambly', '~> 1.0.0'

With this in place, run these commands

$ pod install
$ cp /dev/null App/index.js

Then open Ejecta.xcworkspace. If the pod install command recommend making changes to the project—to perhaps set build settings to $(inherited)—do so now.

Find AppDelegate.m and make a couple of mods to hook in Ambly:

Before @implementation AppDelegate, add these lines

#import "ABYContextManager.h"
#import "ABYServer.h"

@interface AppDelegate ()
@property (strong, nonatomic) ABYContextManager* contextManager;
@property (strong, nonatomic) ABYServer* replServer;

and add these lines prior to the return YES; at the end of application:didFinishLaunchingWithOptions:

NSURL* out = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
self.contextManager = [[ABYContextManager alloc] initWithContext:((EJJavaScriptView*)window.rootViewController.view).jsGlobalContext compilerOutputDirectory:out];
[self.contextManager setUpAmblyImportScript];
self.replServer = [[ABYServer alloc] initWithContext:self.contextManager.context compilerOutputDirectory:out];
[self.replServer startListening];

With these revisions, run the modified Ejecta app in the simulator or on a device.

In another directory set up a small ClojureScript project. Do lein new ejecta and edit the resulting project.clj to specify [org.clojure/clojurescript "1.9.521"] and [ambly "1.0.0"] as :dependencies.

Then lein repl and connect by evaluating these two forms and then choosing your device from the list presented:

(require '[cljs.repl :as repl] '[ambly.core :as ambly])
(repl/repl (ambly/repl-env))

You can now draw on the Ejecta canvas element by evaluating ClojureScript like this:

(let [ctx (-> js/document
              (.getElementById "canvas")
              (.getContext "2d"
                           #js {:antialias        true
                                :antialiasSamples 4}))]
  (set! (.-fillStyle ctx) "#FFFFFF")
  (.beginPath ctx)
  (.arc ctx 300 150 100 0 (* 2 Math/PI))
  (.fill ctx)
  (set! (.-lineWidth ctx) 12)
  (set! (.-strokeStyle ctx) "#96CA4B")
  (.beginPath ctx)
  (.arc ctx 300 150 86 1.57 4.71)
  (.stroke ctx)
  (set! (.-strokeStyle ctx) "#5F7FBF")
  (.beginPath ctx)
  (.arc ctx 300 150 86 4.71 1.57)
  (.stroke ctx)
  (.fillRect ctx 294 55 12 190))

With any luck, you should see:

Of course, there is much more you can do, such a loading namespaces containing drawing code using require, or bundling all your code using :advanced and including that in the app, but above minimal example should get you started.

Have fun!

Tags: Ambly ClojureScript