ARC weak references on iOS 4

I’m finally exploring ARC (Automatic Reference Counting) for development on iOS 4 and above.  Mike Ash provides a terrific introduction to ARC along with some pitfalls.

One of the pitfalls of using ARC is that weak references (aka zeroing weak references) are not supported on iOS 4 or on Snow Leopard.  You could use Mike’s MKZeroingWeakRef class with all weak references, but now there is a better option:  PLWeakCompatibility.  This package allows you to use all the goodness of ARC weak references with the native syntax.  Brilliant!

You can even get PLWeakCompatibility on GitHub so you can keep up to date on its progress.

References:

Mac Mail and ReplyWithHeaders

I’ve been using Microsoft Outlook 2011 on Mac OS X for a couple years and I finally decided I needed to give Mac Mail and iCal a try.  In Lion (Mac OS X 10.7), Mac Mail has added some great features, but one thing it is missing is including the headers of the original message when replying.  Here is an example of what Mac Mail shows:

On Jun 14, 2012, at 1:03 PM, Geoff Walker wrote:

Outlook shows this:

From: John Smith [mailto:jsmith@example.com] Sent: Thursday, June 14, 2012 12:41 AM
To: MyTeam
Subject: Yesterday’s meeting

I found an add-on that will modify Mac Mail to do the same thing.  Saptarshi Guha wrote ReplyWithHeaders, which is a bundle that can be added to Mac Mail to modify its behavior, and released the source on GitHub.  Here are the steps to use it if you have access to Xcode (the development tool for Mac OS X and iOS):

  1. Clone the ReplyWithHeaders repository by using the following command:
    git clone https://github.com/saptarshiguha/ReplyWithHeaders.git ReplyWithHeaders
  2. Quit out of the Mail app.
  3. Build the ReplyWithHeaders project in Xcode.  This will place the ReplyWithHeaders.mailbundle file in the right place, which is in ~/Library/Mail/Bundles directory.
  4. From a Terminal window, execute the following commands:
    defaults write com.apple.mail EnableBundles -bool true
    defaults write com.apple.mail BundleCompatibilityVersion 4
  5. Start the Console app to help you verify that the installation worked.  In the search bar in the upper right corner of the Console app type ‘Mail’.
  6. Restart the Mail app.
  7. In the Console app you should see the following lines:
    ReplyWithHeaders: Oh its a wonderful life
    Loaded ReplyWithHeaders

You can also find these instructions on the webpage for the bundle.  Pretty cool, huh?

Folding code in Xcode

A colleague of mine has the practice of collapsing all the methods in an Objective C file when he loads it to simplify finding the right method to view. When I asked him how he did it, he couldn’t remember since it was an automatic action. I finally got around to figuring out how to do it.

The feature is called Code Folding and can be done using the pointer, the menu bar, and the keyboard.

Code Folding using the Pointer

You may have noticed the different colored gray bars on the left of your code in Xcode and may have even accidentally hovered over one of them and been treated to a drastic change in the UI. Those gray bars allow you to fold – or collapse – your code. Using those bars, you can fold any size block, such as an if block, a for block, an @synchronized block, or even a whole method block.

Hovering over the gray bar shows you the block that will be folded. Clicking on the gray bar folds the block and shows a yellow ellipsis between the curly braces where the block used to be. It also changes the gray bar to an arrow. Clicking the arrow or double-clicking the ellipsis will unfold – or expand – the folded block.

Code Folding using the Menu Bar

The code folding functionality is exposed in the menus accessible from the menu bar as well.  In Xcode 4, check out Editor -> Code Folding.

Code Folding using the Keyboard

Apple has defined default keystrokes for code folding, but I found that some of them conflicted with keystrokes defined for other functions. In most cases the conflicts were caused by changes I had made – either in Xcode or in Mac OS X Lion – to accommodate writing code.

The following code folding functionality can be controlled using the keyboard:

  1. Fold all methods and functions – Option + Cmd + Shift + Left Arrow
  2. Unfold all methods and functions – Option + Cmd + Shift + Right Arrow
  3. Fold the current block – Option + Cmd + Left Arrow
  4. Unfold the current block – Option + Cmd + Right Arrow

I had to change the fold/unfold block key bindings because they conflicted with the key bindings I had selected for switching between Spaces in Lion. I changed them to Ctrl + Cmd + Shift + Left Arrow/Right Arrow respectively. This, of course, required that I change another key binding in Xcode – the key bindings for fold/unfold comment blocks.

Comment blocks are blocks of comments using /* and */ as delimiters. Since I almost never use this style of commenting, removing the key binding for folding/unfolding comment blocks in favor of code folding/unfolding was a good trade-off for me.

Mac Utility: Save

I’ve been enjoying my switch from Windows to Mac.  However just like Windows, Mac OS X has its quirks and irritations.  There are some very nice changes in 10.7 (Lion) to window management, but one thing missing is to restore window positions and sizes when switching between a single monitor and multiple monitors.

The Stay utility from Cordless Dog solves this problem.  Here is the summary of the app from the website:

What is Stay?

If you’re fastidious about keeping your windows tidy, Stay is for you. Stay ensures that your windows are always where you want them to be, even as you connect and disconnect displays.

The app is available both from the App Store as well as from their website and costs $15 ($14.99 from the App Store).  The difference between the two versions is that the version from the website supports Spaces.  The reason for this is that Spaces support requires private APIs, and Apple doesn’t allow apps submitted to the App Store to call private APIs.  Cordless Dog did a cool thing, though, for those that want to purchase from the App Store but also want Spaces support.  Simply make your purchase from the App Store and then install from the website.

One thing I’m not crazy about is that they app is delivered as a .zip file from the website rather than as a .pkg file.

Functionality Overview

Stay provides the following functionality:

  • Store windows for all applications.
  • Store windows for the current application.
  • Store the active window.
  • Restore all windows.
  • Edit stored windows.

When you choose to store windows for all applications, the app flips through all the windows, which takes more time than you’d think.  If you’re running the version that supports Spaces, it takes even longer, depending on how many windows and spaces you have.

Restoring windows isn’t quite as reliable as I was hoping.  The app uses window titles to identify windows, but some applications, such as Google Chrome, use a relatively transient string – the webpage title – for the window title.  This means as you switch tabs, Stay may not be able to identify the window to restore its position.

Overall, Stay is a big help in window management when switching between a single monitor and multiple monitors.

Git and git-flow command line completion

I switch between the command line and a GUI tool called SourceTree to manage my Git repositories.  When I first installed git-flow, I noticed that the readme talked about command line completion.  It took me a while to get around to installing it, but I have to say that it is really impressive.

Git and git-flow command line completion works like the rest of bash against the file system.  It reminds me a little of PowerShell on Windows (that should tell you how much I know about bash).

To install command line completion on a Mac, you need to find a copy of the git-completion.bash script.  Some of the posts I read on the net seem to indicate that it should be installed with Git in a contrib folder, but I couldn’t find it (showing how new I am on a Mac), so I decided to just clone Git from Git-Hub. Once I had that, I created a link to the script in my ~/bin directory:

cd ~/src/external
git clone https://github.com/git/git.git git
cd ~
mkdir bin
ln -s ~/src/external/git/contrib/completion/git-completion.bash ./git-completion.sh

I did a similar thing for git-flow-completion:

cd ~/src/external
git clone https://github.com/bobthecow/git-flow-completion.git git-flow-completion
cd ~/bin
ln -s ~/src/external/git-flow-completion/git-flow-completion.bash ./git-flow-completion.sh

The next thing to do is to modify your .bash_profile to call those scripts.

source ~/bin/git-completion.sh
source ~/bin/git-flow-completion.sh

Now when you execute git commands from the command line, you can use tab to complete commands, branches, remotes, and features. On top of that, it works for both local and remote elements. Very cool!

Resources

Defensive Programming in Cocoa (from VTM_iPhone)

I’ve been attending the iPhone Developers Conference in Seattle hosted by Voices That Matter (check out VTM_iPhone for the Twitter stream), which has been really informative.  Easily the most informative session at the iPhone Developers Conference for me was Defensive Programming in Cocoa by Mike Ash.  On Mike’s blog he has a series called Friday Q&A and it appears that most of the material for his talk was taken from an article he posted in December with the same name.  Two sections really stand out for me.

Memory Management – Put all your memory management for member variables in the setter for that member variable rather than scattering it around the code.  So instead of doing this:

[_myMemberVar release];

do this:

self.myMemberVar = nil;

after implementing this:

@property (retain) MyClass * myMemberVar;
@synthesize myMemberVar = _myMemberVar;

or this:

- (void) setMyMemberVar: (MyClass *) myMemberVar
{
    if (_myMemberVar != myMemberVar)
    {
        [_myMemberVar release];
        _myMemberVar = [myMemberVar retain];
    }
}

Categories – Since someone could override a method in your category with their own method making your method impossible to call, name your methods in a way to reduce that possibility.  The converse is also true.  For example, if you define a method in a category for an Apple-provided class, Apple could later come along and define that method in a class category and then all their calls will go to your method instead of theirs.

Key-Value Observers – The only thing not covered in that article that he covered in his talk was something that I started applying immediately:  When implementing key-value observers, make sure your observer only handles the notification if it knows that it is for your class.  This is a problem because your observer could be overriding the superclass’s observer and your observer must call the superclass’s observer if the notification isn’t for you.

To solve this, always pass specific to your class as the context parameter to the addObserver: method.

static void * s_kClassContext = &s_kClassContext;
...
[obj addObserver: self
      forKeyPath: "foo"
         options: NSKeyValueObservingOptionNew
         context: s_kClassContext];
...
- (void) observeValueForKeyPath: (NSString *) keyPath
                       ofObject: (id) object
                         change: (NSDictionary *) change
                        context: (void *) context
{
    if (context == s_kClassContext)
    {
        if ([keyPath isEqual: @"location"])
        {
            ...
        }
    }
    else
    {
        // Notification is not for us.
        [super observeValueForKeyPath: keyPath
                             ofObject: object
                               change: change
                              context: context];
    }
}

MySQL issue installing Gerrit on Snow Leopard Server

I spent more time than I wanted installing Gerrit on our Snow Leopard Server at work so that we could streamline code reviews.  I’ve found dealing with diffs in email to be rather inconvenient, so I’m hoping that Gerrit will be a big help.

As I went through the process of installing Gerrit, I hit a speed bump at the end of the init process where it couldn’t create a required function (nextval_account_id()).

Here’s a log of what happened:

$ java -jar gerrit.war init -d /Library/Gerrit/

*** Gerrit Code Review 2.1.6.1
***

*** Git Repositories
***

Location of Git repositories   [/Volumes/Data/src/git]:
Import existing repositories   [Y/n]?

*** SQL Database
***

Database server type           [MYSQL/?]:
Server hostname                [localhost]:
Server port                    [(MYSQL default)]:
Database name                  [reviewdb]:
Database username              [gerrit2]:
Change gerrit2's password      [y/N]?

*** User Authentication
***

Authentication method          [OPENID/?]:

*** Email Delivery
***

SMTP server hostname           [exchsrvr]:
SMTP server port               [(default)]:
SMTP encryption                [NONE/?]:
SMTP username                  [devbuilder]:
Change devbuilder's password   [y/N]?

*** Container Process
***

Run as                         [devbuilder]:
Java runtime                   [/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home]:
Upgrade /Library/Gerrit/bin/gerrit.war [Y/n]?
Copying gerrit.war to /Library/Gerrit/bin/gerrit.war

*** SSH Daemon
***

Listen on address              [*]:
Listen on port                 [29418]:

*** HTTP Daemon
***

Behind reverse proxy           [y/N]?
Use SSL (https://)             [y/N]?
Listen on address              [*]:
Listen on port                 [8081]:
Canonical URL                  [http://myserver.local:8081/]:

Exception in thread "main" com.google.gwtorm.client.OrmException: Error in mysql_nextval.sql:
CREATE FUNCTION nextval_account_id ()
  RETURNS BIGINT
  LANGUAGE SQL
  NOT DETERMINISTIC
  MODIFIES SQL DATA
BEGIN
  INSERT INTO account_id (s) VALUES (NULL);
  DELETE FROM account_id WHERE s = LAST_INSERT_ID();
  RETURN LAST_INSERT_ID();
END;

	at com.google.gerrit.server.schema.ScriptRunner.run(ScriptRunner.java:55)
	at com.google.gerrit.server.schema.SchemaCreator.create(SchemaCreator.java:103)
	at com.google.gerrit.server.schema.SchemaUpdater.update(SchemaUpdater.java:52)
	at com.google.gerrit.pgm.Init$SiteRun.upgradeSchema(Init.java:190)
	at com.google.gerrit.pgm.Init.run(Init.java:85)
	at com.google.gerrit.pgm.util.AbstractProgram.main(AbstractProgram.java:76)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.google.gerrit.launcher.GerritLauncher.invokeProgram(GerritLauncher.java:155)
	at com.google.gerrit.launcher.GerritLauncher.mainImpl(GerritLauncher.java:89)
	at com.google.gerrit.launcher.GerritLauncher.main(GerritLauncher.java:47)
	at Main.main(Main.java:25)
Caused by: java.sql.SQLException: This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3558)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3490)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2109)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2642)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2571)
	at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:782)
	at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:625)
	at com.google.gerrit.server.schema.ScriptRunner.run(ScriptRunner.java:53)
	... 13 more

The solution is to make one change to the MySQL configuration before initializing Gerrit:

SET GLOBAL log_bin_trust_function_creators=1;

Thanks to Anthony on the Google Group for Gerrit for the answer.

Collapsing commits in Git

There are times with any source control system that changes are committed after which you realize that you made a mistake and need to make a quick change.  In those cases, it seems pretty silly to keep the history for both those commits.

Git supports collapsing commits natively with the git rebase command.  This command provides more than simply collapse functionality, and the collapse functionality appears to be provided only in an interactive mode.  Paralyzed Egg has a good article on how to do this, but there was one pitfall I fell into that wasn’t described there so I’ll provide the detailed instructions here.

The first step is to look at the log to determine which commits you want to collapse:

$ git log -3
commit d43ace68df8a298d205fb422b3261d69bc5d19e4
Author: David Potter <davidp@example.com>
Date:   Sat Mar 26 15:21:29 2011 -0700

    Move README back to the root.

commit 553561e3330ea60db8a3f7baadd08ef116d22129
Author: David Potter <davidp@example.com>
Date:   Sat Mar 26 15:20:46 2011 -0700

    Move project files to the src directory.

commit 41a6af9de1f0e3fb84ae46c301a0252d0f861f02
Author: David Potter <davidp@example.com>
Date:   Tue Mar 22 11:35:14 2011 -0700

    Updated API layer

In this case I had moved one too many files into the src directory and needed to move it back. I wanted to collapse the last two commits into a single commit. To do this, I executed the following command:

$ git rebase -i HEAD~2

This will bring up your editor.

Note that git expects the editor to return when it is done. I had my EDITOR environment variable defined to launch TextMate which returns immediately, which prevents this feature from working.

The contents of the editor will look something like this:

pick 553561e Move project files to the src directory.
pick d43ace6 Move README back to the root.

# Rebase 41a6af9..d43ace6 onto 41a6af9
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

To merge those two into a single commit, change the second pick command to squash. Here is the result of that edit:

pick 553561e Move project files to the src directory.
squash d43ace6 Move README back to the root.

# Rebase 41a6af9..d43ace6 onto 41a6af9
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Save the file and exit. Git will bring up the editor with content that looks like this:

# This is a combination of 2 commits.
# The first commit's message is:

Move project files to the src directory.

# This is the 2nd commit message:

Move README back to the root.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    74-location.png -> src/74-location.png
#       renamed:    ACS-Info.plist -> src/ACS-Info.plist
#       renamed:    ACS.xcodeproj/project.pbxproj -> src/ACS.xcodeproj/project.pbxproj
#       renamed:    ACS.xcodeproj/project.xcworkspace/contents.xcworkspacedata -> src/ACS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
#       renamed:    ACS_Prefix.pch -> src/ACS_Prefix.pch
#       renamed:    Classes/ACSAppDelegate.h -> src/Classes/ACSAppDelegate.h
#       renamed:    Classes/ACSAppDelegate.m -> src/Classes/ACSAppDelegate.m
#       renamed:    Classes/BaseUrlConnection.h -> src/Classes/BaseUrlConnection.h
#       renamed:    Classes/BaseUrlConnection.m -> src/Classes/BaseUrlConnection.m
#       renamed:    Classes/InrixApi.h -> src/Classes/InrixApi.h
#       renamed:    Classes/InrixApi.m -> src/Classes/InrixApi.m
#       renamed:    Classes/MKMapView+ZoomLevel.h -> src/Classes/MKMapView+ZoomLevel.h
#       renamed:    Classes/MKMapView+ZoomLevel.m -> src/Classes/MKMapView+ZoomLevel.m
#       renamed:    Classes/Settings.h -> src/Classes/Settings.h
#       renamed:    Classes/TileOverlay.h -> src/Classes/TileOverlay.h
#       renamed:    Classes/TileOverlay.m -> src/Classes/TileOverlay.m
#       renamed:    Classes/TileOverlayView.h -> src/Classes/TileOverlayView.h
#       renamed:    Classes/TileOverlayView.m -> src/Classes/TileOverlayView.m
#       renamed:    Classes/TrafficViewController.h -> src/Classes/TrafficViewController.h
#       renamed:    Classes/TrafficViewController.m -> src/Classes/TrafficViewController.m
#       renamed:    Classes/TrafficViewController.xib -> src/Classes/TrafficViewController.xib
#       renamed:    GDataXMLNode.h -> src/GDataXMLNode.h
#       renamed:    GDataXMLNode.m -> src/GDataXMLNode.m

The instructions say that all lines beginning with a # will be ignored. That means all lines NOT beginning with a # will be included in the resulting commit message. That wasn’t clear to me when I first read that, maybe because that message is in the middle of the buffer. I changed this buffer to look like this:

# This is a combination of 2 commits.
# The first commit's message is:

Move project files to the src directory.

# This is the 2nd commit message:

#Move README back to the root.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    74-location.png -> src/74-location.png
#       renamed:    ACS-Info.plist -> src/ACS-Info.plist
#       renamed:    ACS.xcodeproj/project.pbxproj -> src/ACS.xcodeproj/project.pbxproj
#       renamed:    ACS.xcodeproj/project.xcworkspace/contents.xcworkspacedata -> src/ACS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
#       renamed:    ACS_Prefix.pch -> src/ACS_Prefix.pch
#       renamed:    Classes/ACSAppDelegate.h -> src/Classes/ACSAppDelegate.h
#       renamed:    Classes/ACSAppDelegate.m -> src/Classes/ACSAppDelegate.m
#       renamed:    Classes/BaseUrlConnection.h -> src/Classes/BaseUrlConnection.h
#       renamed:    Classes/BaseUrlConnection.m -> src/Classes/BaseUrlConnection.m
#       renamed:    Classes/InrixApi.h -> src/Classes/InrixApi.h
#       renamed:    Classes/InrixApi.m -> src/Classes/InrixApi.m
#       renamed:    Classes/MKMapView+ZoomLevel.h -> src/Classes/MKMapView+ZoomLevel.h
#       renamed:    Classes/MKMapView+ZoomLevel.m -> src/Classes/MKMapView+ZoomLevel.m
#       renamed:    Classes/Settings.h -> src/Classes/Settings.h
#       renamed:    Classes/TileOverlay.h -> src/Classes/TileOverlay.h
#       renamed:    Classes/TileOverlay.m -> src/Classes/TileOverlay.m
#       renamed:    Classes/TileOverlayView.h -> src/Classes/TileOverlayView.h
#       renamed:    Classes/TileOverlayView.m -> src/Classes/TileOverlayView.m
#       renamed:    Classes/TrafficViewController.h -> src/Classes/TrafficViewController.h
#       renamed:    Classes/TrafficViewController.m -> src/Classes/TrafficViewController.m
#       renamed:    Classes/TrafficViewController.xib -> src/Classes/TrafficViewController.xib
#       renamed:    GDataXMLNode.h -> src/GDataXMLNode.h
#       renamed:    GDataXMLNode.m -> src/GDataXMLNode.m

After saving and quitting the editor, Git prints out what it did:

[detached HEAD 419d987] Move project files to the src directory.
 26 files changed, 0 insertions(+), 0 deletions(-)
 rename 74-location.png => src/74-location.png (100%)
 rename ACS-Info.plist => src/ACS-Info.plist (100%)
 rename {ACS.xcodeproj => src/ACS.xcodeproj}/project.pbxproj (100%)
 rename {ACS.xcodeproj => src/ACS.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%)
 rename ACS_Prefix.pch => src/ACS_Prefix.pch (100%)
 rename {Classes => src/Classes}/ACSAppDelegate.h (100%)
 rename {Classes => src/Classes}/ACSAppDelegate.m (100%)
 rename {Classes => src/Classes}/BaseUrlConnection.h (100%)
 rename {Classes => src/Classes}/BaseUrlConnection.m (100%)
 rename {Classes => src/Classes}/InrixApi.h (100%)
 rename {Classes => src/Classes}/InrixApi.m (100%)
 rename {Classes => src/Classes}/MKMapView+ZoomLevel.h (100%)
 rename {Classes => src/Classes}/MKMapView+ZoomLevel.m (100%)
 rename {Classes => src/Classes}/Settings.h (100%)
 rename {Classes => src/Classes}/TileOverlay.h (100%)
 rename {Classes => src/Classes}/TileOverlay.m (100%)
 rename {Classes => src/Classes}/TileOverlayView.h (100%)
 rename {Classes => src/Classes}/TileOverlayView.m (100%)
 rename {Classes => src/Classes}/TrafficViewController.h (100%)
 rename {Classes => src/Classes}/TrafficViewController.m (100%)
 rename {Classes => src/Classes}/TrafficViewController.xib (100%)
 rename GDataXMLNode.h => src/GDataXMLNode.h (100%)
 rename GDataXMLNode.m => src/GDataXMLNode.m (100%)
 rename MainWindow.xib => src/MainWindow.xib (100%)
 rename car_256.png => src/car_256.png (100%)
 rename main.m => src/main.m (100%)
Successfully rebased and updated refs/heads/master.

Here is the result:

$ git log -2
commit 419d987ffbf2c70b1cad664aa2a56281e681268d
Author: David Potter <davidp@example.com>
Date:   Sat Mar 26 15:20:46 2011 -0700

    Move project files to the src directory.

commit 41a6af9de1f0e3fb84ae46c301a0252d0f861f02
Author: David Potter <david@example.com>
Date:   Tue Mar 22 11:35:14 2011 -0700

    Updated API layer

If conflicts occur during this process, you can resolve them and then continue the rebase, like so:

$ git rebase --continue

If you decide you don’t want to continue, you can abort the operation instead:

$ git rebase --abort

You can see the operations that have been performed on the repository using the following command:

$ git reflog --oneline

419d987 HEAD@{0}: rebase -i (squash): Move project files to the src directory.
41a6af9 HEAD@{1}: rebase -i (squash): updating HEAD
553561e HEAD@{2}: checkout: moving from master to 553561e
d43ace6 HEAD@{3}: commit: Move README back to the root.
553561e HEAD@{4}: commit: Move project files to the src directory.
41a6af9 HEAD@{5}: pull origin master: Fast-forward
ce65952 HEAD@{6}: pull origin master: Fast-forward
876b07b HEAD@{7}: pull origin master: Fast-forward
28c415e HEAD@{8}: clone: from git://server/project

Mount a Mac volume on a Mac from the command line

I still feel like a noob on a Mac even though I’ve been working professionally on one for almost 9 months.  Today I needed to copy files from one Mac to another and I wanted to do that all from the command line.  One way to do that is to use the mount command.  Here are the steps:

The trick here is to specify the file system (-t afp) and to specify the remote shared folder with the correct syntax (afp://remotehost/shareddir).

I found the man pages for mount to be less than helpful for discovering this.  However, I did find a web page that helped.

Bug in removeOverlay: in MKMapView

I’ve been developing an iPhone app on my job at INRIX that uses MapKit and have run into a number of interesting issues.  One in particular baffled me for some time, and it turns out to be (or at least appear to be) a bug in MKMapView.

The app displays traffic on a map, and on iOS4 it uses an overlay to display the traffic tiles.  As the user moves around or zooms in and out, the traffic tile image must be updated, which means removing the overlay and adding a new one.  This appears to work at most zoom levels until you start zooming in closer.  In those cases, the traffic tile would appear as a big blob – very ugly.

Searching around the net I found a few people that had experienced the same problem.  The work-around is to add a dummy transparent overly.  Sounds silly, but it works.

Here are some code snippets that work for me:

- (void) loadView
{
    // ...
    _mapView = [[MKMapView alloc] init];
    _mapView.delegate = self;
    // ...
    [self.view addSubview: _mapView];
    // ...
    BOOL isOS4orAbove = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 4.0f);
    if (isOS4orAbove)
    {
        // Add a dummy overlay to fix a bug in MKMapView where overlays don't get removed properly.
        // https://devforums.apple.com/message/307581
        MKPolyline * dummyOverlay = [[MKPolyline alloc] init];
        [_mapView addOverlay: dummyOverlay];
        [dummyOverlay release];
    }
    // ...
}
- (MKOverlayView *) mapView: (MKMapView *) mapView viewForOverlay: (id <MKOverlay>) overlay
{
    if ([overlay isKindOfClass: [TileOverlay class]])
    {
        // Return view for traffic tile overlay.
    }
    else if ([overlay isKindOfClass: [MKPolyline class]])
    {
        // This is for a dummy overlay to work around a problem with overlays
        // not getting removed by the map view even though we asked for it to
        // be removed.
        MKOverlayView * dummyView = [[[MKOverlayView alloc] initWithOverlay: overlay] autorelease];
        dummyView.alpha = 0.0;
        return dummyView;
    }
    else
    {
        return nil;
    }
}

The forum post that led me to this solution can be found here.  This appears to be a bug in MapKit.  I’d be interested in hearing if others have encountered this problem and how you solved it.