I’m happy to announce my new side project: Tweets Lounge.

Tweets Lounge is a directory of local neighborhood businesses using Twitter. It’s like a Yelp directory for Twitter.

We’re initially supporting Seattle and the surrounding area, but we plan on expanding to other cities soon. Check out the site: http://tweetslounge.com and follow us at @TweetsLounge.

Apple released an iPhone app for this year’s WWDC. You can download the app from the WWDC attendee site.

The instructions are to download the zip file, drag the app and the provisioning profile to iTunes then sync your phone.

If you’d like to install the WWDC app on a device that isn’t setup for iTunes syncing, then you can use Xcode instead of iTunes. Here are the steps:

1. Download the zip file. Expand it and you’ll see two files: WWDC09.app and a .mobileprovision file.

2. Launch Xcode and open the Organizer (Window -> Organizer)

3. Select your iPhone/iPod Touch in the organizer window.

4. In the Provisoning section click the + under the list of provision files and select the WWDC app’s provision file you downloaded.

5. In the Applications section click the + under the list of Applications and select the WWDC09.app you downloaded.

When Xcode is done, the WWDC09 app will be installed on your device.

When you show web content in a UIWebView, links that open a popup do nothing.

This makes sense. Since UIWebView is an embedded control in your app, there is no logical default for popups.

So how will users view those pages? The solution is to convert all popups to normal links before displaying the web page:

-(void) webViewDidFinishLoad:(UIWebView *)wv
{
    [wv stringByEvaluatingJavaScriptFromString:@"\
    links = document.getElementsByTagName('a');\
    for (i=0; i<links.length; i++) {\
        links[i].target = '_self';\
    }"];
}

Update (7/21/09): TweetDeck finally speaks up about this issue, offers the same exact advice as below: http://tweetdecksupport.posterous.com/fixing-the-blank-tweetdeck-problem?

Yesterday I launched TweetDeck to get my Twitter fix and was surprised to see TweetDeck stuck with a blank empty screen:

TweetDeck stuck, launches blankSearching around I discovered that there are two places where TweetDeck stores it’s data:

Preferences Folder

  • This contains preferences like your columns, layout, etc.
  • To find this folder go to your Home directory -> Library folder -> Preferences folder then find the folder that starts with TweetDeck.
  • The actual path on my machine is:
    ~/Library/Preferences/TweetDeckFast.F9107117265DB7542C1A806C8DB837742CE14C21.1

Adobe AIR Encrypted Local Store (ELS)

  • This contains your login info to Twitter
  • To find this folder go to your Home directory -> Library folder -> Application Support folder -> Adobe folder -> AIR folder -> ELS folder then find the folder that starts with TweetDeck
  • The actual path on my machine is:
    ~/Library/Application Support/Adobe/AIR/ELS/TweetDeckFast.F9107117265DB7542C1A806C8DB837742CE14C21.1

I tried moving/deleting the Preferences folder, but that did nothing. I ended up deleting the Adobe AIR ELS folder and leaving the Preferences folder as is. TweetDeck asked me for my Twitter credentials again, but then everything was back to normal, including all of my preferences.

If you are on Windows you can find these folders here (courtesy Christopher Grant):

  • C:\Documents and Settings\[user]\Application Data\TweetDeckFast.[guid]
  • C:\Documents and Settings\[guid]\Application Data\Adobe\AIR\ELS\TweetDeckFast.[guid]

I’m used to taking screenshots using Xcode, part of Apple’s iPhone SDK.

So it was news to me that ever since the 2.0 firmware, you can take screenshots directly on your  iPhone:

Press and hold the Home button (round button on the front) then click the Sleep/Wake button (small button on top of the iPhone).

You’ll hear the camera sound and the screen will flash. Launch the Photos app and you’ll see your new screenshot in the Camera Roll.

A lot of iPhone applications need to convert a user’s location from the latitude/longitude that the iPhone SDK returns into a postal code. This is primarily because a lot of APIs still use postal codes to look up information. This is the case for Hot Popcorn where we look up movie times using either a postal code or city, state.

The popular choice for doing this conversion is GeoNames, a web service run by Marc Wick free of charge under a creative commons attribution license. When an API is free, there have to be limits to set to prevent abuse. GeoNames limits usage in it’s terms of service to 50,000 requests per IP address per day.

But this is a new world with rich clients like the iPhone, each with their own IP addreess. So what happens when a free popular iPhone game like iMob uses GeoNames? Mayhem.

iMob Online is currently #5 in the Top Free Apps on iTunes and this has essentially brought down GeoNames:

The problem since yesterday evening is an enormously popular iphone application called iMob. It is hammering the server with > 100 requests per second which has the effect of a DDOS attack. I am trying to get in touch with the developers to have them remove this feature from their application.

This forced Marc to take down the server:

We have temporarily removed the domain ws.geonames.org from the dns setting to protect the server from exessive use by an iphone application. You can access the server with the domain ws5.geonames.org

Now any iPhone application trying to use GeoNames will fail. Thanks iMob!

Hunting down crashes in an iPhone app often involves uncaught exceptions. Even if we’re running from the debugger at the time, the crash happens after the exception is thrown, so the only entries shown in the debugger’s stack are from the exception catching code.

We do get a printout of the stack at the time of the exception but it’s a numeric stack trace so we’ll need to use the atos command line utility to make sense of the numbers. Here’s an example of a crash in a development build of Tomatoes:

2008-08-01 21:10:20.138 Tomatoes[151:20b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFString replaceCharactersInRange:withString:]: Range or index out of bounds'
2008-08-01 21:10:20.213 Tomatoes[151:20b] Stack: (
808163835,
806099672,
807978623,
807978527,
812533155,
812164939,
812716685,
22067,
35325,
812317455,
812317325,
824032771,
824024301,
824022997,
807779567,
807777675,
829004012,
816177936,
816214500,
8381,
8244
)
terminate called after throwing an instance of 'NSException'
kill
quit

We’ll need to call atos for each stack entry and we’ll need to know the path to the app on disk: In Xcode right click on the app under Products and select ‘Reveal in Finder’, then right click on the app in the Finder and select ‘Show Package Contents’, find the executable and drag it to the Terminal window to get the path.

The entries in the stack that belong to our app will be have smaller values. In this case 22067 and 35325. So let’s see what we have:

atos -o /Users/pboctor/iphone/Tomatoes/build/Debug-iphonesimulator/Tomatoes.app/Tomatoes 22067
-[NSObject(MovieUtils) stripHTML:] (in Tomatoes) (MovieUtils.m:55)

atos -o /Users/pboctor/iphone/Tomatoes/build/Debug-iphonesimulator/Tomatoes.app/Tomatoes 35325
-[RottenCurrentMoviesTableController connectionDidFinishLoading:] (in Tomatoes) (RottenCurrentMoviesTableController.m:482)

It looks like after we’re done downloading some data, we call stripHTML and then crash while trying to replace some values in a string. Off to fix some code!

One final thing: If the numeric stack trace came from the app while running on the iPhone and not in the simulator, then we need to tell atos the proper architecture of the executable:

atos -o /Users/pboctor/iphone/Tomatoes/build/Debug-iphoneos/Tomatoes.app/Tomatoes -arch armv6 22067

Happy bug fixing.

To copy the list of installed printers from one machine to another, copy the following files:

/etc/cups/printers.conf
/etc/cups/printers.conf.O
/etc/cups/ppd/*
~/Library/Printers/*
~/Library/Preferences/com.apple.print*

Make sure the files in /etc/cups retain the proper owner and group (root:_lp) and permissions (‘u=rw,go=’ for the conf files and ‘u=rw,go=r’ for the ppd files).

You’ll also have to re-install any printer drivers that you installed on the old machine.

Restart after you’re done and System Preferences -> Print & Fax should now show your printer list.

After installing Leopard and restarting, Migration Assistant offers to transfer your data:
Leopare Migration Assistant

If I chose “from another volume on this Mac” (connected via USB) or “from another Mac” (connected via Firewire disk mode), I’d immediately get one of these errors:

  • “Mac OS X was not found on the volume you selected.”
  • “There are no version of Mac OS X on your old Mac.”
  • “You can only transfer information from a Mac that has OS X installed.”

The error comes right away, before I actually selected anything. The drive booted without any problems either.

So here is the very non-intuitve fix in case any one else runs into the same problem:

  • When the error comes up, just let it sit there for a while (a good 10 minutes in my case).
  • You’ll then start to notice that the information from your drive/machine starts showing up (Account info, size of the Applications folder, etc)
  • After all the information is populated, click Cancel in the dialog box which will take you back to the previous screen.
  • Select “from another volume on this Mac” or “from another Mac” again and this time there will be no error.

Inspired by DRY up your views (slides) at last week’s RailsConf, I’m trying to improve a view that contains far too much code.

The view needs to display a menu of tasks. Whether and how each task is displayed depends on a combination of the current user’s rights as well as what they had done previously.

With a big block of code for each task, the view file quickly became a mess:


<div>
  <ul class='menu'>
    <% if current_user.has_right_for?('foos') %>
      <li>
        <% if current_user.foo %>
          <%= link_to 'Stop Foo', foo_url, :method => :delete %>
        <% else %>
          <% form_tag(foos_url, :name=>'add_foo',
                      :style=>"margin:0;padding:0;display:inline;") do %>
            <%= hidden_field_tag :bar_id, @bar.id %>
            <%= link_to_function "Start Foo", "document.add_foo.submit()" %>
          <% end %>
        <% end %>
      </li>
  </ul>
</div>

OK, so let’s move each task into a helper:

<div>
  <ul class='menu'>
    <%= start_or_stop_foo -%>
  </ul>
</div>

def start_or_stop_foo(tag='li')
  if current_user.has_right_for?('foos')
    content_tag(tag, link_to('Stop Foo', foo_url, :method => :delete))
  else
    content_tag(tag) do
      form_tag(foos_url, :name=>'add_foo',
                  :style=>"margin:0;padding:0;display:inline;") do
        hidden_field_tag :bar_id, @bar.id
        link_to_function "Start Foo", "document.add_foo.submit()"
      end
    end
  end
end

This unfortunately doesn’t work. Both content_tag and form_tag assume that you’re calling them from an action view template. A fix has been applied to edge rails for content_tag, but we need one for form_tag:

From form_tag_helper.rb, replace:

concat(tag(:form, html_options, true) + method_tag, block.binding)
concat(content, block.binding)
concat("</form>", block.binding)

with:

form_tag = tag(:form, html_options, true) + method_tag
if block_is_within_action_view?(block)
  concat(form_tag, block.binding)
  concat(content, block.binding)
  concat("</form>", block.binding)
else
  form_tag + content + "</form>"
end

def block_is_within_action_view?(block) 
  eval("defined? _erbout", block.binding) 
end

I don’t like patching Rails because it’s fragile. I have to remember to remove or re-apply the patch the next time I upgrade my Rails installation.

Is there another way of moving this code out of the view and into a helper?