Fixing Ionic Icon and Splashscreen Being Stuck as Default

I want to share my fix for the Ionic icon and splashscreen issue I just hit upon.

The issue was that no matter what I did, deploying my Ionic app to my Android phone always ended up with an empty / blank / white splash screen and the default ionic icon.

Ionic default icon on Android
Ionic default icon on Android

No matter what I seemed to do, that darned icon would not change.

Before I go further however, it is definitely worth pointing out an awesome feature in Ionic – ionic resources – a command line utility which will take an icon and a splash screen image (.png files, Photoshop .psd files, or Illustrator .ai files) and create you all the correctly sized icons / splash screen images AND update your config.xml file for you. Of course, you need to have the two images to begin with – and if you don’t, check out somewhere like fiverr or odesk (ahem, upwork?) to have these files made for you.

From my project root, I put my icon.png and splash.png into a folder called resources and then ran the ionic resources command. Checking my config.xml after this shows something along the lines of:


  *snip* 
  <platform name="android">
    <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png"/>
    <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png"/>
    <icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png"/>
    <icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png"/>
    <icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png"/>
    <icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png"/>
    <splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png"/>
    <splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png"/>
    <splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png"/>
    <splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png"/>
    <splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png"/>
    <splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png"/>
    <splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png"/>
    <splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png"/>
    <splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png"/>
    <splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png"/>
    <splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png"/>
    <splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png"/>
  </platform>
*snip*

And each of those resources/android… whatever paths is an actual file, resized properly.

Ok, enough of the set up. As mentioned, all this looked right yet still every time I did an ionic run android, the icon would be the default icon, and the splash screen was empty / blank / white.

There are a number of apparent fixes to this problem online, but none seemed to work for me. The best I could tell, my ionic icon was cached somehow, but as I was building on the Mac, I didn’t have any form of Android tooling available to open the project and clean the install.

Ionic Icon Woes : The Solution

Though it took a lot of trial and error (precious hours I will never get back!), the solution to this problem was pretty easy.

I had noticed the widget id value in my config.xml was a bit odd. For some reason (which I haven’t figured out still), when Ionic had initially generated my project skeleton, it had added some numbers on at the end.


<widget id="com.ionicframework.tweethours24237" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
*snip*

All it took to fix my Ionic icon problem was to remove these numbers.

As soon as I did that, my icon changed to what I expected:

Ionic icon on Android displaying as expected
Ionic icon on Android displaying as expected

Result!

Splash Screen Solution

Unfortunately, this didn’t fix my splash screen issue.

But, as above, I had correctly generated the splash screen so this was a little puzzling. Everything seemed ok – and I did see the 3 seconds of white / blank / emptiness that implied the splash screen code was actually executing, just missing the expected image.

To fix this I added the cordova-plugin-splashscreen as mentioned here.

Remember to update your package.json file whenever you add a plugin, so you or your colleagues aren’t caught out by a missing plugin at some point in the future.

# /packages.json
  *snip*
  "cordovaPlugins": [
    "cordova-plugin-splashscreen",
  *snip*

To make this work I also needed to update my .run method:

# /www/js/app.js
.run(function($rootScope, $ionicPlatform, $cordovaSplashscreen){
    setTimeout(function() {
        $cordovaSplashscreen.hide()
    }, 3000);

After that, my splash screen worked as expected:

Custom Ionic Splash Screen on Android
Custom Ionic Splash Screen on Android

And finally, I was done 🙂

For reference, here is my ionic info on the off chance this is useful:

Cordova CLI: 5.0.0 
Gulp version: CLI version 3.8.1 
Gulp local: Local version 3.8.11 
Ionic Version: 1.0.0 
Ionic CLI Version: 1.4.6-beta.0 
Ionic App Lib Version: 0.0.22 
ios-deploy version: Not installed 
ios-sim version: 3.1.1 
OS: Mac OS X Yosemite 
Node Version: v0.12.4 
Xcode version: Xcode 6.3.1 
Build version 6D1002

Also, my base image sizes are as follows:

icon.png – 768px x 768px
splash.png – 2208px x 2208px

I also used this splash template – just make sure you get everything between the green grid lines, or you will get unwanted clipping on smaller screen sizes.

Angular Services with Karma and Jasmine Testing

viva las vegas - karma and jasmine go testing

Karma and Jasmine. Not the names of two girls I partied with in Vegas. Something even better.

Yes, let me introduce you to Jasmine testing, a Behaviour Driven Development testing framework. And her friend Karma, a spectacular test runner.

Now we are done with the introductions, let’s get down to detail. Testing an AngularJS / Ionic application is well documented and baked right in to the framework. That’s cool. Sometimes though, if your project reaches any sort of complexity above the tutorials, you are going to need to test Angular services that themselves have dependencies.

I found the documentation on this topic sparse and confusing. So hopefully this example can help shed a little more light on the subject.

The Situation

How can we unit test an Angular service that has another service (dependency) injected in to it?

Let’s say we have two services that we care about. Service A, and Service B.

Service A receives Service B via dependency injection.

How do we mock this up so that it becomes testable?

How can we fake the data coming back from Service B in a way that will allow us to make the dependency behave exactly how we want it to?

And how can we do this in such a way that our individual tests don’t become huge and unwieldy?

Before Each, Throw Me a Curve Ball

Assuming we have a service definition that looks like this:

“` language-javascript
‘use strict’;

angular.module(‘core’)

.service(‘TweetHoursStorage’, [‘storage’, function($localStorage) {
this.clearAll = function() {
$localStorage.clearAll();
};

// * snip *

}])
;
“`

Let’s say we have a test file that looks something like this:

(note: don’t copy / paste this, as it doesn’t work)

// app/modules/core/tests/services/tweet-hours-storage.service.test.js
'use strict';

describe('Service: TweetHoursStorage', function () {

    // load the service's module
    beforeEach(module('core'));

    var TweetHoursStorage;

    beforeEach(inject(function(_TweetHoursStorage_) {
        TweetHoursStorage = _TweetHoursStorage_;
    }));

    afterEach(function () {
        TweetHoursStorage.clearAll();
    });

    describe('update method test', function() {

        it('Tests the update method returns false if not passed an object', inject(function() {
            expect(TweetHoursStorage.update('blah')).toBeFalsy();
        }));

    // * snip *

When we run this, we will see an error similar to the following:

TypeError: queueableFn.fn.call is not a function

Why?

Well, it turns out there are two problems with the above code. One is more visible than the other, but we can fix both at the same time.

This issue occurs before we even hit our first test proper. This is a set up issue.

Firstly, beforeEach(module('core')); – this is redundant. We can remove this and combine with the second beforeEach(); method call just below.

Let’s do that:

// app/modules/core/tests/services/tweet-hours-storage.service.test.js
'use strict';

describe('Service: TweetHoursStorage', function () {

    var TweetHoursStorage,
        mockLocalStorage;

    // load the service's module
    beforeEach(function() {
        module('core', function($provide) {
            mockLocalStorage = jasmine.createSpyObj('mockLocalStorage', ['clearAll']);
            $provide.value('storage', mockLocalStorage);
        });

        inject(function(_TweetHoursStorage_) {
            TweetHoursStorage = _TweetHoursStorage_;
        });
    });

    afterEach(function () {
        TweetHoursStorage.clearAll();
    });

    describe('a clear all method test', function() {
        // * snip *
    });

Now, lines 11-14 go about creating a Jasmine spy object which allows us to pretend mockLocalStorage is really a real life, working as expected instance of $localStorage, which the service we are testing actually depends on – the line from our service definition above for reference:

.service('TweetHoursStorage', ['storage', function($localStorage) {

Once we implement this our tests should actually start running. They may / likely won’t be passing at this stage, but that bit is easier (in my opinion at least) than getting this set up the first time you do it.

Faking an API Call when Jasmine Testing

What about if we have a remote API that we need to get some data from?

How can we mock that out in a test?

 // app/modules/core/tests/services/selected-hours-to-notification-data-transformer.test.js

'use strict';

describe('Service: SelectedHoursToNotificationDataTransformer', function () {

    var SelectedHoursToNotificationDataTransformer,
        mockApiLookup,
        mockDateProcessing;


    // load the service's module
    beforeEach(function (){
        module('core', function($provide) {
            mockApiLookup = jasmine.createSpyObj('mockApiLookup', ['getAll']);
            mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });
            mockDateProcessing = jasmine.createSpyObj('mockDateProcessing', ['process', 'nextStartDate']);

            $provide.value('ApiLookup', mockApiLookup);
            $provide.value('DateProcessing', mockDateProcessing);
        });

        inject(function(_SelectedHoursToNotificationDataTransformer_) {
            SelectedHoursToNotificationDataTransformer = _SelectedHoursToNotificationDataTransformer_;
        });
    });


    /**
     * This is the pretend result set that might be returned by a call to the API GET end point
     * @returns {*[]}
     */
    var start = moment().subtract(6, 'days');
    var getFakeDataSet = function () {
        return [
            { id: 4, title: 'event4', start: start, duration: 2 },
            { id: 5, title: 'event5', start: start, duration: 1 },
            { id: 8, title: 'event8', start: start, duration: 1 },
            { id: 99, title: 'event99', start: start, duration: 1 }
        ];
    };

This code may not be the most elegant or ninja-level JavaScript ever written, but by jingo it shows a few things that I would have found helpful a few months hence.

Line 15 – we are doing much the same as in the previous example.

This time though, we want to replace the real outcome with one we can control. Testing a remote API during our testing is not a good thing for many reasons including the API likely not being entirely tolerant to us repeatedly sending in the same fake data every time we run our Jasmine test suite. Also, speed, predictability, and many other factors.

First we declare the method as being available on our mock – line 15.

Then we tell Jasmine what we want anything that calls that method to get instead:

mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });

Further down, starting on line 34, we do something that feels a little different to you if you’re used to working with PHP. We declare var getFakeDataSet which contains a yet to be executed function.

When that function is executed, it will return an array of JSON, representing the data we would anticipate receiving back from our remote API in the real world.

That data set will be entirely dependent on your project’s circumstances. My data is just for show.

So, back to line 16, where we return that data. The reason for me going into detail there is that it is entirely possible – and correct in some circumstances – that you would want to return the unresolved function.

mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet; });
// vs
mockApiLookup.getAll.and.callFake(function(){ return getFakeDataSet(); });

Just be careful of this one. It’s not specific to Jasmine testing, it’s a JavaScript thing in general, but it can be confusing.

I digress, but essentially the first line will return the function, unresolved until you call it by adding the () to the end of the variable holding that function. Confusing? A little. Tricky to spot for sure, when you inevitably make that mistake whilst coding.

Back to the example – line 19 and 20 show how to add multiple mocked dependencies should your service require it.

Jasmine Testing Cheat Sheet

The manual is pretty good on this topic.

But I also found this cheat sheet very helpful.

In App Notifications with the Ionic Framework

I’m working on an app that relies heavily on the concept of in app notifications. Rather than make this app two or more times (in Java for Android, in Objective-C or Swift for iOS, in whatever Windows phones use), I have opted to go hybrid, enabling me to code in JavaScript and deploy anywhere.

Now, all this is tickety boo, except that as you might expect, working in one language for three platforms has its *ahem* caveats.

Before I go further I firstly want to say how amazingly awesome it is that the Ionic Framework even exists, let alone as an open source project that’s free to use for all. I also want to give my love and thanks to all the contributors of plugins that allow Ionic-based apps to do 90%+ of what can be done natively in app.

I can only imagine how difficult it must be to make one plugin that works on multiple platforms, in a market that is as fast paced as the mobile device industry.

Inevitably, I have hit on a few issues as I’ve been working on this app, and a large part of the reason and process behind this is to show what those problems are, and just as importantly, how I have overcome them.

Local Notifications with $cordovaLocalNotification

Sebastián Katzer
Sebastián Katzer

As mentioned, a large part of my app relies on the use of in app notifications.

The gist of the app is that a user can select one or more ‘hours’ to subscribe too / be notified about, and then the mobile device should send an alert when those hours are about to begin.

The obvious starting point here then is the Local Notifications plugin by Sebastián Katzer.

The code itself to get this working is pretty straightforward. This is in large part to the brilliant Wiki documentation that is chock full of helpful examples, coupled nicely with the official documentation – scroll to the bottom of this page for the Ionic-specific Examples section.

So now we have some code to work with:

module.controller('MyCtrl',
  ['$scope', '$rootScope', '$ionicPlatform', '$cordovaLocalNotification',
   function($scope, $rootScope, $ionicPlatform, $cordovaLocalNotification) {

  $ionicPlatform.ready(function () {

    // ========== Scheduling

    $scope.scheduleSingleNotification = function () {
      $cordovaLocalNotification.schedule({
        id: 1,
        title: 'Title here',
        text: 'Text here',
        data: {
          customProperty: 'custom value'
        }
      }).then(function (result) {
        // ...
      });
    };

 

This is where things started to get interesting for me, and also where things started to go wrong.

My first issue was that whilst all the examples I could find involve $scope, I didn’t have access to this as I was calling the schedule method from a Service.

My solution to this (which may be incorrect, definitely feels incorrect, and is not taking advantage of dependency injection so is hard to test), was to switch to the following:

'use strict';

angular.module('core')
    .service('NotificationUpdater', ['$ionicPlatform',function($ionicPlatform) {

        this.updateNotifications = function (chosenHours) {

            $ionicPlatform.ready(function () {
                cordova.plugins.notification.local.cancelAll(); // left this in for show, but not there in the real code

                var now = new Date().getTime(),
                    _5_sec_from_now = new Date(now + 5 * 1000);

                cordova.plugins.notification.local.schedule({
                    title: "Tweet Hours are Starting",
                    text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In nec magna libero. Ut at massa pellentesque massa euismod volutpat id quis metus.",
                    at: _5_sec_from_now,
                    badge: 3
                });
            })
        };

 

This is a rough draft / proof of concept. But, importantly, it worked. And I say it worked, it took a day and a half of hacking to get it to work. It may not be pretty but by Jove it sends me some damn alerts and right now, that’s good enough.

If Only Coding Were That Easy

Of course, if it had been that simple, it wouldn’t be worth blogging about.

I strongly encourage you to make use of a README.md file in your project, making notes on how you get things to work in your project. It’s all fine and dandy today, but what happens next week, next month, or in my case, an entire year later you decide to pick the project back up and things don’t work as you remember. You may be thinking, Chris, I would just type history in to my terminal and look at what I did to make it work. Well, that’s a solution for sure, but not for many of us, especially if your wife had accidentally tipped a glass of wine over your old Macbook during the interim.

To make things trickier, as mentioned, some things that blow up on one platform, work splendidly on another. A case in point would be when you have failing dependencies that are Android specific, which allow you to run your iOS project but blow up on building your Android project. Hair loss time.

Firstly, check your Ionic info:

ionic info

Your system information:

  • Cordova CLI: 5.0.0
  • Gulp version: CLI version 3.8.1
  • Gulp local: Local version 3.8.11
  • Ionic Version: 1.0.0
  • Ionic CLI Version: 1.4.6-beta.0
  • Ionic App Lib Version: 0.0.22
  • ios-deploy version: Not installed
  • ios-sim version: 3.1.1
  • OS: Mac OS X Yosemite
  • Node Version: v0.12.4
  • Xcode version: Xcode 6.3.1 Build version 6D1002

Updating Node is pretty straightforward on Mac – just download it and run the installer.

As part of getting this to work today, I also ran:

sudo npm install -g driftyco/ionic-cli
sudo npm install -g cordova
sudo npm install -g ionic

I think that can be done as a one-liner, but I didn’t, so I am showing each one as it is in my history.

Android Specific Stuff

Quite possibly the second most time intensive part of this whole process – after debugging why Local Notifications kept blowing up – was the download and install the latest Android SDK stuff. Man. Alive. It took ages.

On Mac:

brew install android-sdk

That bit was pretty quick. Then, as prompted, run:

export ANDROID_HOME=/usr/local/opt/android-sdk

Check the path given by brew installer, as the above may not work for you.

Then:

android

This pops up the Android SDK Manager, which *should* go off and search for all the latest Android stuff that you will need to download (which takes bloody ages), and install (which takes about the same amount of time as downloading).

Android SDK on Mac OSX

I hit a bit of trouble here which will likely not affect you. As I record my training videos on a distraction free second profile, and because I am making this app as part of a course for Code Review, I couldn’t run the SDK manager from the profile I hadn’t run the brew install android-sdk command from originally. This ultimately involved switching profiles whenever I have needed to update the SDK – which is more frequently than I had anticipated (every other day so far, but then, Android M is releasing currently).

Keeping Ionic in Sync with your Installed Plugins

As mentioned, to get in app Local Notifications to work involved using a plugin.

Installing this plugin is described on the plugin authors website as:

cordova plugin add https://github.com/katzer/cordova-plugin-local-notifications

That was my initial move. There are some other suggested ways of installing this plugin on that website, and I tried all of them.

The problem was, I kept hitting on issues when trying to build for Android, which were predominantly dependency names used in the Katzer provided config.xml which were 404’ing when npm was trying to download them.

Here’s a good example of what I mean:

tweetHours git:(master) ✗ ionic platform rm android && ionic platform add android 
Updated the hooks directory to have execute permissions 
Removing platform from package.json file 
Updated the hooks directory to have execute permissions Adding android project... 
Creating Cordova project for the Android platform: 
  Path: platforms/android 
  Package: com.ionicframework.tweethours247847 
  Name: tweetHours 
  Activity: MainActivity 
  Android target: android-22 
Copying template files... 
Android project created with cordova-android@4.0.0 
Running command: /Users/Shared/Development/tweetHours/hooks/after_prepare/010_add_platform_class.js /Users/Shared/Development/tweetHours 
add to body class: platform-android 
Installing "com.ionic.keyboard" for android 
Installing "cordova-plugin-android-support-v4" for android 
Installing "cordova-plugin-device" for android 
Installing "cordova-plugin-whitelist" for android 
Installing "de.appplant.cordova.common.registerusernotificationsettings" for android 
Installing "de.appplant.cordova.plugin.local-notification" for android 
WARNING: android.support.v4 has been renamed to cordova-plugin-android-support-v4. You may not be getting the latest version! We suggest you cordova plugin rm android.support.v4 and cordova plugin add cordova-plugin-android-support-v4.
Fetching plugin "android.support.v4" via cordova plugins registry 
Fetching from cordova plugins registry failed: EACCES, open '/Users/codereview/.plugman/cache/b41e20d1-android-support-v4.lock' 
Fetching plugin "android.support.v4" via npm 
npm http GET https://registry.npmjs.org/android.support.v4 
npm http 404 https://registry.npmjs.org/android.support.v4 
Fetching from npm failed: 404 Not Found: android.support.v4 Failed to install 'de.appplant.cordova.plugin.local-notification':Error: 404 Not Found: android.support.v4 at RegClient. (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:268:14) at Request.self.callback (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:148:22) at Request.emit (events.js:110:17) at Request. (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:876:14) at Request.emit (events.js:129:20) at IncomingMessage. (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:827:12) at IncomingMessage.emit (events.js:129:20) at stream_readable.js:908:16 at process.tickCallback (node.js:355:11) Error: 404 Not Found: android.support.v4 at RegClient. (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:268:14) at Request.self.callback (/usr/local/lib/nodemodules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:148:22) at Request.emit (events.js:110:17) at Request. (/usr/local/lib/nodemodules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:876:14) at Request.emit (events.js:129:20) at IncomingMessage. (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/npm/node_modules/request/index.js:827:12) at IncomingMessage.emit (events.js:129:20) at stream_readable.js:908:16 at process.tickCallback (node.js:355:11)

It would then promptly bomb out and blow up.

The important line is:

WARNING: android.support.v4 has been renamed to cordova-plugin-android-support-v4. You may not be getting the latest version! We suggest you cordova plugin rm android.support.v4 and cordova plugin add cordova-plugin-android-support-v4.

I tried the obvious – running:

cordova plugin rm android.support.v4 and cordova plugin add cordova-plugin-android-support-v4

And that kinda works. It works because it does what it says on the tin. But the issue is that it’s not you who is managing that dependency, it’s the plugin you are trying to use – in this case, it’s a dependency of the Local Notification plugin (I think, I hit so many of these that this was just the first one I found in my terminal when scrolling back).

As this is a dependency of a plugin, you can’t – directly – change this without submitting a Pull Request, waiting for that PR to be approved, merged, and then having the new version imported. I couldn’t wait that long, and I’m always reluctant to submit a PR to a project I don’t understand how to test. So, instead, I forked it.

I updated the plugins to use the new paths for the dependencies, and all was sorted – on that front. If you’re interested, click here to see the commit history.

For reference, my package.json file looks like this:

{
  "name": "tweethours",
  "version": "0.0.2",
  "description": "tweetHours: An Ionic project",
  "dependencies": {
    "gulp": "^3.5.6",
    "gulp-concat": "^2.2.0",
    "gulp-minify-css": "^0.3.0",
    "gulp-rename": "^1.2.0",
    "gulp-sass": "^1.3.3"
  },
  "devDependencies": {
    "angular-mocks": "^1.4.0",
    "bower": "^1.3.3",
    "gulp-util": "^2.2.14",
    "shelljs": "^0.3.0",
    "karma-chrome-launcher": "^0.1.4",
    "karma-jasmine": "^0.1.5"
  },
  "cordovaPlugins": [
    "cordova-plugin-android-support-v4",
    "cordova-plugin-device",
    "cordova-plugin-whitelist",
    "com.ionic.keyboard",
    {
      "locator": "https://github.com/a6software/cordova-plugin-local-notifications.git",
      "id": "de.appplant.cordova.plugin.local-notification"
    },
    {
      "locator": "https://github.com/katzer/cordova-common-registerusernotificationsettings",
      "id": "de.appplant.cordova.common.registerusernotificationsettings"
    },
    {
      "locator": "https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin",
      "id": "nl.x-services.plugins.toast"
    }
  ],
  "cordovaPlatforms": [
    "ios",
    {
      "platform": "ios",
      "version": "",
      "locator": "ios"
    },
    "android"
  ]
}

You can make ionic do the dirty work for you on keeping this file updated, just run: ionic state save – but before you do, make sure you have a back up of your package.json just in case.

The reason some of the plugins use the locator and id properties inside their own JSON object is that I added Github repo URLs instead of the package names, e.g.:

cordova plugin add https://github.com/a6software/cordova-plugin-local-notifications.git

instead of:

cordova plugin add de.appplant.cordova.plugin.local-notification

More Problems On Android

However, when building I was still hitting on another issue – this time one with an outstanding pull request.

:generateDebugSources :compileDebugJava/Users/Shared/Development/tweetHours/platforms/android/src/de/appplant/cordova/plugin/localnotification/LocalNotification.java:564: error: cannot find symbol webView.evaluateJavascript(js, null); symbol: method evaluateJavascript(String,) location: variable webView of type CordovaWebView /Users/Shared/Development/tweetHours/platforms/android/src/de/appplant/cordova/plugin/localnotification/LocalNotification.java:561: error: cannot find symbol webView.post(new Runnable(){ symbol: method post() location: variable webView of type CordovaWebView Note: /Users/Shared/Development/tweetHours/platforms/android/src/com/ionic/keyboard/IonicKeyboard.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. 2 errors FAILED

FAILURE: Build failed with an exception.

For me, this issue occured when running ionic run android – I don’t use the emulator, I can’t seem to make the thing work for Android, so this was deploying to a real Android phone.

I applied the fix directly to my own fork, and that resolved the issue.

Don’t Be Afraid To Remove Your Platform

Seemingly the best way to ensure your project is fresh is to remove and re-add your platform whenever you make a change:

ionic platform r, android && ionic platform add android

This way, you can be sure you are getting the exact plugins you expect rather than some existing ones that may be cached / different to what you expect.

This process *should* blow up if any of your plugins have errors / unmet dependencies etc.

Other Things I Had To Do

These bits are things I did which I am unsure whether they helped, but they were done so are here for reference:

sudo chown -R {yourUsername} ~/.android

I found my ~/.android (my user’s home directory /.android dir) was owned by root. This caused the build to fail. The message was quite clear on this. Not sure how it ended up owned by root in the first place mind.

ionic browser remove crosswalk

(http://ionicframework.com/docs/cli/browsers.html)

In App Notifications Working!

android local notifications with ionic
android local notifications with ionic

Success. At last! 🙂

Shhh, Don’t Tell Anyone About My Bit On The Side

Side projects are, in my humble opinion, by far and away the best thing that we as developers (not just a PHP developer, a developer in any language) can do for ourselves, our careers / bank balance, and most importantly, our happiness.

Dell 5K Monitor
Dell 5K Monitor
Having a day job is all fine and dandy. We have to put bread on the table. And that snazzy Dell UltraSharp UP2715K 27″ 5K Widescreen LED Monitor with its 5120×2880 Resolution won’t pay for itself. Good Lord how I want that. Still, with the pace technology moves at, in two years that kind of resolution will be in your pocket on the iPhone 9… but I digress.

The big problem is that in most instances, the code you are writing for your day job is not the code that get’s you excited.

Let’s not blame the code though. It’s unfair to blame that friendly if / else statement. After all, even if you were writing the software of your dreams, you would likely still need a conditional here or there. Or maybe not if you’re Sandi Metz 🙂

Rather, it is the domain in which your software applies that may not be your dream come true.

But we can fix that.

We can work in our dream domain in our own time. In my experience, few are the software developers who don’t have ambitions of making their own app or software company or dream product. And yet, so few work in their own time to make those dreams come true.

Jobs was a poser. He didn't even write code.
Jobs was a poser. He didn’t even write code.

How many times have you thought about learning Python, Go, or the latest JavaScript framework (I am loathe to name just one, for fear this post will be outdated by the time I hit publish)? Every time you look at the job boards no doubt.

What about tinkering with Redis, Elastic Search, or GrayLog?

No, management don’t understand any of that. And anyway, what are you doing looking at that stuff? Get back to work. There’s a thousand unresolved tickets in JIRA. Marketing need to know urgently whether that div with background-color #9888ce converts better than #a89ad5.

Ugh.

Please don’t put your life in the hands, Of a Rock n Roll band, Who’ll throw it all away

Ok, so far, so sweeping generalisations.

I’m not here to rag on day jobs.

Management may very well have good reason why you shouldn’t be looking into blindly adding new tech to their existing stack. But this can’t be your excuse to sit on your hands and stagnate.

I find it baffling that simply because a technology of interest is not in use in the work place / day job, that this is any reason not to actively try / learn about said technology in your own time. And worryingly I see this attitude time and time again.

But Chris, Learning technology in isolation is a bit dull… I hear you cry.

And I agree.

The Roosevelt Elk
The Roosevelt Elk, essential for any modern dev stack
Sitting down to figure out the ELK stack is a worthwhile en devour, but this largely comes down to reading some docs, having a stab at installing all the component parts, seeing the dashboard and thinking: Great! Then, powering down the VM and never looking at it again.

Why?

Because there is no purpose to it. You installed it. It worked. Next.

What is much more interesting is to use the technology in context.

Imagination Station

Let’s imagine a happy place where you have taken that app, that SaaS, that dream of being the next Zuckerberg, and you’ve seen it past the first hurdle… that is, you’ve actually started it. There’s some code on your machine that does something. Regardless of how small that something is, it’s a step towards improving yourself, your career / bank balance, and your happiness.

Now, if this project is of any worth, making sure it doesn’t fall over in some unexpected way should be quite high on your list of priorities – especially as your project grows and you and your end-users start interacting with it.

Suddenly your VM with that lovingly built ELK stack has a purpose. Hurrah. Fire it back up, quick. Let’s find a Symfony bundle to make sending messages from our project to Logstash so we get some nice visuals in our ELK dashboard.

Even if your project ultimately gets shelved, the knowledge you have gained from implementing ELK in a real / production environment puts you well ahead of the majority of your peers.

And of course, the ELK stack is just one such example. I use it because implementing logging / monitoring is an inherently useful and transferable skill to have. Setting this up for one project is no different to setting it up for another – whether that be another side project (you used Ansible right, so this is easy and quick to do?) or for your day job.

I’ve worked in places that actively discourage you from having a side project. May I be the first to say – that’s a stupid policy that can only hurt both you and them. And then there are places like these. This reminds me of that story about the brick layer who build his own house in his spare time, and then found out his home was actually owned by his boss at the building company where he worked. Oh yeah, wait, that never happened.

Continuous Improvement

I want to share with you some tips and tricks I have found to motivate myself towards a lifetime of Continuous Improvement.

1. Make a list.

Start by making a master list of all the projects / dreams / ideas you have in your head.

This doesn’t have to be fancy – pen and paper is a great way to start, but of course, there are likely a ton of apps for this sort of thing too.

I use pen and paper as it’s simple and easy, and available to me when I don’t want to be sat at my computer (shock horror).

If your list is anything at all like mine, it will be long and non-exhaustive. That’s great, keep adding to it whenever you think up something new. Simply writing it down will free up some of your limited head space to focus on the here and now.

And again, if your list is anything like mine, one simple line on an A4 jotter could be a year or more’s worth of work. That’s cool too.

2. Chunk in down

For each item on your master list, start a new page and brain dump everything you think you will need to do to make that idea into a reality.

As most of my ideas are ‘software for x’ kind of things, I like to brain dump everything I think I will need to do to get to v1.0.0.

I have never yet managed to write everything down in such detail that this covers every single task I need to do. That’s cool too. This process is a lot like Behaviour Driven Development (BDD) in that you will only start discovering more of the steps to get you to your goal as you start working on achieving that goal.

Try and write down everything and anything that comes to mind. The amount of mental clarity you will gain from freeing up clogged brain space is a very worthwhile side effect of this exercise.

3. Pick Something… Anything… And Get Started

I found that initially, possibly the hardest part is knowing which project to pick.

This is a nice problem to have. However, it can be frustrating.

My advice – pick the one that has the shortest path to v1.0.0.

By writing out each of your dreams / goals, you should have a much better idea of the timescales each project will involve.

If two or more look very similar in terms of time, choose the one that you would most enjoy.

Once you have picked one, look at your list for this project, pick the easiest thing to do from that last, and get started.

Just make sure you get started!

4. The Daily List & The Daily Review

Whatever you opinion of the daily stand up meeting, completing a similar exercise for yourself and your own projects is highly rewarding.

Just don’t do it first thing in the morning. Do it last thing at night instead.

You already have you high level overview, but the day to day may require more technical information, bug fixing, or time consuming bits of admin.

Running through this first thing in the morning is a time consuming waste of energy. Instead, review, re-organise or re-arrange your task list in the evening. That way you can hit the ground running first thing the following morning, or whenever you next get chance to work on your project.

This is similar in some ways to leaving a failing test when doing TDD or BDD. You work to a point, then leave the test failing for your next / current feature, which in turn makes it much easier to get back into the swing of things when you next load up your code base.

Each night you should review your tasks, prioritise accordingly, and be realistic about what can be achieved the next time you will be working on your side project. The very act of making and reviewing this list will increase your personal productivity significantly, and being able to see a check list of ideas, goals and tasks each ticked off on your paper is hugely motivating.

5. Use Technology You Know

Your own, personal, GitLab
Your own, personal, GitLab
Git is probably the single biggest technological win for me in the past few years.

A simple task on your list of daily tasks towards achieving your goals could be to git init a new project, and push the contents of your dir up to your newly created repo. Progress!

The real power for me has been standing up a Gitlab server for my own private projects.

Inside GitLab you can add in task / bugs lists, add in Wiki entries for complex parts of your project, and generally do 99% of the things you can do with GitHub. About the only thing not there is the public’s code to search.

But the most powerful feature of GitLab for me is the Contributions Calendar. GitHub have this, and if you were to look at my profile you would think I rarely did anything.

Not so. I just commit to private GitLab instances a heck of a lot more than I use GitHub.

Keeping that streak alive is a powerful motivator, and it’s built in for free if you use the same GitLab a lot. Of course, you’re free to use GitHub or whatever, but don’t under estimate the power of seeing all of your daily activity in a friendly little widget.

Share Where? Here. There. Everywhere!

So now you have one or more projects you are working on in your spare time.

You’re immediately ahead of the curve.

This makes your resume more complete. It gives you things to talk about inside an interview situation that aren’t blah blah corporate finance payroll software blah blah. And hopefully it’s got you highly excited about some of the amazing open source tech that makes your world a much better place to be.

You can choose to share these projects any way you see fit.

Of course, be careful about this if you do work for a company where side-projects are banned or frowned upon.

Share the source code via GitHub – hey, free side benefit is your GitHub Contributions Calendar will look awesome!

Talk about your project on your blog – you do have a blog, right? If not, add that to your list.

You don’t have to share your source code – talking about what you’re up too is fine, and sharing some code snippets with a bit of context can be equally helpful.

Always wanted to do a conference talk? Great, your side project is likely interesting to others – maybe not the domain in which you operate, but the process and the journey it’s taken you on.

Meet new, like-minded people. Every language has it’s own community. There are some real lemons in each one, I’m sure. But don’t let that put you off. You will highly likely find some amazing, like minded friends amongst them.

tl; dr

If your passion is coding, do it every single day.

Working on code / in a domain that interests you is much more motivating than coding for a pay-cheque.

As software developers we can build whatever we can dream up.

Dream up something – anything – and get started. The journey is just as important as the destination.

2 Things I Wish I Knew Earlier About Ionic Nav Menu

There’s a couple of issues I have hit on today as I was working through an Ionic nav menu that I wish I knew about earlier.

The good news is, both can be demonstrated in the same code sample.

Let’s see the code first, then go through what it’s doing:

<ion-side-menu side="left" expose-aside-when="large">
    <header class="bar bar-header bar-calm">
        <h1 class="title">Menu</h1>
    </header>

    <ion-content class="has-header">
        <ion-list>
            <ion-item ng-repeat="dayName in days"
                      menu-close
                      ui-sref="app.day-view({day:'{{ dayName }}'})"
                      nav-clear
                      ng-class="isToday(dayName) ? 'item-stable' : ''">
                {{ dayName }}
            </ion-item>
        </ion-list>
    </ion-content>
</ion-side-menu>

Upper Class

The first is that when using the ion-item it’s possible to use the Ionic predefined CSS styles to give your navigation / list items a bit of colour.

The available options are the same that are available for everything else, just prefixed with item:

  • item-light
  • item-stable
  • item-positive
  • item-assertive
  • item-balanced
  • item-energized
  • item-royal
  • item-dark

If you’re unsure about what any of them look like, this is a good example.

Notice, this goes on the ion-item definition, and although I have used ng-class in the example, this can be swapped out for the standard css style class.

Standard CSS example:

<ion-item class="item-stable">list item text here</ion-item>

More dynamic method using ng-class and the ternary operator to do an inline or shorthand if / else.

<ion-item ng-class="yourFunction(yourProperty) ? 'item-stable' : 'item-light'">
  {{ yourProperty }}
</ion-item>

Ionic Losing My History

I’m going to cut right to the chase on this one – if you are finding your ‘back’ operations get all screwed up when using a side menu, then search your project for instances of nav-clear and remove with extreme prejudice.

No doubt this is wanted in some circumstances, but it cost me at least an hour of head scratching today – not helped by the Ionic forums being offline for some reason.

It turns out this nav-clear attribute deletes history, in my case, whenever a user clicks on a different menu option. Not at all what I wanted.

The documentation for this is good, but it’s seemingly hidden. I could only find it with a well thought out (read: after a million attempts) Google, but frustratingly it is all there if you know what to search for.

It just doesn’t show up on the menu anywhere as far as I can see.

Always Open Sidebar (On iPad or Similar)

Even though I said I would do two tips, this third one is worth mentioning. This one didn’t actually cost me any time – it was found whilst Googling for the other problems above.

Anyway, it’s too good not to share.

If you have a side menu and you think it would be a good thing if it was always open, should the device have enough screen space (e.g. on an iPad or similar), then stick in the following:

expose-aside-when="large"

And so long as your device width is >768px it will always show the sidebar.

This one is right there in the documentation, but I thought it was awesome and worth sharing.