Uncommented Bytes

Thursday, July 07, 2016

Guide to Creating Native Mobile Apps with Ionic2

Over the last few years I've had a nice little side project called GasPumpr.com. It is an ethanol efficiency calculator that determines the equivalent price-per-gallon of different ethanol blends. I use it to decide which gas is the most economical to purchase when standing at a pump that has different blends (no-ethanol, e10, e15, e85). You can read all about how it works and why at http://gaspumpr.com/about.html, but the TLDR is that ethanol has a lower energy output than gasoline so it needs to be cheaper to make it worth the reduced miles-per-gallon that it provides.

It works great as a responsive mobile site, but I wanted to create a native app to make it available without a network connection. This is where Ionic2 steps in. Ionic2 uses Angular2 on top of Apache Cordova to generate native iOS and Android apps. It essentially allows for html/css/JS/AngularJS (which I know well) to be used instead of ObjectiveC/Swift (which I know very little).

The purpose of this post is to roughly document the process that I went through to install/build/run/publish the GasPumpr Ionic2 application. There are many Ionic2 guides out there that I used as reference, so I'm using this post to mostly document any gaps along the way. All of the source is on GitHub in the gaspumpr-ionic2 project.


Installation is covered well in the Ionic2 Getting Started Guide
  • npm install -g ionic@beta
  • ionic start gaspumpr --v2 {generates a default app with the 'tabs template'}
  • cd gaspumpr
  • ionic serve
The `ionic serve` runs the app in a browser in livereload mode. So any changes to the source will be noticed and rebuilt quickly and refreshed automatically into the browser. A simple thing, but if you've never used livereload you'll quickly find it irreplaceable. Now go into Chrome DevTools and enable the Device Toolbar by clicking the phone-icon on the top left of the toolbar. Now you can pick a phone size to easily see how the app will look on various browsers.

Device Emulators

Before going further I wanted to see the app running in Android and iOS emulators.
  • Install Cordova - needed to run on the emulators
    • sudo npm install -g cordova
  • Android Emulator
    • ionic platform add android
    • Install Android Studio from https://developer.android.com/studio/index.html
      • After installing, launch it once and open project in platforms/android
      • Then close Android Studio
    • Genymotion Android Emulator - free
      • Install from https://www.genymotion.com/
        • This requires creating an account
      • Launch Genymotion and create a Virtual Device (I chose the Samsung Galaxy S6)
      • Now run the Virtual Device
    • ionic run android
    • At this point I received an error like Warning:Gradle version 2.10 is required. Current version is 2.2.1. If using the gradle wrapper, try editing the distributionUrl in gaspumpr\gradle\wrapper\gradle-wrapper.properties to gradle-2.10-all.zip

      I think I got it because I had an older AndroidStudio previously installed before I did the `add android` command. In any case, the fix is to edit platforms/android/cordova/lib/builders/GradleBuilder.js and change the distributionUrl gradle wrapper to be 2.10 like this.

      Next I had an error like Error executing "adb devices": ADB server didn't ACK * failed to start daemon *. Fix this by going into Genymotion settings for ADB and select custom Android SDK and select your SDK location (mine is /users/jeffsheets/Library/Android/sdk). Thanks to this tutorial for the fix.

      After fixing this and running ionic run android I could see the app in the android emulator.
  • iOS Emulator
    • Install/update XCode on your Mac
    • ionic emulate ios {this will launch in the default emulator - iPhone 6s Plus for me}
    • ionic emulate ios --target="iPhone-6s, 9.3" {for running on 6s, similar for 5s}
      • Note: Supposedly you can run ionic emulate ios with --livereload to see file changes quickly in the emulator. But I couldn't get it to work, so after any file change I'd have to rerun this command to see the changes.


At this point I roughly copied all of the html source from GasPumpr.com into the page1.html and page2.html files. Along with the styles into the appropriate style files.

Only a couple of notes on development.
  • It wasn't clear where to add images that were used in the app. I settled on adding them in www/img folder and then in the css referenced the location like background-image: url(../../img/gas-icon.png);.
  • I wanted to select a Tab via a separate button click.  This StackOverflow answer helped me out.

Ionic View

The Ionic team has provided an Ionic View app that can render your app on device by basically just pushing the html/JS/css to the device. It is REALLY SIMPLE to get working so this is an easy step to quickly see your app running on iOS or Android. (And as a friend pointed out, it is a great way to have your kids create apps and easily share them with friends and family!)
  • The tutorial steps are mostly correct
    • Signup for an account on https://apps.ionic.io/signup
    • Run command ionic upload and enter username and password
      • At this point I had a weird error that wasn't very helpful in google. The answer is to change the ID of the app because it is using the default tabs template ID. So go into config.xml and change the widget ID to something unique for your app. I chose com.sheetsj.gaspumpr for mine. This is also a good time to edit the name/description/author details. Then run `ionic upload` again
    • Install and run the Ionic View app from the App/Play Store
      • Login to app and you should see your application. Run it!
  • Note: when doing this, ionic creates a .io-config.json file that holds your Ionic View api-key. This file is not in .gitignore by default, but I added it so that the api-key is not uploaded to github. Feels like a best practice that everyone should follow.

Test on iOS Device

Testing directly on my iPhone SE was fairly easy too. It is also free to test on your device now with the latest iOS versions. (sorry no steps for testing on Android because I didn't have one available)

I mostly followed these StackOverflow steps to Deploy to your own iOS device.

  • In XCode go into Preferences and add your Apple ID
  • Open the project from platforms/ios in XCode
  • In the Scheme Selector (next to the Play and Stop buttons in toolbar) select your application
  • Plug your phone into Mac via USB cable
  • Select your phone in the Device Selector (next to Scheme Selector)
    • I think I had certificate messages at this point. I clicked a 'fix' button in XCode which generated certs.
  • Now click the Run button
    • First time I got a message to go onto Phone > Settings and enable the app to be executed. Just follow the steps and it worked fine.
  • See the app on the phone!
    • I was surprised to see the app stays on the phone when disconnected. I didn't realize till now that you can create and run and keep your own apps on your iPhone for free. Pretty cool. (yeah, Android people can roll your eyes here)

Setup App Icons

Ionic has a nice `resources` utility that can generate App Icons and Splashscreens of all sizes for iOS and Android for you. For icons just add a 192x192 pixel PNG named icon.png to the resources folder. Then run ionic resources and it will upload your image to an ionic server and process and download icons for you into the correct folders.

Now you can push the app to iPhone and see the real icon on the phone!

Publish to Android Play Store

Following the ionic guide worked perfectly to deploy to the Play Store.

  • The most time consuming part was taking screenshots from the emulators and saving them to the random pixel sizes required by the Play Store submission process.
  • Also it needed a Privacy Policy link on the web. So I just created a simple privacyPolicy.txt file, added it to GitHub, and linked to the raw version.

Publish to Kindle Store

The process to publish to the Kindle Store was easy once the .apk was already built for the Android Google Play Store. Just fill out the Kindle Store Publishing info, upload the same .apk file, and generate some more screenshots of random pixel ratios. And like that it is also published to the Kindle Store.

Publish to iOS Apple App Store

I also plan to publish this to the Apple App Store using these instructions. I'm just waiting because of the high $100 price tag! Once completed I'll update with any progress here.

Labels: , , , , , , , , ,

Friday, December 28, 2012

Jetty JTA via JNDI with Spring

Finally got Jetty 8 to have JTA transactions available to Spring via JNDI lookup using Atomikos 3.8 after a lot of trouble. I'm not 100% sure if this is correct but it seems to be working for me.

My main driver was not wanting to have Atomikos classes hardcoded in the spring config. Instead we can just register the Atomikos UserTransactionManager and UserTransactionImp classes in JNDI and have Spring look them up, just like if we were using Websphere, Weblogic, etc. Now I can use Jetty for local development instead of Websphere without any change to my actual code base. (Be warned that I'm not using this in prod.)

1) Jetty 8 install
    Download, unzip, setup JETTY_HOME var for IntelliJ to use
2) Atomikos 3.8 install
    Download, unzip
    copy from the Atomikos lib/ directory into Jetty lib/ext/:
    copy all the JARs from the Atomikos dist/ directory into Jetty lib/ext/.
    (Hat tip to this blogpost for helping)
3) Jetty config for JTA (Ref posting that helped)

4) Spring JTA lookup
    Spring will lookup using standard JNDI locations of java:/TransactionManager and java:comp/UserTransaction

Labels: , , ,

Tuesday, September 18, 2012

iPhone Gmail Notes sync to Mac

After setting up Mountain Lion on my Mac, I went and tried the new Notes sync feature. I've been syncing my notes from my iphone to Gmail for months now (or so I had thought).

But going into the Notes app on my Mac I did not see anything newer than 6-8 months ago.

Then I checked gmail and the Notes label showed the same. And a few google searches turned up empty.

The fix for me was to go into iPhone settings > Mail, Contacts, Calendars > Fetch New Data > Advanced > and change my Gmail Notes account from Fetch to Manual.

Then I relaunched the Notes app on my phone and everything magically synced.

Scary thing is I think none of my Notes were backed up, even though I thought they were.... Hopefully this will help someone else out there.

Monday, August 06, 2012

CSS word-break and word-wrap

Finding a cross-browser CSS solution to force breaks in long words inside of table-cells is not an easy task. My issue was specifically with email addresses formatted like reallyreally.long.email.addressthatdoesnotbreak@fakeemail.com. The snag is that support is decent inside of block-level elements like divs, but only hacks seem to exist for table cells.

The widely supported white-space will instruct the browser to wrap on normal wrapping characters, but the browsers will not break a word. There are two newer CSS properties to try: word-wrap and word-break.

word-break:break-all will force a break anywhere but ignores spaces. It works well if you know your table cell will only contain a single term or word, and you don’t care about wrapping on commas, hyphens, or spaces.

word-wrap:break-word will honor commas, hyphens, and spaces to wrap first and then will force a break in the middle of words when needed. The catch is it only works cross-browser on block-level elements and not on table cells. Also a width or max-width must be specified.

One promising hack sets a table to be table-layout:fixed but this did not work with the jQuery datatables plugin.

So here is my cross browser solution, tested in Chrome, Firefox, and IE9. It unfortunately requires wrapping your td contents inside of a div. But it does work with the jQuery datatables plugin.

/* forces wraps in middle of words when necessary */
div.force-wrap {
  white-space: normal;
  word-wrap: break-word;
td.email div.force-wrap {
  width: 30em;

  <td class="”email”">
<div class="”forcewrap”">
reallyreally.long.email.addressthatdoesnotbreak@fakeemail.com, another.email.addressthatdoesnotbreak@fakeemail.com

Additional References


Labels: ,

Wednesday, June 27, 2012

jQuery to Disable IE MS Lync phone number icons

Corporate environments have been pushing out MS Lync as the IM successor to Office Communicator. With that comes a click-to-call plugin that automatically puts a little phone icon next to any detected phone number on a web page. I can only assume that clicking the icon will make a phone call, as if anyone really wants that feature to be on by default.

Unfortunately for web developers, the icon is rendered by inserting markup into the webpage. This is a problem for a number of reasons:

  1. The icon looks like it is part of your page, but it's really not. 
  2. It even prints with the page. 
  3. It screws up your layout because the icon is inserted AFTER the page is rendered and onLoad javascript executes! This plays double issues when using a scrolling tables plugin like jQuery datatables because now the header doesn't lineup with the columns.
  4. There is no meta tag to disable the plugin. The only option to disable the plugin is up to the user or administrator, and we know we can't count on users to disable it for us.

Our phone numbers were in the common US format of (555) 555-5555. After some trial-and-error I found that MS Lync does not detect phone numbers that use a non-breaking hyphen (&#8209) instead of the normal hyphens (&#45 and &#4208) (big thanks to this Dashes and Hyphens page).

Now I could have changed our code server-side to render the new hyphen, but this caused issues with our exports to Excel, Word, and PDF. So instead I created a simple jQuery plugin that will replace the common &#45 hyphen with a &#8209 non-breaking hyphen and foil the MS Lync plugin from detecting our phone numbers.

Labels: ,

Tuesday, June 19, 2012

JQuery UI datepicker IE focus fix

A little writeup cross-posted on the OPI blog about making the jquery UI datepicker send blur events when dates change, while handling a quirk with IE:

Labels: ,