Code Small FTW: Thursday

Yesterday’s installment including the Pomodoro Break and The Server Side sections can be found here.

Putting it all together

I now had a front end iPhone app and back end services, both of which were tested in isolation. It was time to hook everything up.

My next pomodoro was spent hooking up the settings tab of the app to the authenticate endpoint on the server side. In this case, I started with authentication since I knew that I would have to authenticate prior to being able to hit any of the other endpoints. I soon realized I would need some additional fields on the settings tab. I needed a “Login” button to pass along the token that had been entered. I also wanted a “save” switch since it would be a hassle to have to enter the auth token every time you launched the app. This would enable the auth token to be saved in the app and to have the app auto-login each time it was launched. This was inline with the goal of making sure that the network did not get in the way. Here’s a screenshot of new settings tab:

 

Before I implemented the save token and auto-authenticate functionality, I wanted to hook up the “Login” button to actually hit the server side endpoint.

I took an initial stab at calling the backend authentication service inside of a pomodoro and then ran into a roadblock. Authentication from the app was failing. I added some logging code and things were blowing up. The short story: I spent a frustrating 2 hours debugging, outside of pomodoros. As is often the case, I finally made headway by discovering a dumb mistake and then working through some poorly documented aspects of Titanium Appcelerator.

First, I was getting some perplexing output in the console. It was a single line that looked like:

[WARN] Exception in event callback. {

This output was less than helpful. It seemed clear to me that there were additional lines of output that were getting chopped off. I remembered seeing multi-line errors in the console in the past. It also occurred to me that I had recently updated Titanium. After some digging around, I found I needed to change the global log level in Titanium Studio in order to see multi-line output in the console. This is clearly a bug in the new version of Titanium Studio. In case anyone else runs into this, you can make this change by following these steps:

  1. Right click the project name and choose “Run As” -> “Run Configurations…”
  2. On the “Main” tab, change the Log level drop down to: “Trace”
  3. Click the “Apply” button

Once I had multi-line output in the console again, I discovered the dumb mistake I was making. Because I was frustrated, tired and not taking breaks it took me longer to discover the mistake than it would have otherwise. Perhaps I would not even had made the mistake in the first place? I wanted to have some debugging output to figure out why authentication was failing. I added in some code like this:

Ti.Api.info("I'm here...");

This code was throwing an exception because the correct form of the call is this:

Ti.API.info("I'm here...");

Note the capitalized “API” in the function call. Sheesh! What a bunch of wasted time. Still, I was (once again) in the “plow through it” mode and continued debugging.

After being stuck for a while with authentication inexplicably NOT working from the app while working just fine from the command line, I decided to get a little closer to the metal. I wanted to see the exact HTTP conversation that was being generated on the App side. I ended up purchasing HTTPScoop – an excellent little HTTP snooper app for Mac.

Using this, I was able to compare the HTTP conversations from the command line using curl and from the iPhone app running in the simulator. What I found was that the iPhone app was including a session cookie in the initial call to the authenticate endpoint while curl was not. Where did this cookie come from? It probably got stuck there during an unsuccessful attempt to hit the authenticate endpoint and now it was causing the call to fail every time since there was no matching authenticated session on the server side.

This where I lost some more time finding out that I needed to call the “clearCookies” at initialization time when working with Titanium’s HTTPClient. I’ve interacted with services before with Titanium and didn’t remember having to do this, so it might be another “feature” of the update. I finally found a reference to the issue here. To be fair, the comment on this link talks about needing to clear cookies when the server sets a cookie and it needs to be removed, so it’s possible that the problem was on the server side in the express or connect libraries. My point here, is that this function call is poorly documented and it took a while to figure it out. It was well worth the cost of HTTPScoop to figure out the root cause of the problem.

It was now as easy as I was used to get the communication between the app and server side working and authentication was working properly. After spending these 2 hours of debugging, I once again came to my senses and took a long break before returning back to the next pomodoro.

At this point, I had worked for 23 pomodoros plus 1.5 hours of unstructured work time at the beginning and another 2 hours of debugging time for a total of a little over 13 hours of work time.

The next pomodoro was spent hooking up the transaction endpoint to the transaction tab in the app. Piece of cake.

In the pomodoro after that, I hooked up history. That went quickly, but I realized that I ought to have a refresh button since both my wife and I could be using this at the same time.

I spent the next two pomodoros hooking up the process endpoint (to remove transactions from the history once they had been processed) and to add in the refresh button and its functionality.

It took another two pomodoros to add in the save-token and auto-login functionality. I also did some refactoring and general cleaning up in these pomodoros.

My next steps were to spruce up the icons, create a splash screen, deploy the server code and get the app on my phone for some real world testing. I hit the wall of the Apple deployment process involving a quagmire of key generation and website portal interaction. It had been some time since I released my first iPhone app, Passable, so I had to relearn the process all over again. All I can say is: bleh! I understand the Android deployment process is far simpler.

Just as a little sidebar here to illustrate the pain Apple makes you go through. You cannot paste into the fields on the developer web portal pages. Since I use PassableDesktop to generate my passwords, I had to “trick” the Apple developer portal into letting me paste my password in. I did this by using Google Chrome’s Inspector and deleting the code on the page that prevented me from pasting in the text fields.

A full discussion of the device deployment prep and execution process is way outside the scope of this article. Suffice it to say that a few hours later, I had the app on my iPhone.

Heroku makes deployment of code so easy. I am a HUGE fan of their service. Their Cedar stack adds the ability to deploy Node.js code. A few setup commands later and a heroku push and the server side was deployed. I also primed the mongo database at mongohq with user documents for my wife and I, including the all important value for token.

I verified that the server side app was responsive using curl. I launched the iPhone app, put in the value for token and clicked the Login button. And… nothing. I got the handy popup notification that the app was unable to connect to the server.

I realized pretty soon that config in the app was set to development, which meant it was trying to connect to my local computer. I changed the environment variable in the app to production and re-deployed the app to my iPhone. And… voilà! It worked! (Since I had put in my token and enabled the Save switch, it logged me in as soon as I launched the app again). From this experience, it became clear to me that which server the iPhone app connected to should be configurable in some way. This is a good example of avoiding early optimization. Having the server hardcoded to localhost served me well throughout the development process. It stopped me from going down a rabbit hole of building in flexibility before it was needed.

What I ended up doing was to have the app automatically determine which server to use based on whether it was running in the simulator or real hardware. In Titanium, the setting that has this information is:

Titanium.Platform.model

A quick check of that property and the iPhone app can set itself to development or production mode. Perhaps in the future I will build some UI elements to put in whatever server you want, but that was overkill for the project at this point. The property check was just the right amount of code for this situation.

Here’s a screenshot of the final version of the app with updated splash screen and icons.

A handful of minor bugs emerged that were easily fixed. A few feature requirements emerged as well. The first was that the app should re-login each time it is resumed. The authentication process is very lightweight and fast and this way we don’t have to worry about session timeouts. The second important feature update was to have the history listed in date order. This took a small amount of work because I originally was storing the dates as strings. Because of good test coverage it was easy to make the change to store the dates as long values.

Another quick side note here: If you’ve ever worked with sending JSON objects over the wire, you probably figured out pretty quickly that you can’t serialize javascript Date objects. The best way to store and pass dates around is as epoch time longs. It’s then easy on either end of the wire to reconstitute a Date object.

The best part of the implementation of this feature was that I didn’t have to make a single change on the app side. The app expected a history list and presents it in the order received. Once I had the dates stored as longs in mongo, it was simple to retrieve the history in date order and to replace the longs in the resultant list with their pretty string representations. The app dutifully displays the history list as always.

This experience supports one of the major points of this exercise: the new feature requirements (as well as the bugs) emerged very naturally through the use of the application. Trying to architect all the features upfront (as opposed to only those that are critically needed) would likely have produced a bunch of features that were, potentially, under-utilized or not used at all.

Tomorrow, the final installment: Epilogue



Leave a Reply