Initial Reaction to React Native

This is to summarize my first experience with React Native. My overall impression is positive and setup went rather smoothly. I hit a few issues with build process and the environment that are worth listing here.

After generating the initial app (using ‘react-native init’) and trying to run it.

  1. “Missing Iconions” icon error. It turns out I was missing a ‘linking’ step. Although I had added the icons using npm, they still needed to be linked to the native project, in this case the Xcode project file. ‘react-native link’ does the trick.
  2. “Could not load app due to a missing require()”. Weird error! A quick search suggested that there is a name mismatch. Once you generate the app, you cannot change the name that gets registered with the AppRegistry. 
  3. Changing the name back did not fix the issue. I got another strange error but this time React gave me three suggestions to fix it. Apparently there is some caching of app content going on that needs to be cleaned up, either by deleting the npm_modules folder and clearing npm cache or simply by killing the packager process and restarting it. This error occurred a few times since then and every time killing the packager solved it.
As soon as I started writing some UI components, I realized that I must be repeating work that someone else has done. Then, I landed on Native Base, which looked promising. I hit a few issues with it as well which added to my initial list:
  • The ‘List’ component skipped the first item. No matter how many items are included in the model, it always skipped the first one when rendering. It turns out that the List component has to be wrapped in a ScrollView.
  • I was not able to mix usage of Native Base ‘ListItem’ with React Native TouchableHighlight to produce a selectable list item that shows a visual queue when tapped. It turns out that there is already a ‘button’ property that can be set on the ‘List Item’ to produce such a visual effect. It is not perfect, but it is OK for a toy app.
Got the basic Native Base components to work in my app. Back to using just React Native components. 
  1. I attempted to add some navigation using NavigatorIOS. At first, it did not show any content I added to it. As I found out, some styles must be set on the navigator in order for it to work. Most notably, you have to use the {{flex:1}} style.
  2. Specifying property types using PropTypes is neat. Just avoid the pitfall: PropTypes.something.isRequired is correct, whereas PropTypes.something.required produces mysterious error messages!
  3. Also be careful when passing functions as properties. I mistakenly passed one of the properties as {someFunc(someParams)} instead of just {someFunc} which, of course, lead to invoking the function rather than passing a callback.
I am aggregating more notes that I will share later.

Compiling Java in Gulp – Fun Experience Chasing Plugin Issues

Working on the Qortoba project, I needed to add a Java tool for generating JavaScript classes from a Java interface. The need to compile a couple of java source files did not warrant  incorporating Maven or Ant into the project, and a shell script sounded too archaic.

Since I was already using gulp, it made sense to search for a plugin that does the job. Indeed, I found ‘gulp-javac’ which seemed simple enough to adopt. However, after installing with npm and trying to run it I found that it uses some ES6 features, in particular the dot dot dot ‘spread’ operator. I looked up the version of node vs. ES6 support and decided to install the latest (why not!).

Installing the new version with nvm with a breeze. However, after I switched the node version gulp was not found, even after I reinstalled it globally. I figured maybe there is some difference in how the new npm organizes packages, so I deleted the npm_modules folder and reinstalled.

Now, I was getting an error where the ‘addLibraries’ method was not found on the javac gulp task. It was not clear why, so I tried a few things. I replaced the short version of the command (which combines ‘javac’ and ‘jar’) with the long form, and this solved the ‘addLibraries’ problem.

But then I started getting a “file not found” error which seems to have resulted from failure to create a tmp directory. Looked inside the code and found that it uses a some package called ‘tmp’ for managing temporary files and folders. The ‘dirSync’ method that was used was being fed a parameter that allows it to delete the tmp folder on exit even if it was not empty. I removed that parameter in the source and got the same error. I created a two-liner test for the directory creation and I got the same result. It turns out that the ‘tmp’ package deletes folders upon exist anyways, unless you pass it a ‘keep’ parameter. Setting this parameter to true made the two-liner work but not the javac task!

At this point I decided to file an issue on github, and to my delight I got a quick reply indicating that the author found a bug with having the jar file created under any path that contains slashes! The bug fix solved my issue, and I then filed another issue for the ‘addLibraries’ mis-documentation and that too was fixed within hours. 

Presentation @ Google Developer Group LA

Finally got to put all bits of information together and present on connecting JavaScript to Java on Android. It so happens that the hosts, Victorious, talked to me afterwards and indicated that they had to solve the same problem! They added a very valuable hint: when deploying to the Google Play store you have to exclude any HTML/JS files from being processed (obfuscated) by ProGuard, otherwise any reference to injected Java objects will be rewritten causing obvious problems.

Slides can be found here.

Integrating Third-Party JavaScript Widgets with Angular

I was working on integrating a gauge widget into an Angular directive. The widget requires to be setup in JavaScript that looks something as follows:

var gauge = new JustGage({ id : "the-element-id", min: 0, max: 100, title: "Gauge Title" });

It makes sense to represent the gauge widget as its own directive, so I created a directive for that purpose. Then, this imperative/procedural code that manipulates the DOM should go in the ‘link‘ function of the directive. So far so good.

The gauge requires an element to render itself. The ‘id’ attribute of the gauge configuration specifies the ID of that element, which. In my case, I needed to specify the ID dynamically. The ID of the gauge element was supplied to the directive via an attribute:

<my-gauge id="{{model.id}}" ... ></my-gauge>


On first attempt to run, I got an error saying that the element was not found. After debugging a bit and Googling a lot, I found out that the problem is mentioned in this post:

     "Notice that when the link function runs, any attributes that contain {{}}'s are 
      not evaluated yet (so if you try to examine the attributes, you'll get undefined)."
Then came $observe to the rescue. $observe, as opposed to $watch, allows you to listen on changes in the attributes of the directive element via the ‘attrs’ object passed to the link function. So in my link function I did:
attrs.$observe('id', function(newValue) {
// initialize the gauge here
};
And that solved the problem.

Continue reading “Integrating Third-Party JavaScript Widgets with Angular”

Conditional Injection – Testing Angular App on Android

I was developing an Angular-based app inside an Android app. That is, my Android Activity incorporated a WebView that loaded HTML/JavaScript pages that use Angular.

The Java code in the Andorid app communicated to with the JavaScript running inside the WebView by injecting a Java object into JavaScript as discussed in a previous post.

Cool! So?
The problem arose when I wanted to test the app. One could simply test the app within the Android IDE (Eclipse in this case), but the development cycle is much faster if I could just use a browser as one would normally do when building a web app. But the JavaScript depends on the injected Java objects in order to function, so how can I test my app in the browser in absence of the Java code that injects these objects.

I embarked on the following solution:

  • Encapsulate each of the injected Java objects in a “wrapper” Angular service.
  • Also create Angular services that mock each of the wrapper services.
  • When running in Android instantiate the wrapper services, otherwise instantiate the mock ones.
The trick here is that both services, the wrapper and corresponding mock, have the same name and only one of them can be instantiated in a particular run. Since there is no built in conditional compilation/inclusion directive akin to that of C, I came up with my own using a directive that looks like this:
<script conditional-load
        if-defined=""
        else-load=""
        then-load=""/>
The core code for the directive checks whether the specified reference is defined and loads either script accordingly.
myModule.directive("conditionalLoad", function($window, ScriptLoaderService) {

 return {
     link: function(scope, element, attrs, controller) {
         ...
         if ($window[attrs.ifDefined]) {
            ScriptLoaderService.loadScript(attrs.thenLoad);
         }
         else if (attrs.elseLoad) {
            ScriptLoaderService.loadScript(attrs.elseLoad);
         }
     }
 }
})
With an instance of this directive for every injected Java object we are left with writing the ScriptLoaderService, which turns out to be non-trivial, and I will leave this to a separate post.

Twitter Broken GeoCode API

Using the GeoCode parameter with the Twitter Search API did not work for me (returned no results). A quick search turned up this post on the Twitter Community pages. It seems that issues were reported with this API over a year ago. Since then, it has not been fixed and now they say it may never be fixed!  I am not going to rant about how shameful it is to have a public API that you know does not work and just leave out there, but …

Angular Multiple ng-transclude

Quick post that may save someone a lot of time and frustration. Transclusion is a great feature in Angular (see the documentation). The ability to use multiple ng-transclude bits in your template makes it even greater. However, it took me a long time to get the simplest example to work. The problem was that each occurrence of ng-transclude in my template, even though assigned to a specific slot, did end up including everything from the HTML and not just the specified slot.

I compared my work to other examples that did work and found no difference at all in the template, the HTML, nor the code.

Long story short, I noticed in some article on the subject that the multiple transclusion feature was added to Angular starting 1.5. I went to check my bower.json and indeed, I was on angular 1.4.8. Upping the version to 1.5 did solve the problem.

Android: From Java to JavaScript and Back

After the fight with Android Studio and Eclipse yesterday, I finally got to try what I was planning to do all day:

  • From within JavaScript running in a WebView, Invoke Android Java code , and
  • From Android Java code, inject JavaScript code into an HTML page in a WebView.

There are a few samples that can be found for both routes, either in the official docs or in blog posts. However, there were some tricks to get even the simplest example running.

Other than the standard issues of having to enable JavaScript in the WebView and annotate the callback method, here are a couple of interesting ones that I faced trying the first route:

  • Default WebViewClient: Attempted to run some trivial JavaScript inside the WebView to make sure it is enabled. The simplest of examples (popping an alert) did not work. After looking around, it turns out that you must set a WebViewClient on the WebView, even if it is simply the default one from the SDK. Apparently, it provides a method that handles showing alerts to make them fit with the theme, etc.
  • Method Signature: The simplest of examples for calling Java from JavaScript did not work for me. I was getting a “Error calling method on NPObject” message. After trying a couple of things I decided to post on StackOverflow, but I had to double check my code before doing so. And guess what I found? I found that the method signature called from JavaScript did not match the one declared in Java. Fixing that got the sample to work.

The second route was not a smooth ride either. Here are a few issues I encountered:

  • Threading: Apparently, any interaction with a WebView has to happen on the ‘same’ thread (as the error stated). I figured that this meant the event thread, and indeed scheduling the injection of JavaScript using runOnUiThread solved this.
  • Navigation: Upon successful execution of JavaScript code, the original page content was replaced by the result of the JavaScript statement that just executed. Apparently, this is to be expected and others have solved this by appending a void(0); to the JavaScript code. The reason is explained here in detail.
  • Arguments: When passing JavaScript code as a String do not forget to quote values that are supposed to be String valued. This simple advice might save you considerable time if you are passing around element IDs. Apparently, when an element ID appeared in the JavaScript unquoted the interpreter took at it as a reference to the element!

Android Studio and ADT on Ubuntu

This was not a fun day.

I spent hours trying to set up Android Studio on Ubuntu. I followed the instructions given here (after eliminating the extra spaces in the URL) and things seemed to be going. After the install, I went through some gyrations to get the Studio to update and run. I imported a project and tried to run it and the IDE started throwing exceptions in the logs. I fought with it a bit, trying to update the SDK, tools, and installing the missing 32-bit C libraries as suggested in this post (only a few are actually needed, it seems, but I installed all just in case). However, new errors kept popping up and I finally decided to go back to what I know; the Android Eclipse plugin.

It was a surprise to me that Google removed the installation instructions for the Eclipse plugin from their site in an attempt to push the Android Studio. And indeed they tout it as the official IDE now and urge people to migrate.

Anyways, I got the plugin from the Eclipse Marketplace. After installing the SDK (again) and trying to run a sample, Eclipse complained about inability to connect to adb, and kept retrying. I went ahead and manually started the adb server from the command line and this did work, at least the first time. Thereafter, Eclipse complained that the connection was lost and prompted me to start both adb as well as Eclipse itself! After having to do this a couple of times, I realized that the SDK path pointed to the first one I installed, which did seem to be broken in some way. Fixing the path got things working again.

Next, I went back to my project but it had compile errors. Apparently, in the initial SDK install I picked (somehow) just API level 17, whereas the feature I wanted required API level 19. I went back and downloaded both 19 and 23 (the latest) which took forever. Finally, I was up and running!

Dropdown and Popover services in AngularStrap

As per a previous post, I have been using AngularStrap to combine the strength of AngularJS with the beauty of Bootstrap widgets. I think it is fair to say that it has not been a smooth ride. Part of the reason is the API docs for AngularStrap are far from complete or even correct.

Today, I was trying to incorporate a dropdown (a context menu, really) and ran into a case where the documentation does not show how exactly to use the $dropdown service (to launch the popup from the controller).

Looking at the source, I found that there is a show() method similar to other services in their collection. However, this did not quite work and I ended up with a runtime error:

TypeError: Cannot read property ‘link’ of undefined

Googling around I found this plunk that supposedly showed the correct way of using the $dropdown service. However, this did not help my situation and I was left with the same error.

Scratching my head some more and looking around, I found this post about using the $popover service, which works in the same way as the $dropdown one. It suggested that $promise has to be used rather than just calling show() directly. It was unclear what $promise exactly is, and again looking at the source for the tooltip module, which is the “base” for the dropdown module, it turns be a property computed from the Angular compilation of bootstrap content. Looking further, the $bsCompiler module does a bunch of stuff with the template asynchronously (using the $q service). So that was the explanation for why I needed to show the dropdown in this way:

dropdown.$promise.then(function () {
    dropdown.show();
});
This did work and my dropdown (context menu) did show and work properly.