Details : Path’s “+” Button

I felt like one of the last people to finally download the new Path app for iOS the other day. It seemed like everyone on my twitter feed thought this app was the coolest thing ever, but the “It’s like a social network!” aspect of it turned me off. I don’t need another social network in my life. But… since everyone praised the UI, it was time to give it a try.

I still don’t know if/how this app would fit into my regular usage, but I won’t talk about that today. The app is an absolute joy to use, and I think this is entirely because of its UI, and the hard work that went into all of the details. The screenshots don’t do it justice, because it’s the interactions and animations that make it shine. I’d only give it a “above average” as far as pure looks go.

A lot of these UI ideas are going to get copied into apps, just like pull-to-refresh did. One of them was redone using CSS. The + button, this is one of my favorite parts of the app. I could play with that button all day long, but the CSS version brings no such joy.

I want to make something perfectly clear before I go on… I think what Victor Coulon has done is really cool, and I applaud him for taking the time to try to replicate this control using CSS. I think it’s using CSS beautifully to do what CSS should do: presentation of elements. So no… this isn’t a rant about how this doesn’t belong in CSS.

The CSS version doesn’t feel as fun as the original because it tries to be just like the original and fails at it. It’s in uncanny valley. 

So let’s go through where they differ.

Event Triggering

Path’s + button actually gets triggered on touch-down as opposed to touch-up-inside. This is a departure for from most buttons. At first I wasn’t sure I liked this, but I’ve decided that in this case it’s completely appropriate. What’s the harm in accidentally triggering this button? None. It doesn’t bring you to another screen, it’s never destructive, it doesn’t put you in another mode. Accidental tapping in this case can very easily be undone by either re-tapping or tapping anywhere else on the screen (so actually it is modal, I lied).  Having it occur on touch-down makes it feel faster than most buttons. It’s false, but that doesn’t matter. 

Victor’s CSS version gets triggered on click, which is equivalent to touch-up-inside.

Animation Timing

UI animations need to be fast, otherwise they get tiresome after a while because the user can work tap faster than the UI can construct itself.  The animation duration of the CSS version is way off. On the open animation, I’d guess it’s about 25% slow. On the close animation I’d guess about 50% too slow.

This accentuates the fact that the close animation isn’t quite like the original in how the rotation of the x back into + is timed against the rolling back in of the other elements. They feel completely separate on the CSS version, but in Path they feel related, if not synchronized.

Key Frame Animations

There are two major components to this key frame animation: Spinning, and Movement (translation). 

In the original, you can clearly see that the elements spin upon opening. This might be the case the CSS version, but it ends up looking like a blur because the movement part is inaccurate. What’s clearer is that there’s excessive spinning upon closing in the CSS version.  I think the original does either 1, 1.25, or 1.5 revolutions upon closing (likely the same upon opening, but it’s harder to see). The CSS version looks like it’s doing over 2 revolutions. I’ll note though, that being starts, it’s particularly hard to track that.

The movement feels very iOS-ish in Path.  It’s similar to elastic scrolling. When the elements roll out they bounce into place.  The CSS version has the elements smashing against a wall.  Upon closing, the CSS version seems to exaggerate the pull back before they return. The distance that they pull back seems about right, but the proportion of time during the pull back versus the return is off which makes it feel more like a slingshot than the original.

Details Details Details

I think this is a great example of why details matter. This is essentially the same control, as far as functionality goes, but one feels like a treat and the other feels like a gimmick. 

I don’t mean to pick on Mr Coulon’s work. I’m sure if he spent more time on it, he could get it looking (err… WORKING) almost exactly like the original. But that’s my point : details take time. Details are exhausting, and require a ton of trial and error. I spent at least 5 minutes last night just tapping Path’s button again and again looking at the animation to try to figure it out exactly, to figure out why it felt so right. Recreating it would take me much longer than those 5 minutes. This exhausting trial and error is how you can manage to create things that not only look good, but feel fantastic.

Sidekick! Finally!

It’s been two years since I started the big “reworking” of core parts of NetworkLocation. It was my first real Cocoa app. The codebase grew and morphed as I learned the do’s and dont’s of how to write a proper Mac app. Some of the code was still present from v1.0, which could either mean it was doing a really good job or that we had painted ourselves into a corner where replacing it would be painful. There was some of both.

AutoLocate, the little engine we have that figures out where you are, had grown in a way that was really starting to limit us. It had “that one nasty method” that did most of the hard work. It was much too long, and contained approximately zero comments. Modifying it had become something I really dreaded doing because it required thinking of every different permutation that we had designed it to handle, and how that scenario would play down the gauntlet. 

The general UI of the app was still very much so inspired by v1.0. We freshened it up with every release, but it was never redesigned. 

We decided that it was time to start rethinking things, and that it was ok if this release would take longer than our previous releases to build. At the time, we thought… no real new features, we’ll name this NetworkLocation 3.5.

Chris started plugging away at a redesigned AutoLocate engine that was much better designed, and still every bit as powerful (if not moreso).

Phil and I spent countless hours mocking up different versions of the configuration/preferences screen. How do we make all of this stuff simple to use? The biggest disconnect we dealt with was the relationship between AutoLocate rules and Locations. It was a one-to-many relationship, and each AL rule would contain a set of conditions for each type. Combine that with the fact that it wasn’t an ALL or ANY used to evaluate the rule, instead it was “the magic of ALv1 will figure it out”… and you had yourself something that was technically very cool, but in reality, a real pain to explain how to use. As the person who had to attempt to train support staff in how to figure out what was going on with users’ systems, let me tell you… this was insane.

So the challenge… to make something that’s as powerful as we had, but simple to explain. After many attempts we ended up landing on what we have now in Sidekick which is a simple ANY per type, and an ALL across the types. If I configure “at 700 Corydon Ave” and “connected to 22inch Samsung”, then both have to be true for it to work. If multiple devices are listed, then I need to be at that location, and connected to any of those devices. Much. Much. Simpler. Of course, there’s weighting thrown into the mix to find the best match if there’s a tie.

We realized that CoreLocation was by far the most user friendly way of identifying where a Mac was. Having a user interact with CoreLocation though… what with it being based on longitude/latitude… ouch painful. Obviously a map can help here, but interacting with a map in a Mac app is no trivial task. After a bit (ok a lot) of prodding by Phil, I wrote a little prototype to see how difficult it’d be to interact with a live Google Map. Turns out.. it wasn’t that bad. Definitely feasible, but their API is pretty painful to work with. I was stuck writing a bunch of wrappers around it to make it less painful anyways, so that’s when I started MapKit for Mac.

Fast forward another few months when we realize that “NetworkLocation” really doesn’t fit with what the product has become. NetworkLocation started life out as a faster way to switch OS X’s Network Location (the ones you configure under the Network tab of System Preferences). Now very few people actually use NL to do that at all. It’s a tool to configure your Mac based on where you are. One of those things might be to change network settings, but calling it that is about as appropriate as it would be to call it PrinterLocation because it can set your default printer. We had to find a new name: Sidekick. You’re the super hero, it’s that little dude that hangs out and does those annoying little tasks for you.

By now we’ve gutted the UI, rebuilt the AutoLocate engine, added actions, recoded a bunch of the actions with cleaner code, recoded a ton of old code that I decided was too old/ugly to keep around, and changed the entire branding of the app. It wasn’t a hard decision to stop calling it 3.5 and give it a 4.0 version number.

Of course, there were other setbacks, like the fact that we also decided to rename the company at the same time, switched store providers, and wrote a rather sizeable app that sucked up all of my free time.

We worked really hard on this, but I don’t think the end result would have been in the same league had it not been for a bunch of people who helped us out. Ash Ponders and his team at AptFolk have helped us in a big way by providing end user support. I used to do it all, and it left me with no time to do coding. They also wrote the user guide, and did an awesome job of it. Leanne Havelock from FourLetterWord did all of our copywriting for the new site, press release, and a bunch of other stuff (some of which isn’t out yet). Miles Ponson did our app icon and status menu icon, both of which I think he nailed. Our relentless beta testers (too many to name) who sent me many a crash log to tend to. And of course, Tiff who’s been kind enough to let me have an affair with Xcode.

The end result is something I’m really proud of. Sidekick 4.0 works in both Snow Leopard and Lion. We decided to make it a free upgrade for NL3 owners because we believe it’s substantially better and want all of our users to upgrade as soon as possible, so we removed as many barriers as possible. It should be able to import all of your NL3 actions and locations, and even automatically upgrade your NLPlugins to SKPlugins if you have any installed.

Sidekick is the new NetworkLocation.

Wanted: Ottawa Apartment

As some of you may know (and some of you may not) I am leaving Winnipeg and heading to Ottawa in a month. My girlfriend got into grad school there, so we’re heading there at the end of August. 

Why does this matter? Why am I blogging about this? 

This is a plea for a very huge favour from those of you from the Ottawa area. You see, we have been searching for apartments for over a month now, and either have been disappointed with our findings, or have not been on time with our application (since we can never be there in person after a viewing). But we have been limited to internet finds - all of those niche places who have no need to post online have been under our radar. I can only assume that Ottawa is like Winnipeg where the good finds just don’t make it online. We’re starting to run out of time, and don’t especially feel like living in the ghetto.

So, if you can help us find something, we will be forever grateful and shower you with gifts (or just take you out to dinner, whichever). 

Here is a list of what we are looking for:

Location: Central. Tiff is going to Carleton U, so Glebe and north into Central Ottawa is prime and we want it! Basically the area between Bronson and Elgin from the north to the river. She’s planning on busing it to school every day.

2 bedrooms (or one bedroom with den) Parking (1 spot, small car) and Laundry on site, Decent Kitchen, decent square footage (nothing under 700). We’d love a balcony or patio, or some sort of back porch/yard on which to drink/enjoy beer, but we can definitely do without it.

Price? Technically we are looking in the 1200-1400 range, but we can go as high as 1550 with utils and parking included. 

If you can help us out we promise to return the favour. (Did I mention that Tiff makes fresh bread?)

Email me at rick@centrix.ca if you’ve got anything.

Say hello to NetworkLocation v4.

Say hello to NetworkLocation v4.

WWDC Prep-time

I was reading Twitter last night and saw someone mention “Monday’s keynote,” and thought.. “I think you mean… a week from Monday.”  Then I realized, holy crap, nope, it’s THIS Monday. WWDC 2011 is just around the corner.

This will be an interesting one, I’m sure. Besides the normal “The sessions will be awesome, the beer will be fantastic, and the people will be vulgar” I mean.  

This year will be neat for a whole other set of reasons. For one, it’s been like a month since we’ve last seen @cvee, which is the longest we’ve gone without seeing him since we hired the bird-like-individual a few years ago. I look forward to hanging out with him again. There’s no big-bird like a drunk big-bird.

Dave (he uhh… doesn’t do twitter), our new guy, or “Rainman #2” I like to call him (@cvee was Rainman #1) will be joining us for the first time. That’ll be interesting, cause well, he doesn’t drink (guess he didn’t get the memo about what the D stood for). But seriously, I’m looking forward to seeing what he thinks of the conference.  Dave quite literally taught me how to code, forever ago. He somehow had the patience to teach highschool-me how to code in assembly.  It’s taken me years to convince him to learn Cocoa and tackle consumer software with me.

@chrisfarber will be there. Jesus. Still doubting this one will actually happen. I’ve known @chrisfarber for almost as long as I’ve known Dave, the big difference here is that we’ve been running a business together, and I’ve never met him. When we started NetworkLocation, I think he was still in high school.  He’s been in university for what feels like forever. He’ll be bunking with Phil and I, so I fully expect him to leave mentally (and potentially physically/emotionally) scared. He might actually be 21 now, and so there will need to be some type of company initiation thing going on involving medically-unrecommended amounts of beer. Oh… and at one point I have to “accidentally” introduce him to @rudyrichter.

We’ll be sportin some new advert-tees that we just got in this week for Oomph, and Sidekick. Come find us and say hello, we don’t bite. We might say awful awful things, but I promise we don’t physically bite.

Breadcrumb in action

We recently made Breadcrumb available for private beta over at freshcode. I’ve since put it into NetworkLocation so that we can use it, and see how it works in the real world. When we designed Breadcrumb, we wanted it to work with any store so we tried to make it as generic as possible and not require any server software.

NetworkLocation is sold via a slightly customized PotionStore install over at https://secure.centrix.ca. Anyone who knows me knows how much I hate Ruby on Rails, and so I did zero modifications to PotionStore to support Breadcrumb. Someone with less hatred for RoR could likely add the Breadcrumb data to the model in no time, but for my needs, it wasn’t necessary.

Upon first launch of NetworkLocation 3.1.4 and higher, Breadcrumb runs and tries its best to determine where the user got NL, and stores that info in NSUserDefaults. When a user clicks “Buy Now” in NetworkLocation, the app requests that info from Breadcrumb, and I attach it to the URL that’s sent to the browser. So what used to be “https://secure.centrix.ca/?product=nl3” is now “https://secure.centrix.ca/?product=nl3&source=Bodega” where “Bodega” is where Breadcrumb has determined NetworkLocation was downloaded from. 

To see results from this, I used Splunk. I’ve raved about Splunk before; if analytics are your thing, you owe it to yourself to try it out. 

I used the advanced charting functionality to have it draw me a pie chart of how users are getting to my store. I used this query:

source=”/home/centrix/access-logs/secure.centrix.ca-ssl_log” AND (“GET / ” OR “GET /?” OR “product=nl3&source”)| replace “__utma*” with “From NetworkLocationApp.com” in uri_query  | chart count by uri_query

Basically it looks for the different ways a user could be getting to our store. In the case of when a user goes to our store from the main product page, Google Analytics goes and makes the query string nasty with “__utma….” so when it sees that it just replaces that with a prettier string. Then it does a summation grouped by uri_query, and makes a chart.

So a quarter of people who go to our store get there from our product page. About 8% or so got there with just “product=nl3”, so no real useful information attached. 24% came in with source=(null) which means that Breadcrumb wasn’t able to find any download information. Notice that this is different than source=Unknown. In the case of source=Unknown, Breadcrumb found a matching download, but it wasn’t from a source it could recognize. Maybe they got it from an email from a friend or something like that. Bodega only recently started writing the necessary information for Breadcrumb to be able to track its downloads, so it’s quite possible, if not likely, that a good number of those source=(null) people got it from Bodega.

I added two additional sources to Breadcrumb ontop of the ones it supports natively: NetworkLocationApp, and Centrix. NetworkLocationApp is the source when a user has downloaded NL from http://NetworkLocationApp.com, and Centrix is the source when a user downloads it from http://centrix.ca/networklocation/. Considering we’ve stopped linking to centrix.ca/networklocation many years ago, I find it very surprising to see how many downloads are still coming out of it.

Another interesting point here is that MacUpdate didn’t make an appearance. Version 3.1.4 got 153 downloads, but not a single one of those downloads generated a hit on the store. 153 isn’t a large number, so I won’t make any wild assumptions based on it, but it’s still interesting.

That’s how you see how users got to the store which is interesting. But what’s more interesting is… who’s making me money?  I’ve got a query for that too:

file !=”*printer.png*” AND (uri_query = “*product=*” OR “/store/order/thankyou”)  | transaction clientip, useragent maxspan=1h |  where eventcount > 1 AND like(_raw, “%product=%thankyou%”) | chart count by uri_query

I won’t be posting a graphic of that result cause that’s a little personal. Some interesting information that comes out of it though: source=Bodega made up a very small slice of hits to the store, it made up a larger slice of final sales. So even though my sales reports in Bodega make it look rather grim, it’s apparent that being on there and having a promo spot is generating me money.

Doing this without Splunk

Splunk’s my tool of choice, but not everyone can go ahead and install something like that on their server and feed it their access logs.

You could probably get the same information out of Google Analytics if you knew how to use it properly. I’m not saying it’d be easy to get, but somehow, probably possible.  

Some stores already have support for tagging. FastSpring is a good example. Create your tags, then just send up &source=Tag with the url. They’ll store that information for you and it should be available in reports.

As I said earlier, modifying PotionStore to support this natively likely wouldn’t take much work if you’re a RoR programmer.

Conclusion
I’m a stats fiend.  Any time I can get more data to let me make objective decisions to help me run my business better, I jump on it. Considering how ridiculously easy it was to add Breadcrumb support to NetworkLocation, I’ll definitely be adding it to all of my future apps.

Expectation Management

I work with a bunch of different teams… and I think my #1 issue I run into with people is the concept of expectation management. There are a few different definitions of what expectation management is, but here’s what it boils down to for me: Knowing what’s expected of you (deliverables), when it’s due, who needs it, how it affects them, and managing those relationships.

I’m a big fan of people setting their own deadlines. I always feel more comfortable when I can choose my own (I can add a bit of padding if I feel the need), and so I try to extend this to others whenever possible. I also believe that when people set their own deadlines they’re more likely to feel pressure to meet them. When I’m given an unreasonable deadline, the amount of effort I put into reaching it is usually pretty low.  If I set myself an aggressive deadline, I’ll work like hell to make sure I hit it.

The deliverables of a project/task are naturally quite important. I find that a lot of people don’t ask enough questions to really understand what the other one wants.  ”Well they told me what they want..,” you think. No! They told you what they thought they wanted. Pry deeper, make sure you know with confidence what they’re expecting at the end of this.

The what, and by when, are important. But what’s an order of magnitude more important is managing the relationship.

Are you ahead of schedule? Confident that you’ll get done sooner? Tell the person! You’ll make their day, and they might be able to change other delivery dates in response and get their bigger project done faster because you kicked ass.

On schedule? Tell the person! It’ll let the person know that the due date is still tenable and that they shouldn’t expect delays. They can adjust other tasks accordingly.

Feeling iffy about that deadline? Tell the person! Even if you’re not sure that you won’t hit it, a simple “Hey, things are taking a little longer than I expected because writing an html rendering engine in assembly turned out to be a little tougher than I anticipated.” Now they know that if they have some critical task they wanted to start up right afterwards, they might want to consider delaying it. They might say that only part of what you’re doing is a bottleneck, so maybe if you can focus on getting that part done in time, everything can go ahead as planned. If you bust your ass to hit your deadline, they’ll appreciate that you made up time; again you end up looking awesome, and all you had to do was hit your own deadline.  

Blowing the deadline? Tell the person! You should really have told them at the “feeling iffy” stage, but hey, shit happens. Telling them before the deadline lets them readjust.  

Seeing a pattern here? Communication. When I get nothing from a team member, I want to believe that everything is going peachy. When I see a deadline go by and I’ve not heard from the person and I don’t have a deliverable in my hand? It makes me think that they don’t value my time. At this point, I don’t care that an html renderer is hard to write in assembly. You’ve failed.

The frequency of updates depends on the project and its length. Use your best judgement for what’s an acceptable frequency. I like to go with weekly updates on a large project, personally. Usually in the form of an email that says what I’ve been up to, and where I’m heading next in the short term.

Nearly everything we do is a small part of a bigger goal. The cascading effect of a task getting derailed can sometimes have disastrous results, let alone when it gets compounded. Missing a deadline isn’t the end of the world if you’ve managed the other side’s expectations.  

FogBugz counts in Geckoboard

I’ve been playing with Geckoboard the last few nights since we’ve been wanting to build something like that at freshcode.  Geckoboard is really neat.  The problem is that apparently we’ve bet on all of the wrong horses as far as things it connects to.  And so I’m having to build the glue code that can talk to the services we use, and provide a data feed that Geckoboard can read.

Tonight’s task was getting it to connect to FogBugz and get me a count of the number of tickets still open for our upcoming v1.4.0 release of Bodega.  You can see the resulting code here:

https://gist.github.com/819975

All you have to do is change the couple variables at the top.  Put your search string into $query and it’ll spit out XML that you can put into a Geckoboard custom widget.