Saturday, December 25, 2010

Off the Grid - Food, Diving, Love and Cholera in Paradise

Bula! We recently took a trip to the Fiji Islands, and I took this opportunity to go completely off the grid - no cellphone, no computer, no TV, no newspapers. I was afraid I wouldn't last the week and succumb to withdrawal symptoms, but I decided I would give it my best shot. Not happy with just being off the grid, I decided to bring a book with me. If I read it, it would be the first book I would read in almost 4 years. I had heard great things about Love in the Time of Cholera and it was a genre of books I hadn't ever attempted to read before, so I figured it would be ideal. As luck would have it, the King Country public library in Bellevue (a most excellent library, by the way) had the book available to check out.

Well, turns out that the world does continue to turn even if I don't read the news, write some code, catch up on blogs and scan Facebook.

It was a great week full of excellent scuba diving, phenomenal tropical foods and magnificent living in resorts bathed in luxury and relaxation. We spent a few days at Outrigger on the Lagoon near Sigatoka on the main island, Viti Levu. It was larger than I would've preferred, but our plan here was to primarily relax and explore some of the surrounding villages/towns and interact with local Fijians and experience Fijian culture. We did 2 dives at Beqa Lagoon, but saved the majority of our diving for the latter half of our trip, which was near the more renowned dive sites. Instead, we spent a day relaxing at the resort and enjoying a local bar and another exploring some not-so-touristed surrounding. We got invited to dinner at a local family's house (our taxi driver, who happened to be a 4th generation Fijian Indian), and it was a humbling experience. Our hosts were a warm-hearted family who lived in a modest home in a tiny village who served us lovingly prepared Fijian dishes. The fish lovo, made with a snapper that was caught the previous night, and vegetable curry made with Taro root and star fruit, were delicious.

At the end of this part of the trip, I was done reading about a third of my book, and I hadn't felt the urge to get wired in at all, although internet access was readily available. I read by the pool, on the beach and in the lounge - the book was more interesting than I had imagined, and written in a style that was very refreshing.

Our next stop was the Namale Resort & Spa, home to the Sexiest Villas in the South Pacific. I knew the resort was good and I had lofty expectations for the price I had paid, but it exceeded my every expectation. From the warm welcome to the teary send-off, I could not have imagined a better way celebrate love. We spent the mornings diving, after having devoured a scrumptious breakfast. We were lucky enough to dive Chimneys and Grand Central at the Namena Barrier Reef on our last day of diving. I cannot imagine a better dive site than Chimneys anywhere in the world. It was magical - I think I could dive Chimneys every day for a year and still be amazed after every dive. After diving, we would partake of the phenomenal 3-course lunch served at a location of our choice (beach, deck, bure... anywhere we felt like eating) and spend the afternoon lazing on the beach, in the spa or indulging in more water activites (snorkel, water ski, etc). After that, some more R&R and drinks to whet the appetite for the gastronomical extravaganza that was dinner, accompanied by island cocktails or wine pairings and the Kava Boys. Every day, we were made to feel special with several personal touches by the staff at the resort - they really treated us like family. If there is paradise, Namale was pretty darn close to what I'd like it to be.

My reading progressed surprisingly well, and I found myself carrying my book to the beach, to the spa and to the outdoor jacuzzi. Save for about a 100 pages in the middle, I was enthralled by the story and the writing. I completed the book at the airport lounge prior to departure, and it had lived up to it's hype. It was Gabriel Marquez' ode to the myriad forms of love - spurned love, unrequited love, illicit love, love at first sight, true love, love that grows and fades over time and love that cannot really be classified. I got lucky with this book - I couldn't have picked a more appropriate book for the occasion. The fact it was a literary masterpiece didn't hurt either.

Anyways, all good things end, and so did our week in paradise. I hope one day to return to this lap of luxury in the midst of the friendliest people in the world. Until then, it's back to regularly scheduled programming.

Tuesday, September 28, 2010

Fun facts about Pilots

On a recent flight, I sat alongside a pilot who was returning home to the Seattle area. A lot of interesting things came up in my chat with the pilot, stuff I didn't really know about:
  • Pilots fly free on all domestic routes, regardless of airlines. All airlines have an agreement to lets each others' pilots occupy empty seats.
  • If no seats are available, the travelling pilot can also occupy an extra seat in the cockpit that is usually empty.
  • Pilots retire at 65(!).
  • The pilot I spoke with had been flying for 30 years. His pay/benefits today are half what they were 20 years ago (adjusted for inflation).
  • Until ~10 years ago, most commercial pilots came from the military (air force/navy). Now, it's about 50% military and 50% from pilot schools.
  • For pilots, career all about seniority. If this guy (a captain currently) were to leave his airline and join another airline he would start as a flight engineer. That's one level below first officer, who is below captain. This is a result of unionization.
  • Flights longer than 8 hours require 3 pilots (1 captain and 2 first officers) to rotate flying duties. Flights longer than 12 hours require 4 pilots (1 captain and 3 first officers). They usually fly 3-4 hour shifts.
  • Pilots at traditional airlines (American, Continental) make more than pilots at startup airlines (Virgin America, Jet Blue).
  • Airlines like Horizon Air/American Eagle/etc serve as training grounds for pilots, who then move to the more lucrative airlines.
  • On average, pilots fly between 9 and 14 days a month.
  • Pilots train on simulators every 9 months, and typically work on hard scenarios like dual engine failures once every couple of years.
  • Boeing and MD planes have "steering wheel" type controls. Airbus has fly-by-wire joystick like controls.
  • A 737 needs about 6000 feet to land comfortably. Headwind makes a huge difference in amount of runway needed.
  • Airbus planes felt more "robust" to this pilot.
  • Bird hits are more common than passengers realize.
  • Tegucigalpa, Honduras, is the hardest airport that he's landed in. Overshooting the 5000 foot runway drops you off a cliff and on to a strip mall. There have been 2 crashes in the last few years, with one dead pilot.

Thursday, September 9, 2010

Google Instant's biggest win? Teaching users to search better

The biggest impact of Google Instant is that it teaches users how to search more effectively. Based on these dynamic results, users can tweak their query such that it helps them find the exact answer they were looking for, even if they started with a bad query. Teaching users how to improve their search query is huge. The immediate impact is that it improves users' perception of Google's search results. Same with speed - users will perceive Google as being much faster than anything else out there because of the instant results.

Perception is reality. Speed and relevance are a search engines bread & butter. I think Google just upped the ante by a lot.

Credit to Microsoft though - this probably wouldn't have happened if it weren't for Bing's recent gains. Proof that competition provides the best kick-in-the-pants needed to innovate.

Friday, September 3, 2010

Integrating Core Data into your Three20 App

The Three20 library for iPhone app development is pretty cool - it take care of a lot of the 'mundane' code that you might need to write to accomplish basic tasks. Apple really should include some of the functionality of Three20 in the iPhone SDK itself, specially the Network stack.

As I built a more advanced app using Three20, I realized that it would be great to be able to persist to a database to make loading quicker when state on the server is unchanged. I needed some pretty basic Core Data integration in my app, and it was a little annoying that Three20 didn't have a Core Data-backed implementation of TTModel built into it. It seems like a lot of people want this, but there isn't any good post about it I could find. Jeff Verkoey, who currently maintains Three20, started writing a Core Data based tutorial but didn't get around to actually doing much with it. And there are a few threads in the Three20 Google group but nothing too comprehensive.

Having built this for my app, I figured I would post some more concrete information here for others who might find it helpful. I really want to just contribute a clean cut of this code as a TTCoreDataModel with unit tests to the Three20 source, but I don't have the luxury to spend time on it currently.

So here's a quick step-by-step on how to modify your existing Three20 based app to add basic Core Data support. The specific example I use fetches data from a server using TTURLRequestModel and writes it out to SQLite using Core Data entities. When displaying data to the user, it fetches data from Core Data using a NSFetchedResultsController, which efficiently interacts with Core Data.

Step 1:
Add CoreData.framework to your list of dependencies. Then, add the Core Data stack. Usually, you would do this in the AppDelegate. Jeff's AppDelegate files have this implemented, if you want an example. In my case, I wanted different databases for different Models, so I had these in my TTModel implementations.

Step 2:
Create your model. Add a new file of type "Data Model" in XCode (available in the 'Resource' section). Make sure the .xcdatamodel file gets added to your Target so that it's compiled into your binary. Look here for information on using the modeling tool. Once you've created your model, import the code for the Entity objects into your project. Now you're ready to start using Core Data.

Step 3:
Update your TTURLRequestModel implementation. Let's assume you're simply downloading some JSON and want to write it out to Core Data as an Entity you created in Step 2. Update your (void)requestDidFinishLoad:(TTURLRequest*)request implementation of TTURLRequestDelegate as follows:




A few notes about the code above:
  • DBResource is my simple entity containing an "id" and a "name".
  • The NSAutoreleasePool helps clean up memory allocated in this tight loop here
  • -reseting the context makes sure that the data faults in as needed instead of staying in memory when you read it using the NSFetchedResultsController
Step 4:
In your TTTableViewDataSource implementation, create a NSFetchedResultsController and configure it to fetch your Entities as needed. Then, override the (id)tableView:(UITableView*)tableView objectForRowAtIndexPath:(NSIndexPath*)indexPath method to return a TTTableItem built using the Entity you fetched from Core Data




Now, your TTTableViewDataSource is configured to read results from the DB you created and display them in your view. Remember that tableViewDidLoadModel can be called multiple times, so clean up any previously allocated NSFetchedResultsController if you need to.

Step 5:
Implement your logic to delete/update your Core Data entities. Depending on how your app uses data persistence, you want to ensure that you update/delete your entities from Core Data. For instance, if you always want to fetch data from the server and load them into Core Data on first run, then update Step 3 to first delete all entities. If you wish to show local data when available but fetch when not, then update Step 3 to not hit the server before checking locally. When deleting entities, remember that Core Data only lets you delete a single entity at a time (so you must fetch all and loop through each to delete it). If you want DROP TABLE ... semantics you're going to need to maintain different NSManagedObjectContent and NSPersistentStoreCoordinator for each such table, and then simply delete the database file.

So, this isn't too hard really. Assuming you're familiar enough with building your Three20 app, my notes above should help you get started with integrating Core Data into your app. My app uses a TTSectionedDataSource, and with a few tweaks to NSFetchedResultsController it's simple to do that too.

Saturday, August 7, 2010

An Apple A Day Keeps the Bugs Away

I've been working from home for the last year or so, and it has tremendously increased my productivity as a developer. Besides the obvious reasons (lack of distractions, no meetings, etc) there is one other advantage that I haven't often heard people mention before.

I have been able to find solutions to some really hard technical problems much quicker, and I attribute it to the fact that I work from home instead of in a more office-like environment.

Most software engineers will tell you that some of their best ideas have come to them when doing the most mundane of things - taking a shower, strolling across lawns, eating a snack and so on. I don't know the psychology behind this, but I know it's true for me. Something about being alone and thinking about your problem subconsciously, and yet being physically distracted with an unrelated task seems to work wonders. I have solved more tough problems while in the shower, when sitting down to eat an apple, when watering the plants or simply doing the dishes than I have sitting at a desk and just thinking. So obviously, being at home makes it much easier for me to find one of these mundane tasks that let me 'detach' and yet perform an activity that is beneficial to me both personally and professionally.

It's become a pretty important weapon in my software development arsenal - if I'm stuck on something for more than 30 minutes, I get out of my workspace and go do something mundane around the house or eat a quick snack. More often than not a solution magically appears in my head.

Back when I worked in an office, if I was stuck trying to solve a hard problem my best option was to find someone familiar enough with the problem space and bounce ideas off of them. This worked pretty well initially but didn't scale too well with problems that became narrower and deeper in scope - I often found myself spending a lot of time trying to catch others up to where it would be valuable to bounce ideas off them. And heading off into the office kitchen to reorganize stuff or watering random plants around the office would just make me look weird.

So, the next time you're having a hard time finding a solution to a problem in the office think about taking the next day off and working from home. Oh, and make sure the dishes aren't done. ;)

Wednesday, June 2, 2010

Slow onclick Javascript handling in Google Chrome 5.0.375.55

Google's Chrome has been a pretty slick browser, and it's speed was the primary reason it became my default browser. The V8 Javascript engine supposedly blows other engines out of the water, and it did feel noticeably faster than others - until this latest version.

Sometime last week, my version of Chrome was auto updated to 5.0.375.55, and I noticed that the website I've been developing became terribly slow. Because of a previous Chrome bug, I hadn't migrated my site from jQuery 1.3 to 1.4, so one of the first things I did was verify the fix and then update my site to use the newer, faster jQuery. As I began testing, I noticed a significant reduction in performance of some features on my site.

Initially I suspected it had something to do with jQuery upgrade. After a few hours of changing things around, I was pretty convinced the problem wasn't jQuery 1.4. I noticed that Firefox and even IE 8 didn't exhibit this latency, so now I started to suspect the new version of Chrome. I hacked up a couple of test pages, and I think I found the culprit. It looks like the onclick handler's performance degrades with the size of the HTML page, resulting in a very noticeable (1-2 second) lag once you have a table with 3000/4000 rows. Firefox and IE 8 don't seem to have any trouble handling this.

I've logged Issues 45620 in the Chromium project with details and a repro to track this problem. This definitely isn't good news for me - with growing adoption of Chrome and general perception that Chrome is the fastest browser, problems like these make website look faulty even when they might not be.

As an aside, I have noticed that Chrome feels like it has regressions more often than other browsers (perhaps a function of shipping more often?) I've run into several issues developing for Chrome - the silver lining is that bug fixes are quicker than rival browsers, but better QA before release would certainly help developers.

Other Chromium issues that I've had trouble with and have had to work around are Issue 7771, Issue 30693, Issue 33411 and Issue 36559.

Update: An comment on Issue 45620 identifies one of the reasons for the slowdown and a workaround.

Wednesday, May 19, 2010

Showing the Cancel button in UIActionSheets on iPad

In it's iPad Human Interface Guidelines Apple provides several guidelines about how user experience should differ between iPhones and iPads. One such element that should behave differently is the UIActionSheet.

Apple recommends that cancel buttons not be shown for action sheets on iPad:
Because taps outside the popover dismiss the action sheet without selecting an item, this results in a default way to cancel the sheet. Including a cancel button would therefore only cause confusion.
But the guidelines do allow exceptions to this rule:
However, if you have an existing popover and are displaying an action sheet on top of other content using an animation, a cancel button is still appropriate.
So you would think following this guideline is as simple as not specifying a cancel button in most cases, and only specifying a cancel button when you have carefully considered the exception case, right? Wrong. Even when you do specify a cancel button, it looks like the iPad runtime automatically always hides it.

So how do you provide a cancel button in the exception case? You could just provide another normal button that has cancel behavior, but that's not ideal because you really want the difference in appearance that UIActionSheets on iPhone provide by default. After some tinkering, the best solution I could find was to set the actionSheetStyle property of the UIActionSheet to UIActionSheetStyleBlackOpaque.

I can speculate why this works, but I don't know for certain so I'd rather not. If you know of a better/more correct way to do this, please do leave a comment.

I am surprised that Apple was mindful enough to list the exception case, and yet careless enough to not provide documentation on how to handle this case - they even seem to have forgotten to document that the iPad automatically overrides user-provided cancel buttons by default.