Actions

Developer area/Mahara Mobile

From Mahara Wiki

Revision as of 15:32, 23 September 2016 by Aaronw (talk | contribs)

Mahara Mobile is a cross-platform mobile app for Mahara, being developed by Catalyst IT for the Mahara project. It's currently (Sept 2016) still in development, with a potential launch corresponding with the Mahara 16.10 release. The application uses updates to Mahara's webservices, which are only present in Mahara 16.10 and greater.

See Developer_Area/Specifications_in_Development/Mahara_Mobile for the design process behind it.

Source code

The source code is on the Mahara github project: https://github.com/maharaproject/mahara-mobile

Mahara Mobile is a Cordova app, which means it's written in Javascript and runs in a Webview (a GUI element that's basically a stripped-down web browser), which allows the same code to run on multiple native OS's.

It's essentially a single-page web application; a static HTML file on the user's phone, which loads up a bunch of local Javascript files. If you're coming to this from PHP, the big mental shift is that in a Cordova app there is no "server-side" to speak of. The "server-side" is a static HTML file that says which Javascript files to run. The Javascript runs in the local Webview/browser.

The program is designed to send/retrieve data from a Mahara site. It does this through Webservices.

Organization of the git repo

This is a description of the directory structure you'll see if you check out the git repository.

Config files

  • config.xml: This is the Cordova project's configuration file. It contains metadata about the project, like its name, description, icons to use in various OS's, which Cordova plugins to include, etc. It also indicates that index.html is the "entry point" for the application; the file to actually load into the WebView when the app launches.
  • package.json: This is the NPM package description file. Its most important feature is that it lists all of the NPM modules that we use in the project; both in the project itself, and in its Gulp build script. It also has a scripts tag that configures several useful build commands, such as npm run start.
  • gulpfile.js: The Gulp build file. It contains the script that is used in the first step of our build process.
  • cordova.json: Not a core cordova config file. It stores some build directives to pass in to cordova when publishing the Android app.
  • jsconfig.json: A config file for the VisualStudio Code IDE.

Build products

Step 0: /src

The actual codebase of Mahara Mobile is in the /src directory. Specifically, most of the Javascript is in /src/js.

Step 1: Gulp

The first build step is to run gulp. This then compiles our Javascript in the /src directory (which contains JSX and ES2017 code) into cross-platform-compatible Javascript. It also bundles it up into one big Javascript file, called bundle.js. Build steps are also applied to the CSS files, and to any other resources in the /src directory. These build products are copied into the /www directory.

Step 2: Cordova

The next build step is to run cordova build. This copies the Javascript from the /www directory, and bundles/packages it as needed for each OS we're targetting, along with any included Cordova libraries, plugins, and native code. If you do cordova build android, it compiles it into an Android apk file; if you do cordova build ios, it makes an iOS app; if you do cordova build browser, it just turns it into a local web app on your computer.

These build products are under the /platforms directory: /platforms/android, /platforms/browser, etc.

Layout of code in /src

/src/index.html

This is the "single page" in our "single page application". It's the HTML file that Cordova loads into the webview when you launch the application. It's named in /config.xml.

Think of this file as the boot loader for the app. All it does is display a simple "Loading..." graphic, and then load in the cordova.js library; and once that's done, Mahara Mobile's ready.js file.

/src/ready.js

If index.mtml is the boot loader, ready.js is the OS startup sequence. This file locates and processes the language strings stored under /src/i18n. Then it loads and executes all of the Javascript files, under /src/js and /src/libs.

Control then flows to /src/js/index.js, via the IIFE] in that file.

/src/js/index.js

This file declares a render() method, which is sort of like the Event loop for the application's React-based UI. Every time the app's state changes (via the StateStore), render() is called, looks at the value passed to it by the statestore, and uses a case-switch to decide which page to display/update.

This typically will then put the application in a state of waiting for the next user input. If, instead, the app should automatically carry on to some additional step, an "after" method can be triggered here to start that going.

/src/js/state.js

This file contains most of the "controller" logic for the application. Its main feature is the StateStore object, which is a Redux store.

Most of the file is taken up by the "MaharaState" function. This function acts as the "reducer" for the Redux store. Each time the state of the application changes, a dev calls StateStore.dispatch(action), where "action" is an object with information about the state change. This "action" is then passed to the "MaharaState" function, where the giant case-switch statement determines which action was taken, and updates the internal state accordingly.

MaharaState() also mirrors the storage into LocalStorage. This allows for data to persist offline. (Although we should probably re-implement this with proper Cordova libraries, because iOS sometimes reclaims LocalStorage.)

Remember that "render()" in index.js is subscribed to the StateStore. So after each state update, render() is called and updates the UI.

/src/js/router.js

This file declares a Grapnel router. This allows you to call router.navigate(<pageid>) to change pages, using one of the page id constants declared in /src/js/constants.js.

This is the main way to navigate between pages in the app. (What it actually does is fire off a StateStore dispatch, with {type:PAGE.something}. In MaharaState()'s switch-case loop, this updates state.page. And then in render.js the change to state.page is noticed, and causes the appropriate page to be rendered.

/src/lib

Contains Javascript libary files that aren't managed by NPM or Cordova.

/src/js

Software libraries used

Cordova/PhoneGap

The program is written for the Apache Cordova platform. This is also sometimes referred to as PhoneGap; but specifically PhoneGap is a Adobe's distribution of PhoneGap.

Cordova apps are able to run on multiple platforms, because they primarily run as Javascript inside a web browser/webview. Thus any platform that provides a suitable Javascript runtime, can potentially run a Cordova app. The Cordova framework includes additional native libraries that can be accessed from Javascript, to allow the JS code to access native functionality on the device.

Writing a Cordova app is not exactly the same as writing a responsive web application. They often share a lot of the same front-end code. But, Cordova apps typically run in a "Webview" rather than an actual web browser. A "Webview" is a special type of GUI element that can render HTML & JS content, but has some different behaviors than a normal web browser. In particular, Cordova applications are usually single-page applications, with movement between "pages" done by Javascript. This is because, if the webview were pointed to a different URL, the entire Cordova application and all of its Javascript libraries, would have to re-render, with a noticeable delay and loss of all non-persisted variables.

React

The single-page application framework that Mahara Mobile uses is React, written by Facebook.

Traditionally in Mahara we've used one of two methods to update pages via Javascript:

  • Reload and replace the entire page/iframe (Easier to code, but slow and ugly for the user)
  • Use JQuery to locate specific DOM elements to update, and just change those (Faster for the user, but finnicky and bug-prone to code)

React avoids both of these, by using a "Virtual DOM" and automatically adjusting the "actual DOM" to match it. So, you can write React code that acts as if it's re-printing the entire page, and then React will only update the parts of the page that have actually changed. Despite the extra computation, this actually makes for a faster user experience.

JSX

Taking the place of Mahara's template files, Mahara Mobile uses Facebook's JSX. This is a Javascript syntax extension that lets you write code inside your Javascript files that looks like HTML. A build tool will later compile it into actual Javascript.

// Awkward block of variable declarations
var Form = MyFormComponent;
var FormRow = Form.Row;
var FormLabel = Form.Label;
var FormInput = Form.Input;

var App = (
  <Form>
    <FormRow>
      <FormLabel />
      <FormInput />
    </FormRow>
  </Form>
);

So if you see code like that in the codebase, know that it's JSX.

Redux

The app uses Redux to maintain state. You'll notice a prominent "StateStore" object in the codebase. That comes from Redux.

Grapnel

We also use the JS library Grapnel as a "router". This is basically the way that we manage switching between different "pages" of our single-page application.

Babel & Browserify

Mahara Mobile uses Babel and Browserify in its build script.

Babel is a Javascript compiler. It converts the JSX in our code, into actual Javascript. It also convert's Mahara Mobile's Javascript from bleeding-edge ES2017 syntax, into Javascript that will run in any modern web browser.

Browserify allows us to use the require() statement to pull in Javascript code from other files. It does so mainly by compiling all our separate files together into one big bundle.js file.

MaharaDroid (the old app)

Mahara's predecessor as official mobile app was MaharaDroid, an Android native application. It is no longer being maintained. The code is available here: Developer_Area/Specifications_in_Development/Mahara_Mobile

MaharDroid was written before the Mahara webservices plugin was implemented. So it instead uses the code located under htdocs/api/mobile.php, which has to be activate by enabling the "Allow mobile uploads" site setting.