noizZze

Ruby, Rails, Elixir and mobile development blog

My current Elixir toolkit

All my free time I'm spending on a my own project that I decided to write in Elixir, Rails and CoffeeScript. There is the service backend that runs heavily multi-threaded and so Elixir / Erlang was a natural choice.

I figure some of my friends might be interested in the tools I use currently, so here's the short rundown:

Release management - exrm

Fantastic tool for building releases ready for the deployment. Prepares a versioned nicely packed archive with all dependencies and start / stop / restart / remote_console scripts.

JSON handling - jsex

Sweet little library for encoding, decoding and jumping through all sorts of hoops with JSON.

Websockets - sockjs-erlang

Although there's great variety of web servers in Erlang Kingdom, I chose this one for my own needs for its simplicity. It sits on top of Cowboy and provides a very nice interface, so you focus on the task not on the Websocket handling chores.

UUID - uuid

Tiny library for all sorts of UUID generation.

HTTP client - httpoison

As with web servers, and basically everything else, there are plenty of options, but the one I stuck with is Httpoison. Being based on HTTPotion, it's an excellent piece of software that makes running HTTP requests async or not a breeze.

These are basically the essentials I spent time finding. Hope it saves a bit of time for some of you.

Completes and totals, or how to count

In every third project I see SQL like this:

SELECT
      (SELECT COUNT(*)
       FROM table 
       WHERE group_id = X AND completed = 1) as completed,
    
      (SELECT COUNT(*)
       FROM table
       WHERE group_id = X) as total
    
    FROM table
    WHERE group_id = X
    GROUP BY group_id
    

As you might understand the intention is to count all records in the group and those with completed flag set. This solution gives me creeps.

Here's how I would write the same:

SELECT
      COUNT(NULLIF(completed, 0)) as completed,
      COUNT(*) as total
    FROM table
    WHERE group_id = X
    

Now pick your DB book and go read what NULLIF function is, and do me a favor. No more crazy subselects, ok?

Appcelerator Titanium: Scaling and cropping images

At the time of writing Titanium is at: 3.3.0 GA

In the project I'm supporting we juggle with camera shots like crazy. One of the recent requests from the client was to show square thumbnails in the list of taken pictures. While the requirement itself is pretty reasonable, Titanium doesn't make our life easier.

It's easy to hack resizing with ImageView that is given the image and set to the known width / height. You then save it to the blob and off you go.

The harder it was to make it actually crop the image with unknown dimensions (thanks to device resolution diversity) to 96x96 thumbnail. Here's how I did it.

I've started with nesting image views inside other views, but that led nowhere. Finally I came across one old module -- ti.imagefactory -- stuck somewhere in the August of 2013 in its development. Even though it's quite old, it still has got everything we need -- resizing and cropping.

Here's how the end result looks:

// Target image dimensions
    var w = 96, h = 96;
    
    // Loading the module
    var IF = require('ti.imagefactory');
    
    // Getting file name like "UUID-<width>x<height>" and
    // matching it to grab the width / height
    var id       = photo.imageId,
        match    = /^.*-(\d+)x(\d+)$/.exec(id);
    
    // Reading the file from the storage
    var file     = FileStorage.getFile(id),
        img      = file.read(),
        finished = null;
    
    // We are storing image dimensions in the file name
    // since Ti doesn't recognize loaded Blobs as images
    // all too well.
    if (match != null) {
      var iw = parseInt(match[1]),
          ih = parseInt(match[2]),
          tw = iw < ih ? w : iw * h / ih,
          th = iw < ih ? ih * w / iw : h,
          resized = IF.imageAsResized(img, { width: tw, height: th });
    
      finished = IF.imageAsCropped(resized, { width: w, height: h });      
    } else {
      finished = img;
    }
    

Here, if we don't have image sizes in the file name or they are "0" for some reason, we just let the image stretch itself. Ugly, but yet better than nothing. Normally, we don't have this issue. It's just a safety net.

Another note, that imageAsCropped isn't given the x/y offset and so it will automatically gravitate towards the center of the image. Sweet.

I saw a ton of discussions on the subject and figured I'd share. Hope you enjoyed this bit.

Thoughts on present iOS development

Now the time for iOS has come and I'm putting together these notes basing on my recent experience with XCode 6 Beta, Beta 6 and Beta 7.

On to the great sides:

  • XCode 6. Great Interface Builder experience so far. Although it's terribly slow at times, it's been very handy. Live rendering of custom views is both super useful and confusing at times. I don't know what hardware do they use for demos at WWDC, but on my 2010 Macbook Pro it's not even close to real-time (takes 20-30 seconds to build and update the view). Still cool and faster than rebuilding the project for that little tweak.

  • Swift is brilliant. I thought moving from ObjC will bring pain and even wanted to postpone until I'm more or less at home with it. Glad that I've done otherwise with the latest project. Aside of a couple little hacks, experience has been smooth and pleasant so far.

  • UI preview feature. Handy to check the constraints and have a rough idea of what the view will look on various devices, but with its weak sides -- not totally correct rendering of table view cells, latency and some crazy behavior with messed up constraints that doesn't match to that of a real device.

Now to what's not so great:

  • Installation issues. First launch after the fresh install is confusing. Filed the issue.

  • Docs can be better. Although the built-in doc browser has been seriously improved, guides are still in read it all in one book format, and are very hard to navigate and fish out necessary info (unless you are reading it from cover to cover).

  • Unwind segues not recognized when working with Swift and need ugly ObjC workaround.

  • Swift rules change often (especially for unboxing). I moved during the week from Beta to Beta 6 to Beta 7, and with each move there was a bunch of warnings about unboxing rules. Glad it evolves, but man, it's annoying at times (especially when you have to update template code that was generated for you and you don't have a clue what it should be without digging in).

  • Interfacing from Swift with some legacy libs can be an adventure. You'll need bridging header files and hand-keeping them in sync when you update libs.

  • Debugger doesn't show any vars and fields when working with Swift. Error messages are hilarious and don't help at all. One day it says something about some Metal library it couldn't compile, the other -- about an unknown variable that is actually defined two lines above the breakpoint. All in all, you can't debug Swift code reliably and have to get back to Stone Age NSLog techniques and good deal of guesswork.

What are we still missing:

  • Automatic laying out screen space on soft keyboard appearance / disappearance. It's 2014, but we still need to handle this manually. Android has the feature either to pan the screen or to resize it, but Apple figured that's something we can do ourselves. In every project, on every screen with fields.

  • Fitting of content within multiline labels doesn't work. Single-line UILabel views are capable of shrinking text to fit text fragments that are too long for the allotted screen space. When you go multi-line, you lose that. Oh boy, there's nothing terribly complex with that, and why is it not there yet. (I hear it is in iOS 8. Backport would be nice, guys.)

Bottom line is that toolkit has gone long way to become really helpful and usable. There are still lots of weak spots and at times experience is frustrating (rebuilding complete project for 2 minutes after a one line change is one of them), but at least designing and building stuff for iOS and Mac OS X is now a pleasurable experience.

If you are still using Appcelerator Titanium, consider going native with your next project. Both Android and iOS platforms give all you need to work on your projects efficiently now.

We are living through exciting times, ladies and gents!

What I like about present Android development

Android Studio. Number one most important thing in whole Android development story is Android Studio built on top of IntelliJ IDEA engine. Excellent off the box Gradle support, intuitive source control, intelligent code completion, responsiveness of the UI, debugging -- all top notch. They even help you complete repetitive strings in JavaDoc.

Responsiveness. Very simple and super-flexible means to layout interfaces for any platform, and means to pick the right resources (strings, values, drawables etc) for each. If I am to give just one suggestion here, it will be -- forget the visual UI designer. Give manual XML editing a try. It is very easy and pays of million times with precision and clarity in interfaces.

Building blocks. Extensive collection of modules and classes makes development a real pleasure. Take a look at AsyncTask and IntentService. These are the real pleasure to work with. Database management tooks, and speech recognition / synthesis are super handy too.

Documentation. Detailed tutorials and reference pages. In this aspect Google is light years ahead of Apple. The way Android Tutorials are designed and written is absolutely fantastic. You get just the necessary zoom level into the technology. If you need more, you drill down, if you need less -- you get up the hierarchy. In contrast, Apple has traditional all-in-one-book PDF-like papers, which makes it a complete waste of time if you need just a tad for solving the present task.

Fragments. With the introduction of fragments the development for Android went to a totally new level. They are those "sub-activities" you can embed in your views, replace, and transit to and from. You can juggle with them and use to compose interfaces for different platforms and form factors (think master-detail laid out sequentially for handhelds and as a sidebar+main area for the tablets.)

There's plenty more. I've highlighted something that stuck in memory after the recent week-long project.

Constraints aware soft keyboard compensation on iOS

A while ago Apple introduced constraints-based layouts for Mac OS X and iOS user interface designs. It's a huge and logical step forward from pixel-perfect fully manual laying out of things. The line of devices grows and creating all possible variations of interfaces has become a seriously tedious task.

One piece still missing from the puzzle is the set of tools for proper compensation for the soft keyboard. (It's when the keyboard slides up from the bottom of the screen.) There's a good chance that on a non-scrollable page your text fields at the bottom of it will become hidden by the keyboard. At this point you are on your own.

Recently, I've got exactly same problem and fished for solutions. All I could find were several pieces of code with frame-based calculations. That's when you figure the height of the keyboard that's about to slide out and animate the portion of the screen up by the same amount. It all looks like this:

- (void)registerForKeyboardNotifications {
        [[NSNotificationCenter defaultCenter] addObserver:self
            selector:@selector(keyboardWillShow:)
            name:UIKeyboardWillShowNotification object:nil];
    
       [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(keyboardWillHide:)
             name:UIKeyboardWillHideNotification object:nil];
    }
    
    - (void)keyboardWillShow:(NSNotification*)aNotification {
        NSDictionary* info = [aNotification userInfo];
        CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
        CGRect aRect = self.view.frame;
        aRect.size.height -= kbSize.height;
    
        [UIView animateWithDuration: 0.3 animations: ^{
            self.view.frame = aRect
        })
    }
    
    - (void)keyboardWillHide:(NSNotification*)aNotification {
        NSDictionary* info = [aNotification userInfo];
        CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
        CGRect aRect = self.view.frame;
        aRect.size.height += kbSize.height;
    
        [UIView animateWithDuration: 0.3 animations: ^{
            self.view.frame = aRect
        })
    }
    

This approach work great until there's a view you want to also resize or move depending on what you do to your primary target. Say, you have a table above your data entry section that you want to shrink as the data entry section goes up when the keyboard slides out.

idea

And that's just the simplest example, believe me...

With modern constraints based layout, you tell the table view bottom edge to follow the entry section top edge and the entry section bottom edge to stick to the bottom of the container. Now to modify the frame-based functions we need to save the bottom edge constraint of the entry section into the outlet (here inputsSpaceConstraint). And then we can safely modify the keyboardWillShow and keyboardWillHide methods as follows.

- (void)keyboardWillShow:(NSNotification*)aNotification {
        NSDictionary* info = [aNotification userInfo];
        CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
        [self.view layoutIfNeeded];
        [UIView animateWithDuration: 0.3 animations: ^{
            self.inputsSpaceConstraint.constant = -kbSize.height;
            [self.view layoutIfNeeded];
        })
    }
    
    - (void)keyboardWillHide:(NSNotification*)aNotification {
        [self.view layoutIfNeeded];
        [UIView animateWithDuration: 0.3 animations: ^{
            self.inputsSpaceConstraint.constant = 0;
            [self.view layoutIfNeeded];
        })
    }
    

The idea here is to use the constant field of the constraint to shift the view by the given amount (the height of the keyboard), and then, when we hide the keyboard, return it back to zero (or whatever you are using).

One specific thing to notice is that you absolutely need to call layoutIfNeeded before and in the process of animation to see the animation itself, not just before-and-after. That's how Apple suggests it to be.