MobileFirst Foundation 8.0 Developer Labs


logo lab session for 8.0

Pre-requisites:

Follow the Setup section to install an Ubuntu image, install the MobileFirst Developer Kit, archetypes and CLI and register at IBM Cloud.

You can download videos for offline usage from from Dropbox or OneDrive (2.62 - 2.66)

Setup

The recommended way to setup your workstation for these labs is to download the prepared VMware image that has all required software installed and ready to be used. However, if you want to do this on your own you will need to setup the dependencies listed below.

If you will be using ubuntu-based x64 distro, additional packages are required: lib32z1, lib32ncurses5, lib32bz2-1.0, and lib32stdc++6.
Finally, do not forget to set JAVA_HOME, ANDROID_HOME, MAVEN_HOME and M2_HOME and add them to your PATH.

Alternatively to watching labs on YouTube, you can download offline version from Dropbox or OneDrive. The videos are located inside the "labs_en" folder and have "webm" extension. VLC, same as most of media players and browser will be able to play them.

Download and setup the VM image

Download and install the MobileFirst Developer Kit

Register on IBM Cloud

Part 1

Lab highlights

Time to complete: up to 4 hours


Lab steps

Description: Install MobileFirst client-side sdk for cordova, register application and perform a proper init by catching SDK init event. Get familiar with basic application bootstrap process.
Time to complete: 30 minutes
Lab number for offline usage: 2.62


Lab helpers:

Repository URL

https://github.com/andriivasylchenko/advancedMessenger

Catching events

renderer.listenGlobal('document', 'mfpjsloaded', () => {
  console.log('--> MobileFirst API init complete');
  this.MFPInitComplete();
})

Init complete function

MFPInitComplete(){
  console.log('--> MFPInitComplete function called')
  this.rootPage = TabsPage;
}


Link to completed lab github repository branch
Description: Create HTTP adapters and modify application to perform backend calls though WLResourceRequest API using adapters. Get familiar with Java and Javascript adapter structure, custom properties
Time to complete: 40 minutes
Lab number for offline usage: 2.63


Lab helpers:

Adapters examples repository URL

https://github.com/MobileFirst-Platform-Developer-Center/Adapters/tree/release80

Employee Javascript adapter getRating()

function getRating() {

	var endpoint = MFP.Server.getPropertyValue("endpoint");
	var input = {
	    method : 'get',
	    returnedContentType : 'json',
	    path : endpoint
	};

	return MFP.Server.invokeHttp(input);
}

News Java adapter JSONObject parsing

JSONObject result = JSONObject.parse(JSONResponse.getEntity().getContent());
String json = result.toString();

Employee provider load function

load() {
  console.log('---> called EmployeeProvider load');
  if (this.data) {
    return Promise.resolve(this.data);
  }

  return new Promise(resolve => {
    let dataRequest = new WLResourceRequest("/adapters/employeeAdapter/getRating", WLResourceRequest.GET);

    dataRequest.send().then((response) => {
      console.log('--> data loaded from adapter', response);
      this.data = response.responseJSON.results;
      resolve(this.data)
    }, (failure) => {
      console.log('--> failed to load data', failure);
      resolve('error')
    })
  });
}

News provider load function

load() {
  console.log('---> called NewsProvider load');

  if (this.data) {
    return Promise.resolve(this.data);
  }

  return new Promise(resolve => {
    let dataRequest = new WLResourceRequest("/adapters/JavaHTTP/", WLResourceRequest.GET);

    dataRequest.send().then((response) => {
      console.log('--> data loaded from adapter', response);
      this.data = response.responseJSON.news;
      resolve(this.data)
    }, (failure) => {
      console.log('--> failed to load data', failure);
      resolve('error')
    })
  });
}


Link to completed lab github repository branch
Description:Create security check to validate user credentials and implement client-side handler to grab them. Secure adapter procedures with scope and map it to security check
Time to complete: 50 minutes
Lab number for offline usage: 2.64


Lab helpers:

AuthInit function

AuthInit(){
  this.AuthHandler = WL.Client.createSecurityCheckChallengeHandler("UserLogin");
  this.AuthHandler.handleChallenge = ((response) => {
      console.log('--> inside handleChallenge');
      if(response.errorMsg){
        var msg = response.errorMsg + '<br>';
        msg += 'Remaining attempts: ' + response.remainingAttempts;
      }
      this.displayLogin(msg);
  })
}

displayLogin function

displayLogin(msg) {
  let prompt = Alert.create({
    title: 'Login',
    message: msg,
    inputs: [
      {
        name: 'username',
        placeholder: 'Username'
      },
      {
        name: 'password',
        placeholder: 'Password',
        type: 'password'
      },
    ],
    buttons: [
      {
        text: 'Login',
        handler: data => {
          console.log('--> Trying to auth with user', data.username);

          this.AuthHandler.submitChallengeAnswer(data);
        }
      }
    ]
  });
  this.nav.present(prompt);
}

Getting active nav

ngAfterViewInit(){
  this.nav = this.app.getActiveNav();
}


Link to completed lab github repository branch
Description: Using new Push API register device and subscribe for a custom tag to receive Push messages
Time to complete: 30 minutes
Lab number for offline usage: 2.65


Lab helpers:

Push provider

import {Injectable} from '@angular/core';

@Injectable()
export class PushProvider {
  data: any = null;
  constructor() {}
  init() {
    console.log('--> PushProvider init called');

    MFPPush.initialize(
    function(success){
      console.log('--> Push init success');

      MFPPush.registerNotificationsCallback(pushNotificationReceived);

      var options = {"phoneNumber": ""};

      MFPPush.registerDevice(
        options,
        function(success){
          console.log('--> Push registration success');
          var tag = ['am'];

          MFPPush.subscribe(
            tag,
            function(success){
              console.log('--> Push subscribe success');
            },
            function(failure){
              console.log('--> Push subscribe failure', failure);
            }
          )
        },
        function(failure){
          console.log('--> Push registration failure', failure);
        }
      )
    }, function(failure){
      console.log('--> Push init failure', failure);
    })

    function pushNotificationReceived(message){
      console.log('--> Push received', message);
      alert(message.alert);
    }
  }
}


Link to completed lab github repository branch
Description: Store news messages inside JSONStore and display them on news tab
Time to complete: 45 minutes
Lab number for offline usage: 2.66


Lab helpers:

Storage provider

import {Injectable} from '@angular/core';


@Injectable()
export class StorageProvider {
  data: any = null;
  constructor() {}
  init() {
    console.log('--> JSONStore init function called');
    let collections = {
      news: {
        searchFields: {text: 'string', date: 'string'}
      }
    }

    WL.JSONStore.init(collections).then((success) => {
      console.log('-->JSONStore init success')
    }, (failure) => {
      console.log('-->JSONStore init failed', failure)
    })
  }

  put(data){
    console.log('--> JSONStore put function called');
    let collectionName = 'news';
    let options = {
      replaceCriteria: ['text', 'date'],
      addNew: true,
      markDirty: false
    };

    WL.JSONStore.get(collectionName).change(data, options).then((success) => {
      console.log('--> JSONStore put success')
    }, (failure) => {
      console.log('--> JSONStore put failed', failure)
    })
  }

  getAll() {
    console.log('--> JSONStore get all function called');

    return new Promise( resolve => {
      let collectionName = 'news';
      let options = {};

      WL.JSONStore.get(collectionName).findAll(options).then((success) => {
        console.log('-->JSONStore get docs success', success)
        resolve(success);
      }, (failure) => {
        console.log('-->JSONStore get docs failed', failure)
        resolve('error');
      })
    })
  }
}

News provider load function

load() {
  console.log('---> called NewsProvider load');
    let dataRequest = new WLResourceRequest("/adapters/JavaHTTP/", WLResourceRequest.GET);

    dataRequest.send().then((response) => {
      console.log('--> data loaded from adapter', response);
      this.data = response.responseJSON.news;
      console.log('--> puttin data to JSONStore');
      this.storage.put(this.data);
    }, (failure) => {
      console.log('--> failed to load data', failure);
    })
}


Link to completed lab github repository branch

Part 2

Lab highlights

Time to complete: up to 4 hours



Lab steps

Description: Add validator to Node.js Mock Server to check authentication token on Mobile Foundation and prevent unauthenticated calls
Time to complete: 45 minutes
Lab number for offline usage: 2.70


Lab helpers:

Schedule provider

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class ScheduleProvider {
  data: any = null;
  distance: any = null;

  constructor(public http: Http) {}

  load() {
    console.log('---> called ScheduleProvider load');
    if (this.data) {
      // already loaded data
      return Promise.resolve(this.data);
    }

    return new Promise(resolve => {
          let dataRequest = new WLResourceRequest("http://192.168.42.169:4567/schedule", WLResourceRequest.GET);

          dataRequest.send().then((response) => {
            console.log('--> data loaded from adapter', response);

            this.data = response.responseJSON.delivery;
            resolve(this.data)
          }, (failure) => {
            console.log('--> failed to load data', failure);
            resolve('error')
          })
        });
  }

  calc(destinations) {

    console.log('---> called ScheduleProvider calc');
    if (this.distance) {
      // already loaded data
      return Promise.resolve(this.distance);
    }
    return new Promise(resolve => {

          let dataRequest = new WLResourceRequest("http://192.168.42.169:4567/distance", WLResourceRequest.GET);

          let curtime = Date.now();
          let origin = '50.019275,14.347424';
          let googleParams = 'origins=' + origin + '&destinations=' + destinations + '&departure_time=' + curtime + '&traffic_model=best_guess';
          console.debug('google params', googleParams);

          dataRequest.setQueryParameter("origins", origin);
          dataRequest.setQueryParameter("destinations", destinations);
          dataRequest.setQueryParameter("departure_time", curtime);
          dataRequest.setQueryParameter("traffic_model", "best_guess");

          dataRequest.send().then((response) => {
            console.log('--> data loaded from adapter', response);
            this.distance = response.responseJSON;
            console.debug('Schedule calc data', this.distance.rows[0].elements);
            resolve(this.distance.rows[0].elements);
          }, (failure) => {
            console.log('--> failed to load data', failure);
            resolve('error')
          })
        });
  }
}


Link to completed lab github repository branch
Description: Create web resources package and test direct update capabilities. Remotely disable application and add notification
Time to complete: 15 minutes
Lab number for offline usage: 2.71


Link to completed lab github repository branch
Description: Enable client-side log collection and add logging on failure during user authentication. Display errors via custom charts inside analytics console
Time to complete: 20 minutes
Lab number for offline usage: 2.72


Lab helpers:

Handle challenge

this.AuthHandler.handleChallenge = ((response) => {
        console.log('--> inside handleChallenge');

        if(response.errorMsg){
          var msg = response.errorMsg + '<br>';
          msg += 'Remaining attempts: ' + response.remainingAttempts;
          WL.Logger.error("Auth error: " + response.errorMsg);
          WL.Logger.send();
        }

        this.displayLogin(msg);
    })


Link to completed lab github repository branch
Description: Upload application binary to IBM Cloud service for security scanning and analyze results
Time to complete: 5 minutes
Lab number for offline usage: 7.8


Description: Create Mobile Quality Assurance service instance on IBM Cloud and using MobileFirst CLI add MQA libraries for hybrid application instrument mobile application and report a first bug
Time to complete: 25 minutes
Lab number for offline usage: 7.9


Description: Create a Cloud Foundry application runtime for Node.js. Prepare local Mock Server and then push it to IBM Cloud.
Time to complete: 35 minutes
Lab number for offline usage: 7.10


Lab helpers:

Package dependencies

"dependencies": {
  "express": "4.13.x",
  "cfenv": "1.0.x",
  "passport-mfp-token-validation": "8.0.x",
  "request": "2.74.x",
  "path": "0.12.x",
  "compression": "1.6.x",
  "body-parser": "1.15.x",
  "cors": "2.8.x"
}

Description: Create server instance, add remote server definition, deploy required adapters and import application version from local server
Time to complete: 25 minutes
Lab number for offline usage: 6.7


Description: Switch Angular/Ionic to production mode, prepare icon and splash screen, create keystore, build binary for release and sign and then distribute using Mobile Quality Assurance service
Time to complete: 20 minutes
Lab number for offline usage: 9.5


Lab helpers:

Terminal commands

keytool -genkey -v -keystore identity.keystore -alias ibm -keyalg RSA -keysize 2048 -validity 10000

ionic build android --release

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore /home/ibm/dev/workspaces/am/advancedMessenger/identity.keystore /home/ibm/dev/workspaces/am/advancedMessenger/platforms/android/build/outputs/apk/android-release-unsigned.apk ibm

/home/ibm/dev/sdk/build-tools/23.0.3/zipalign -v 4 /home/ibm/dev/workspaces/am/advancedMessenger/platforms/android/build/outputs/apk/android-release-unsigned.apk /home/ibm/dev/workspaces/am/advancedMessenger/platforms/android/build/outputs/apk/advancedMessenger_0.0.1.apk

Inclusive terminology note: The Mobile First Platform team is making changes to support the IBM® initiative to replace racially biased and other discriminatory language in our code and content with more inclusive language. While IBM values the use of inclusive language, terms that are outside of IBM's direct influence are sometimes required for the sake of maintaining user understanding. As other industry leaders join IBM in embracing the use of inclusive language, IBM will continue to update the documentation to reflect those changes.