Use Enterlab Rente to create a Cordova backed native app for Android and iOS using OnsenUI for user interface, Facebook React wrapped in ClojureScript Reagent using AJAX for logging on to a remote server and websocket via Sente for app/server communication
(Wheew!)
- git clone https://github.com/enterlab/rente.git [your-app]
- cd [your-app]
- rm app.json
- Replace all instances of “rente” in the project with [your-app]; cljs, clj and project.clj
- Change project.clj “dev-resources/www/js/out” to “resources/www/js/out”
- Change project.clj occurrences of “public” to “www”, including :main, :output-to, :output-dir, :http-server-root, :css-dirs
- Rename folder src/rente to src/[your-app]
- Build with lein clean && lein figwheel
When no build error, continue:
- CTRL+c to stop figwheel
- rm -rf resources
- cordova create resources [com.yourcompany.your-app] “Your app description”
- cordova platform add browser
- cordova platform add ios
- add other platforms: android, wp8 etc. if you want
- cordova plugin add cordova-plugin-device
- cordova plugin add org.apache.cordova.dialogs
- cd .. && lein clean && lein figwheel
In a new terminal tab (or window):
- lein run
This starts the server which respond to AJAX/socket connections
In yet another terminal tab/window:
- cd resources
- cordova prepare && cordova run browser
You should now see an Apache Cordova logo/text and a green box with the text “DEVICE IS READY” in your browser
- cordova run ios
You should now see the same, but this time in your iOS Emulator
- edit www/index.html:
- change last
- Add this just before the tag:
- Add ‘unsafe-inline’ in the meta tag containing the Content-Security-Policy. It should be put just after “default-src ‘self’” and just before “data:”
- In the end of the value of the tag, just after "media-src ‘*’, add “; connect-src ‘self’ ws://localhost:8080 ws://localhost:3449;”
- In index.html add id=“app” to the div element with class=“app”
- In resources folder: cordova run browser
See that the “APACHE CORDOVA” and the “DEVICE READY” has been replaced with content similar to that from Enterlab Rente.
This is rendered using React (via Reagent), which means that Reagent is rendering correctly.
If you open up your browser console, you should see that the app web socket has been connected as well, with a console log line such as:
Channel socket state change: {:type :ws, :open? true, :destroyed? false, :uid :taoensso.sente/nil-uid, :csrf-token nil, :first-open? true}
When you click the two buttons you will get the following console log results respectively:
CALLBACK from server: [:cordevicljs/testevent {:message “Hello socket from server Callback, received: {:message \”Hello socket Callback!\"}"}]
and:
PUSHed :cordevicljs/testevent from server: {:message “Hello socket from server Event (no callback), received: {:message \”Hello socket Event!\"}"}
- Add the following two functions in app.cljs, just above the main function:
(defn ^:export onDeviceReady []
(→ (js* “navigator”)
(.-notification)
(.alert “Device Native Bridge works!”
(fn [] nil)
""
"")))
(defn ^:export prepare-device []
(.addEventListener js/document “deviceready” onDeviceReady true))
- Then add a call to (prepare-device) as the first line in the main function
- One more: cordova run browser
You should now see a standard javascript alert showing that “Device Native Bridge works!”
Let’s test it on the device as well:
- cordova run ios
If you get an alert in the native app that looks like a native alert showing the exact same text, “Device Native Bridge works!”, then you’re good to go – you’ve got the native device bridge working, so you can communicate with most native device features via Cordova! w00t!
Now is a good time for a walk in the park with a great cup of coffee and perhaps a good friend/colleage etc.
Checkout tag: cljs-react-socket-device
- Add language=“en” ng-app=“app” attributes to index.html’s element
- Add to bottom of index.html element
- In top of app.cljs main function (before call to prepare-device), add this angular initialization:
(.module (.-angular js/window) “app” #js [])
- test in browser with cordova run browser
If you get the alert as usual, and reagent renders the Rente like content, it works.
- test in device like cordova run ios
If you get the alert there as well, you’re good to go on! :)
Checkout tag: cljs-react-socket-device-angular
To add support for OnsenUI (http://onsen.io) in CorDeviCLJS, perform the following steps:
- Above the newly inserted Angular.js reference (index.html), insert this:
- Below the link to angular.js, insert this:
- In app.cljs add “onsen” to the empty vector in the function call we added earlier. It should end up looking like this:
(.module (.-angular js/window) “app” #js [“onsen”])
Again: test both browser and ios emulator.
If both work, you have all the basics working, and can now develop your app as you wish! :)
If you continue we’ll add some native-ish look-and-feel to the app.
Checkout tag: cljs-react-socket-device-angular-onsen
Here we will add a native-like look and feel, resembling some sort of a flat UI usable for all mobile platforms.
- Delete the element in index.html, and replace it with this:
- Put these 2 functions in top of app.cljs (below the namespace declaration, duh..):
;; OnsenUI Interop
(defn ons-component [component dom-id & [callback]]
(with-meta component
{:component-did-mount
(fn [this]
(.compile js/ons (.getElementById js/document dom-id))
(when callback (callback)))}))
(defn ons-render [component dom-id state-wrapper & [callback]]
(reagent/render-component
[(ons-component component dom-id callback) state-wrapper]
(.getElementById js/document dom-id)))
- In view.cljs replace the main function with these two functions:
(defn footer []
[:ons-row {:style {:margin-top “10px” :text-align "center"}}
[:ons-col
[:p {:style {:color “#999” :font-size "13px"}}
“Click top left icon to close/open menu”
[:br]
“You can also swipe the page left/right.”]]])
(defn main [data]
[:div
[:span “Hello Clojure World!”]
[:br][:br]
[:ons-button {:modifier “large” :on-click socket/test-socket-callback} “Send Callback”]
[:br][:br]
[:ons-button {:modifier “large” :on-click socket/test-socket-event} “Send Event”]
[footer]])
As you can see, because of the small trick in ons-component and ons-render, we can easily use OnsenUI elements in our hiccup-ish Reagent/React components! :)
- In the main function, delete the the call to (prepare-device) and the when-let form, and replace both with this:
(.ready js/ons
(fn []
(ons-render app “app” state)
(prepare-device)))
- Test in browser with cordova run browser, and on device with cordova run ios.
If both looks like a native-ish flat UI app, you can open the menu and the alert shows, then everything works perfectly. That’s it!
If you want to make sure that you get the exact same code as desribed above, checkout from github via the tag cljs-react-socket-device-angular-onsen-lookandfeel
You’ve now successfully built your own mobile app with native-like performance and native-ish flat look-and-feel via OnsenUI, web socket communication to your server via Sente, React as client side framework via Reagent and Cordova for built tool and access to native device features such as location, camera, Bluetooth etc.
w00t, what a day! :)
You can now add more views etc. to your app using OnsenUI components:
http://onsen.io/guide/overview.html
You can add Cordova plugins for access to native device features:
http://plugins.cordova.io
We use this template ourselves when building hybrid/native mobile apps for iOS and Android. So don’t hesitate to contact us if you have any questions/suggestions.
Happy-happy-happy, joy-joy-joy! :)
Best,
@luposlip
@enterlab