Sep 27, 2011

Unresolved Symbols CAKeyframeAnimation : iOS Animation

I was trying to do some stuff with CoreAnimation and ended up with this compile error:


Undefined symbols for architecture i386:
  "_OBJC_CLASS_$_CAKeyframeAnimation", referenced from:
      objc-class-ref in SpellPageViewController.o
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status

To fix it: Make sure you have linked the QuartzCore library.

Sep 2, 2011

UIActionSheet Cancel Button Not Working

I’m working on a app that has a tab bar with an “Add” button in the middle of that tab bar.

The first step when that “Add” button is hit is to display an UIActionSheet. I found, however, that they “Cancel” button on the action sheet was not clickable. After much confusion, this stack overflow post helped me.

How you display the UIActionSheet is very important. I had copied the Apple NavBar demo that uses it, which used the following code to launch it:


UIActionSheet *styleAlert = [[UIActionSheet alloc] initWithTitle:@"Choose a UIBarStyle:" delegate: self cancelButtonTitle:@"Cancel"
									   destructiveButtonTitle: nil
										otherButtonTitles: @"Default", @"BlackOpaque", @"BlackTranslucent", nil, nil];

[styleAlert showInView:self.view];
[styleAlert release];

The problem is that if you have a UITabBarController, it’s tab bar view takes precedence on the touch (even though the UIActionSheet appears on top), so the Cancel button isn’t actually clicked.

To fix it you need to launch the UIActionSheet differently:


UIActionSheet *styleAlert = [[UIActionSheet alloc] initWithTitle:@"Choose a UIBarStyle:" delegate: self cancelButtonTitle:@"Cancel"
									   destructiveButtonTitle: nil
										otherButtonTitles: @"Default", @"BlackOpaque", @"BlackTranslucent", nil, nil];

// The proper way to do it if there is a tab bar
[typeSelection showFromTabBar:self.tabBarController.tabBar];
[styleAlert release];
Aug 28, 2011

I’m Shopping – Don’t Kick Me Out of Your Store

You wouldn’t believe what happened to me the other. I was in the Apple store and I saw a trackpad and a mouse that I wanted to buy. I paid for the trackpad first which was very easy to do. But then, before I could buy the mouse one of the employees took me out to the street an began to unwrap my trackpad. I knew I wanted the mouse, so I had to go back in to the store, find the mouse all over again, and then buy it. I had thought of getting a new keyboard too, but decided it wasn’t worth the hassle of getting kicked out to the street again…

While this obviously didn’t happen at a real store – it happens everyday in the App Store. Especially on the iPhone! As soon you select an App you are taken to the home screen to see that it is being downloaded. It serves the purpose of informing people where there app is going, and that the purchase worked properly. A message within the App Store app itself would serve the same purpose and be far less intrusive.

One particularly painful point is when you download an app that you found after scrolling down a few pages in the search results. Searches in the App Store are reset to show only the first page (25 entries) when the App Store app exits.

There have times when i would have bought another app if i had been left in the store, but didn’t feel like putting in the effort to go back after being kicked out. That’s an extra 30c that Apple could have had from me but didn’t. Their loss…

Aug 12, 2011

Lite and Full Versions of Apps – There is a Better Way

Consumers obviously love free apps, and app developers obviously love to make money. To bridge the divide – there seem to be 2 common ways to offer a consumer a free trial of an app.

1. Offer a free ‘lite’ version and a paid for ‘full’ version.

2. Offer in app purchases (eg free app with basics, but can buy extra levels).

I cant find any real stats comparing the two ways, but there are obvious problems with both.

The ipad App store has (at the time of writing) about 109,000 apps in it. A search for “lite” apps showed 4400 results. That’s 4% of the apps!! (more if you consider that all those apps are essentially duplicated in the app store because they have ‘lite’ and ‘full’ versions.

So when browsing the app store – 8% of the apps you’ll see are duplicates with arbitrary separation between trial and full. Each of these apps appears in the app store twice (noise) with separate reviews (inconsistency).

For the in app purchase – the common metaphor is buying extra levels, widgets, or content. Technically it is a lot more work, which will prevent a lot of developers from doing it just to offer a trial and full version i the same app. Secondly – although I can’t find any info comparing this, my hunch from my own behavior is to avoid apps that offer in app purchases unless they are content based (eg buy a new book). I don’t like the idea of paying for new levels or add-ons.

Here is what I’d like to see: an Apple sanctioned way of offering in app upgrades. When a user searches for an app they see a single app in the app store. They can install it as either the lite or full version. Either way, the same app is installed, and the app itself has an API call to check what ‘level’ it is. Eg – [theApplication isServiceLevelLite]. There would also need to be an API to upgrade from within the app.

This type of solution has a lot of benefits.

- cleans up the mess of doubled up applications (again – 8% of the app store)

- consolidates incorrectly segregated reviews

- a consistent way of upgrading apps if they are upgradeable

- a less intimidating way (than in app purchases) to offer an upgraded version of an app

- a consistent trial/full paradigm

I think it’s a big win for both developers and consumers. The lite/full app versions is clearly a paradigm that has become commonplace – and I can’t wait until app decides to properly streamline it.

Update : Aparently 66% of in-app revenue is from ‘consumable’ goods. (At least for apps that are games).: http://techcrunch.com/2011/08/16/what-ios-android-gamers-actually-spend-money-on/

Jul 29, 2011

Consistent Naming Avoids Confusion

It is said that naming is one of the few hard things in programming.

It’s not really that hard, but it is VERY important.

As a general rule – the name should reflect a noun or verb in the real world, and should be used consistently, everywhere.

If you ever here someone say “it doesn’t matter what we call it now, we can always change it in the UI later” that should be a BIG red flag.

If you don’t understand something well enough to be able to refer to it by a single word – then you don’t understand it enough to implement it.

—-

On a related note, it really bothers me that Mac and iOS app naming is inconsistent. Eg iCal vs Calendar, or Address Book vs Contacts. Pick a name and stick with it please.

Jul 28, 2011

Dear Apple – nobody likes a spelling Know-it-All

Apple limits settings (and rightly so), but the iOS auto correct really needs some settings. Namely – the option to turn it off would be nice. It seems to be the most painful feature for most iOS users I know.

In my usage – a bad auto correct is far more common than a correct one.

If thy really dot want settings the default behavior should be opt-in to correct – and red underline on mis-spelled words. This would change the ios role from “know-it-all” to “helpful”

Jan 16, 2011

Rescue from 'execution expired' in Ruby

I recently wrote some scripts which were unexpectedly exiting with an ‘execution expired’ message.  A normal catch all exception doesn’t work, because Timeout::Error apparently derives from Interruption, and not StandardError (thanks lindsaar.net). So, I had to rescue it explicitly like so:


begin
  # do something involving Net:HTTP
rescue => e
  # This will NOT catch 'execution expired' !!
  puts "Rescue : #{e}"
rescue Timeout::Error => te
  # This explicit rescue will work
  puts "Rescued from timeout : #{te}"
end

It was driving me a little batty for a while, so I thought I’d share it.

Jan 1, 2011

Google Goes Evil – Blocks Google Apps Group Emails When Any Member is on Facebook

(jump to the fix)

I’m a GoogleApps user and have generally been happy. 2 days ago I stoped receiving some regular emails.  I quickly realized the missing emails were ones sent to groups in our Google Apps domain.  So – I sent an email to some of the group email addresses and got this response:


Delivery Status Notification (Failure)

550 550 5.7.1 Unauthenticated email is not accepted from this domain. g17si24246438ibb.67 (state 18).
Message-Id: <7a90316df68e3a4c1008a795a0c86fd8@www.facebook.com>

Of particular interest here is @www.facebook.com.  Nothing to do with the sender or receiving group is related to facebook in ANY way!

After reading some forum threads showing that many others have had similar problems, it soon became clear that the problem had to do with any group that I was a part of, because my email is registered with facebook.

Sure enough – if I remove my facebook registered email from the group then emails to the group are delivered.  If I add my facebook registered email back to the group, emails are blocked.  I’ve tried this with several groups.

This is probably related to Google’s ongoing spat with facebook about data ownership, but Google definitely sucks for doing this.  I’m not trying to export my data, and I haven’t made any changes to my setup, but now, all of a sudden, any emails that go to a group that has a facebook user as a member get blocked.  Gmail FAIL!


The Fix

Update your group members to include a ‘+g’ in the username. Eg:

remove geoff@domain.com and add geoff+g@domain.com

Gmail allows you to use a ‘+’ in emails that have no impact on the recipient, so both the emails above go to the same place. However, geoff+g isn’t registered with facebook, so group emails will get delivered.

Dec 22, 2010

Speeding up Slow Specs and Features on Ubuntu 10 with Postgres

I’ve recently been working on a Rails 2.3.10 project using Ubuntu 10 and Postgres 8.3 on my dev machine. The specs and features were running really slow (>30min) and I wanted to speed them up.

After some profiling and searching I found 2 quick fixes.

1. Tell postgres not to wait for confirmation that changes are committed to disk:

Add the following lines to your postgres configuration (/etc/postgresql/8.3/main/postgresql.conf) and then restart postgres. These have a very noticable affect on Ubuntu 10 using the ext4 filesystem. They tell postgres not to wait for confirmation from the filesystem that changes have been committed to disk, which results in table truncates/deletes being faster but also increases the chance of a corrupt db if your machine crashes. (that shouldn’t matter for dev/test)

  fsync = off
  synchronous_commit = off

2 – Use Ruby Enterprise Edition

If you haven’t seen the ‘Grease your Suite’ presention – you should watch it. If you are using rvm and can use REE on your project, you should do so. It is a lot faster (for my tests at least). It reduced my test time by 40%.

If you do use REE – add the following to your ~/.bashrc file

  export RUBY_HEAP_MIN_SLOTS=1000000
  export RUBY_HEAP_SLOTS_INCREMENT=1000000
  export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
  export RUBY_GC_MALLOC_LIMIT=1000000000
  export RUBY_HEAP_FREE_MIN=500000

These exports optimize memory allocation and garbage collection for REE.

After these 2 changes the entire test suite took 9min. That is still pretty long, but much better than >30min.

May 3, 2010

Cross domain workaround for @font-face and Firefox

All major browsers (even ie4+) now support the @font-face css property, which is great news for designers.  Unfortunately there are still some kinks:

  • Different browsers support different formats.  You can specify multiple formats within a css file, and as long as you can provide .eot, .ttf, and .svg you’ll be okay.
  • Firefox (which supports @font-face from v3.5) does not allow cross-domain fonts by default.  This means the font must be served up from the same domain (and sub-domain) unless you can add an “Access-Control-Allow-Origin” header to the font.
  • At the time of writing, you cannot set the Access-Control-Allow-Origin” header on S3

I wanted to use @font-face served up from cloudfront – so here is what I did:

  1. Go to fontsquirrel.com and download the font-face kit you want to use.
  2. Go to the fontsquirrel font-face generator and upload the .ttf file from the kit you just downloaded.
  3. Select the ‘Expert’ option.
  4. For format select ‘TrueType’, ‘EOT’, and ‘SVG’.  (Woff is a compressed format only supported by firefox, but firefox also support .ttf, so woff is extraneous)
  5. Under the CSS options select ‘Base64′ Encoding.
  6. Download and use the files provided.

What does this do?  It actually embeds the TTF font within the CSS file, so it can be served up directly from S3/cloudfront and still work on Firefox.  This solution works, but is sub-optimal.  In particular, it bloats your css for other browsers (like mobile safari and IE) that can’t use the TTF format. :-(