Planet Cocoa

July 03, 2009

Uli's Web Site

How to Migrate Time Machine Backups to a Larger Disk

One day it's bound to happen: Your Time Machine Backup Disk is full and there are too many changes for Time Machine to make a new backup. Or you simply think you'd feel more comfortable having more older files. So you go out and buy that coveted 3TB FireWire drive that you always thought you'd want for your backups. But how to proceed? (...)

July 03, 2009 01:32 AM

July 02, 2009

waffle

XHTML 2^W^W

Mark Pilgrim, 2003:

I’ll be revisiting XHTML 2 in about five years, assuming it survives that long.

World Wide Web Consortium (W3C), 2009:

XHTML 2 Working Group Expected to Stop Work End of 2009, W3C to Increase Resources on HTML 5; Today the Director announces that when the XHTML 2 Working Group charter expires as scheduled at the end of 2009, the charter will not be renewed. By doing so, and by increasing resources in the Working Group, W3C hopes to accelerate the progress of HTML 5 and clarify W3C’s position regarding the future of HTML.

On one hand, it survived for six years. On the other hand, it’s not like the people running it gave anyone a reason to revisit it.

by Jesper at July 02, 2009 07:06 PM

Red Sweater Blog

Getting Pretty Lonely

This very post is delivered to your browser or news reader by the famous and fabulous WordPress blogging system. In my work as the developer of MarsEdit, I am exposed to countless blogging options, each with its own strengths and weaknesses. And yet, I stick with WordPress because it strikes a balance of power and ease of use which feels comfortable to me. Not to mention that Joseph Scott and others are tirelessly working to improve its API.

WordPress is licensed under the terms of the Gnu Public License (GPL) which, in a nutshell, stipulates that you are free to use the software however you like, but if you make changes and distribute those changes, then you must share those changes under the same terms. This simple, radical restriction means that you are prohibited from taking a GPL project and incorporating it with a closed-source project.

Violating The GPL

Violating the GPL is easy. All you have to do is write some code, intermingle it with some GPL code, distribute a changed copy of the original, and refuse to share your contributions. Bam! You’re toast. Assuming the original authors discover your violation and decide to pursue a resolution.

Once a violation occurs, it might be settled privately, or could escalate to legal court procedures. But the most obvious form of resolution is for the author of the changes to release their code to the public under the terms of the GPL.

Depending on how much code you “mixed” with the GPL code, this could mean only a small portion, or it could mean the entire source code of your project. This so-called “viral nature” of GPL is what scares the bejeezus out of companies, large and small, who fear the consequences of having to give up their own intellectual property to the public.

The terms of the GPL sound pretty simple at first read, but due in part to the epic consequences of a violation, there has been a great deal of debate and uncertainty about what legally constitutes a violation. Most of the debate seems to boil down to two questions:

  1. What counts as a change to the original product?
  2. What counts as distribution of those changes?

If you can legally justify that your additions to a GPL project either don’t change or derive from the original product, or haven’t technically been distributed, then you are not subject to the restrictive terms of the license.

Take GIMP, the popular GNU-licensed image editing application. The application supports plugins, analogous to the types of plugins you might find for the commercial, closed-source application Photoshop. A savvy developer may argue that a plugin doesn’t meet the criteria of changing the original application, because the original application still runs in its unaltered condition whether the plugin is there or not.

But promoters of the GPL take the position that plugins, by nature of being loaded into the same code space as other GPL code, do constitute a modification of the original, and are therefore subject to the terms of the GPL. As far as I know this is not a question that has been well-tested in courts.

Let me take a moment to make this abundantly clear: I respect the rights of authors to license their software under whatever terms they choose, including the GPL. In my opinion, all the legal mumbo jumbo ceases to matter once the original author’s intentions are made clear. So if the author of GPL-licensed code clarifies to me that it cannot be run on Sundays, then their GPL means it cannot be run on Sundays. But this is one of the problems with the GPL: its terms are not often understood, even by the authors of GPL-licensed code.

WordPress Themes & Plugins

WordPress supports two explicit forms of extension, each of which may affect the appearance and functionality of the product. Themes tend to work as a “skin” for the appearance of a blog, while plugins tend to introduce completely new features. Since plugins in WordPress are analogous to GIMP or Photoshop plugins, it would stand to reason that they would also be covered by the terms of the GPL. But what about themes?

Themes have been controversial in the WordPress community, as a few commercial business models have sprung up to take advantage of bloggers’ desires for high quality themes at an affordable price. One approach is to distribute “free” themes that contains commercial ads. So you might stumble upon the perfect theme for your blog, only to learn that the glaring “Brought to you by Hostess Cupcakes” line near the bottom of your page cannot be removed.

But the terms of the GPL, if themes are covered, would require that end users be granted the legal right to modify and redistribute their own copy of the theme. Zap the sponsorship, reupload to your site, and you’ve got a free, high quality theme with no ugly ads.

Today, Matt Mullenweg of the WordPress project announced his lawyer-supported opinion that themes are partly covered by the GPL:

I reached out to the Software Freedom Law Center, the world’s preeminent experts on the GPL, which spent time with WordPress’s code, community, and provided us with an official legal opinion. One sentence summary: PHP in WordPress themes must be GPL, artwork and CSS may be but are not required.

If you’re starting with the understanding that WordPress itself is GPL, and WordPress plugins are GPL, then it’s not so much extra hay on the camel’s back, to also clarify that its themes are to some extent GPL. But it got me thinking again about my own blog, and about the restrictions the GPL imposes on the kinds of things I can do with the software that runs it.

GPL Stifles Participation

Now for the most controversial point of this article, where I suggest that the GPL does more to harm collaborative development than it does to help it.

For the purposes of this argument, let me reduce all the source code in the world down to three rough categories. I recognize I have omitted some classes of license here, but for the sake of argument, most projects fall into these camps:

  1. GPL code. Changes may be distributed only in other GPL products.
  2. Liberal-licensed code. (MIT/BSD/Apache/etc). Changes may be distributed anywhere. Appropriate origin-attribution may be required.
  3. Closed-source code. May be distributed only by the copyright owner and other explicit licensees.

Now, there are a few people in the world who, for political or philosophical reasons, will only participate in a GPL project. And for compariable yet opposite reasons, there are some who will only participate in commercial, closed-source projects. But I propose that the vast majority of developers will participate in any project that is advantageous to them.

So let’s imagine a representative, run-of-the-mill developer who is working on a project that falls into each of these three camps. If this developer is not radically committed to their own project’s license, they will naturally look to outside resources in order to bolster the success of their own work.

As the developer evaluates communities to participate in, they must evaluate the legal impact such participation will have on their own project. The closed source communities are, by definition uninviting to outsiders. GPL communities are open and embracing of other GPL developers, but generally off-putting to liberal-license and closed-license developers. Only the liberal-license communities are attractive to developers from all 3 camps.

I know what some of the GPL-enthusiasts are thinking now: “leeches don’t count as community.” Many GPL developers take comfort in the fact that their hard work can’t be quietly taken and incorporated into a commercial product, without any payback of time or money to the original project. But you’re piloting an open source project, and the first step of building a community is to get people in the door. Liberal licenses? Whoo-eee do they ever get people in the door.

If you operate from the presumption that great developers love to build great projects, the first step in any successful open source project is to get as many great developers in the door as possible.

It’s Your Party

Yes, this is just me and my crazy theories. I haven’t done exhaustive research to prove that liberal-license communities thrive more than GPL communities. But the anecdotal examples are staggering. The very foundation of Mac OS X, the operating system through which I’m typing, is thanks to the liberally-licensed FreeBSD operating system.

Looking over to the right of my screen, I’m watching this sentence appear in a live web preview as I type, thanks to the WebKit project, whose liberal license makes it compatible with closed source projects such as Safari, as well as open source efforts such as Google’s Chromium project.

For years, the problem of a generic HTTP client library that runs on every major platform has been addressed by libcurl, whose liberal license has caused it to be embraced by countless companies and projects.

The popular Subversion source control system’s liberal license enabled Sofa, a commercial software business to contribute value to the community with its extremely polished, award-winning client application. Meanwhile, the newly popular distributed source control systems presents three major choices: git, Mercurial, and Bazaar. All are restrcted by the GPL-license, and therefore none is likely to inspire development of a Versions-caliber client.

I’ve touched the tip of the iceberg, and yes I’ve neglected to mention some GPL success stories such as Linux, MySQL, and gcc. These communities have thrived to some extent because the passions of the GPL community are strong, but we can’t know whether their success is in spite of the restrictions their license places on participation by the broader developer community.

Speaking of GPL succeses, WordPress is itself an example of monumental success. All of its developers have something to be immensely proud of. But whenever I am reminded that WordPress is GPL, my passion for it takes a bit of a dive. I’m more comfortable with the true freedom of liberally-licensed products. If a liberally-licensed blog system of equal quality, ease of use, and popularity should appear, my loyalties to WordPress would not last long.

It’s your party, and you’re entitled to write the guest list. But take a look around the room: not as many folks as you’d hoped for? Liberally-licensed projects are booming. Speaking for myself, a developer who has been to all the parties, I’m much more likely to pass through the door that doesn’t read “GPL Only.”

by Daniel Jalkut at July 02, 2009 06:09 PM

Cocoa With Love

Custom views in Interface Builder using IBPlugins

If you have custom views configured in code, it can be time consuming to configure them for each instance and make them look right in context. To make the process smoother, you can create Interface Builder plugins and configure your objects in Interface Builder. While the Xcode documentation explains how to do this, it is long, thorough and confusing. Here is the simplified set of steps that I use to create Interface Builder plugins quickly.

Introduction

Apple do have extensive documentation on Interface Builder Plugin creation. However, their documentation is thorough enough that may be overwhelming the first time you want to write an Interface Builder plugin.

I also find the workflow in Apple's IBPluginGuide very different to my typical workflow. I think this is because I normally just want an existing class to show up in Interface Builder so I can tweak one or two attributes — the processes for creating a redistributable library of components is more than I need.

So I was inspired to write this post: a shorter, workflow-optimised version of the same process described in the IBPluginGuide — using Interface Builder to configure a custom button.

A custom buttom

Consider the button in the following window:

ibplugin1.png

A big, drab, gray button. Maybe gray is an all right default but it doesn't really match this window. This post will make it easy to adjust this color in Interface Builder.

The large gray button here uses a custom button cell which draws the button using the Gloss Gradient from one of my earlier posts. The drawing code is:

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
    NSBezierPath *roundRectPath =
        [NSBezierPath
            bezierPathWithRoundedRect:NSInsetRect(cellFrame, 2, 2)
            xRadius:5
            yRadius:5];

    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
    [graphicsContext saveGraphicsState];
    [roundRectPath addClip];

    DrawGlossGradient(
        [graphicsContext graphicsPort],
        self.buttonColor, // -- this determines the color of the button
        cellFrame);

    if ([self isHighlighted])
    {
        [[NSColor colorWithCalibratedRed:0.0 green:0.15 blue:0.35 alpha:0.5]
            set];
        [roundRectPath fill];
    }
    [graphicsContext restoreGraphicsState];

    [[NSColor lightGrayColor] set];
    [roundRectPath setLineWidth:2.0];
    [roundRectPath stroke];

    [self drawInteriorWithFrame:cellFrame inView:controlView];
}

This is all fine except that the color of the button is determined by the self.buttonColor property and if we have to choose this in code (continually changing the value, fixing, continuing, refreshing and repeating) it could get very time consuming.

A better solution would be to edit the button's color in Interface Builder. That way, we will be able to use color sliders to update the button drawn in the complete context of the window, in real-time.

Creating the IBPlugin project

My process normally starts by creating a custom view component. In this case, I have already created a class named CustomButtonCell in the project for my main application (AppWithButton). After creating the class, I have decided it would be a good idea to have an Interface Builder Plug-In.

A quick point about class requirements: every property we want to configure in Interface Builder must be encoded and decoded for the object using implementations of the NSCoder protocol methods initWithCoder: and encodedWithCoder: overrides. Download the project linked below to see how this is done.
1. Create and name the project

"Interface Builder Plug-In" is a project template in Xcode. In the New Project window, it's in the "Mac OS X -> Standard Apple Plug-ins" section.

An important point to consider is the name for the project — it should not be the same as the custom view component (in this case, CustomButtonCell) because the template will create a class with the name and it can't conflict with the existing view component.

I chose "ButtonPlugin" and saved the new project in the folder for my existing AppWithButton project.

2. Make the new project use the existing CustomButtonCell
Delete the default files and insert our own

The template creates a custom view in the ButtonPlugin project. I never use this (since I want to use my existing view). So I delete the ButtonPluginView.m, ButtonPluginView.h references and files.

I add my CustomButtonCell.m and CustomButtonCell.h files to the ButtonPlugin project but I don't copy or move the files — I leave the files at their current locations in AppWithButton project but drag them into the ButtonPlugin's Source Tree.

Make certain these new files get built

You need to check that the CustomButtonCell.m is added to the Compile Sources build phase of the ButtonPluginFramework Target and the CustomButtonCell.h is added to the Copy Headers build phase of the ButtonPluginFramework. Neither should appear in the ButtonPlugin target.

Then, select the "CustomButtonCell.h" file from the "Copy Headers" build phase and in the Detail View, change the "Role" from "Project" to "Public". This is a pretty obscure thing to do. I normally don't have the Detail View visible — if you don't know which view is the Detail View, it's time to learn because there is no other way to change this value in Xcode.

ibplugin3.png

Change the Role of the "CustomButtonCell.h" to "public" in the Detail View

Rename the file ButtonPluginViewIntegration.m to CustomButtonCellIntegration.m and replace every occurrence of ButtonPluginView in this file to CustomButtonCell.

3. Prepare all the other files in the project

Make the following file changes:

  1. Find the ButtonPluginView.classdescription and rename this file to CustomButtonCell.classdecription (same as our custom cell).
  2. In the contents of this file, change the ClassName to match the actual class name CustomButtonCell and change the SuperClass to be NSButtonCell (again, matching the actual super class for our custom button cell).
  3. In ButtonPlugin.m (the top level class in the ButtonPlugin project), set the bundle identifier to something appropriate. I used com.mattgallagher.ButtonPlugin — this needs to be unique among Interface Builder plugins, so you pick an appropriate value each time.
  4. Set the bundle identifier in the Info.plist and the ButtonPlugin-Info.plist to the same com.mattgallagher.ButtonPlugin value.
4. Configure the display of the button cell for the Interface Builder Library panel

Open the ButtonPluginLibrary.nib. This file contains the "Library Object Template" view (an instance of IBLibraryObjectTemplate). You can find it in the "Library Objects" view at the top level.

Delete the library template that doesn't apply

The Library Object Template will contain a "Template" and an "Example" square. The "Template" version is for NSView subclasses but our CustomButtonCell needs to be embedded in another object (an NSButton) so we will use the "Example" square — so delete the "Template" square.

Set our custom class in the library template

If you click on the button in the "Example" square then click again, it will select the NSButtonCell inside the button (these clicks should be slower than a double-click, since a double-click will edit the text of the NSButton instead of selecting the NSButtonCell inside). With the NSButtonCell selected:

  1. Type Command-6 to select the correct inspector panel.
  2. Enter the custom class name in the "Class" field of the inspector — in our case, we need this to be CustomButtonCell.
  3. Set the button cell class for the NSButton to the right of the "Example" box in the same way.

Select the Library Object Template (ButtonPluginLibrary.nib window -> Library Objects -> Library Object Template) and:

  1. Type Command-1 to select the correct inspector panel.
  2. Fill in the "Label", "Summary" and "Description" for your Library Object as you choose. I like to delete the Path and leave it blank but you can add a path if you want to categorize your custom classes.
5. Configure the Interface Builder Inspector panel

Back in the ButtonPlugin project again, open the ButtonPluginInspector.xib.

Set all the controls in the Inspector panel how we want them

I deleted everything in the "Inspector View" window except 1 label and the NSColorWell. I moved these controls to the top of the view and then made the view 35 pixels high.

Connect the controls so they act on our object

To make the color selector do something, I used bindings. Select the NSColorWell and:

  1. Type Command-4 to select the correct inspector panel.
  2. Under the "Value" subheading, bind to File's Owner (make sure the checkbox is selected too).
  3. Set the Model Key Path to inspectedObjectsController.selection.buttonColor.

This binding needs one other change to work. Back in the ButtonPlugin project, open the CustomButtonCellIntegration.m file. Change the [[keyPaths objectForKey:IBAttributeKeyPaths... line to:

[[keyPaths objectForKey:IBAttributeKeyPaths]
    addObjectsFromArray:[NSArray arrayWithObjects:@"buttonColor", nil]];

This tells Interface Builder to track and monitor the buttonColor property on the CustomButtonCell class. It does not actually perform the value changing (the binding we established will change the value directly) but it will make certain that this value will be tracked for undos and can be edited correctly.

Using the IBPlugin

At this point, you can run the ButtonPlugin project and it will launch Interface Builder with the plugin visible. The only problem is that the plugin won't be visible if you launch Interface Builder in any other way.

Build Settings

The following steps will make the ButtonPlugin project build the ButtonPlugin.framework and ButtonPlugin.ibplugin and install it in your Library.

Double click the ButtonPlugin project item in the ButtonPlugin project Source Tree to edit the project settings. Select the "Build" tab, then choose "Configuration: All Configurations". Then make the following changes:

  1. Set the "Deployment Location" checkbox to checked.
  2. Set the "Installation Build Products Location" to "$(HOME)/Library/Frameworks".
  3. Set the "Installation Directory" to "/".

Double-click the "ButtonPlugin" target in the Source Tree to edit that target's settings. Select the "Build" tab, then choose "Configuration: All Configurations". Then make the following change:

  1. Set the "Installation Directory" to "/ButtonPlugin.framework/Resources.
And now it's installed

With these changes made, build and run the ButtonPlugin project and it will install the ButtonPlugin.framework in your ~/Library/Frameworks directory and load it in Interface Builder. From now until you remove the framework from your library, it will remain in Interface Builder and you can use it at any time.

I realize that this configures the app to install the "Debug" build in the user's library, over the top of the "Release" build (if the Release build is already built) but this is really just for local use, so I don't really care. If you do care, you might not want to apply these settings to all configurations. For me: this is faster to implement and easier to manage.

Integration with the original AppWithButton project

After setting up the Interface Builder plugin, instance of CustomButtonCell in your existing .xib and .nib files will not instantly update. You'll probably need to create new versions of the cell by dragging them out from the Library.

In this example, I have chosen to not link the original AppWithButton project with the ButtonPlugin.framework. This is because I don't want to distribute either the framework or the plugin — they are both for my purposes only.

If you wanted to link them, you could remove the CustomButtonCell.m and CustomButtonCell.h files from the original project and replace them with the ButtonPlugin.framework. According to the Xcode documentation, this arrangement would allow you to avoid installing the ButtonPlugin.framework in your ~/Library/Frameworks directory (Interface Builder would automatically find the plugin for any .nib or .xib file in the project). That's a nice idea but it has never worked for me, so I never bother.

Set a build dependency to keep ButtonPlugin up-to-date

Instead, I like to drag the ButtonPlugin.xcodeproj file into the AppWithButton Source Tree and edit the AppWithButton target, go to the "General" tab and add the ButtonPlugin.xcodeproj as a Direct Dependency.

The reason for this is that if I change the CustomButtonCell class and rebuild, it will automatically rebuild the Interface Builder plugin accordingly (you need to restart Interface Builder to see the changes).

ibplugin2.png

The AppWithButton window in Interface Builder, adjusting the color of the button in real-time.

Conclusion

You can download the complete AppWithButton project zip file (including the ButtonPlugin project) (385kB).

It takes quite a few steps to set up an Interface Builder plugin. Fortunately, they're all simple, if a little menial.

Notice how little code was actually written though: bindings and Objective-C 2.0 properties make this whole process considerably easier — there is no actual code written to set the buttonColor (except in the NSCoder method implementations) since the bindings do it all for us.

Once you're practiced at making plugins in this way, the effort to create one may be justified, even just to tweak simple properties like this.

Do I know how to do this for Cocoa Touch classes for iPhone development? No. I imagine it's possible but you'd need to completely change the IBPlugin project from the template and I've never tried.

by Matt Gallagher (noreply@blogger.com) at July 02, 2009 05:47 PM

infodan

Managing static versioned libraries in OS X

The Mac OS X offers great tools to developers in order to manage libraries with many versions: Frameworks.

Anyway sometimes you may want to use static libraries instead. Maybe just as a means of (psycologically) obfuscating your preciuos code you don't want to put in a framework for the world to know...

I'm currently experimenting with an idea that should make easier to selectively link different libraries versions to your application. The trivial idea is to create a folder for each of your libraries, for example in your Library home folder, named after their version:

/home/dan/Library/ACME/MySecretLib-1.0.0/
/home/dan/Library/ACME/MySecretLib-1.0.1/

Inside each folder there is your library archive (or archives if you want debug and release flavours) and a Headers folder. Your client projects will simply include the headers and link the library archive.

I will show you how to obtain this with Xcode. Refer to the Apple Xcode Build System Guide and to the Xcode Build Settings Reference if you're not at home with the (complex) Xcode build stuff.

Open the library Project Info window and in the Build tab define, for all configurations, the user-defined build settings:
  • ACME_LIBRARY_DIR = $(USER_LIBRARY_DIR)/ACME
  • ACME_LIBRARY_NAME = MySecretLib
  • ACME_LIBRARY_VERSION = 1.0.0
Then open your static library Target Info window and in the Build tab edit the settings:
  • DEPLOYMENT_LOCATION = YES
  • PRODUCT_NAME = $(ACME_LIBRARY_NAME)
  • DSTROOT = $(ACME_LIBRARY_DIR)
  • INSTALL_PATH = /$(ACME_LIBRARY_NAME)-$(ACME_LIBRARY_VERSION)
  • PUBLIC_HEADERS_FOLDER_PATH = $(INSTALL_PATH)/Headers
Then add a Copy Headers Build Phase to your target and copy your public headers to it. Remember to set their Role to Public.

If you need a debug library with symbols simply add a "debug" line to the BUILD_VARIANTS setting.

To link your library you will define the following build settings:
  • ACME_SECRETLIBRARY_DIR = $(USER_LIBRARY_DIR)/ACME/MySecretLib-1.0.0
  • ACME_SECRETLIBRARY_NAME = libMySecretLib
  • HEADER_SEARCH_PATHS = $(ACME_SECRETLIBRARY_DIR)/Headers
  • OTHER_LDFLAGS = $(ACME_SECRETLIBRARY_DIR)/$(ACME_SECRETLIBRARY_DIR).a
changing the .a to .da if you're using the debug/release flavours.

by ildan (danpizz@gmail.com) at July 02, 2009 11:49 AM

July 01, 2009

waffle

Solid

Yesterday, my new Unibody 15″ MacBook Pro arrived. Like last time, there are stats to be had, but they won’t give you the big picture. (I’ve placed them at the bottom.)

I ordered it with a 256 GB SSD, and I don’t think I’m ever going back.

  • Spotlight is instant. It’s so instant that you see the menu flickering as it is being redrawn continually. It appears that the search results come in at a steadier clip and with a lower latency than with hard disk drives.
  • Xcode builds are very fast. Adium 1.3.5 builds about 1.6 times faster, but a simple project like Hex Color Picker that took 13 seconds to build from scratch on my previous MBP now take just over one second. If I were to second-guess this startling advance, I’d say that with an SSD, going to disk and seeking wildly is no longer the issue it once was. It’ll still be redundant once we get smarter compilers, but reading a number of blocks randomly scattered across the storage is the kind of thing SSDs were built to do.
  • Time from bootup sound to usable desktop: 14 seconds. I shit you not. I know of PC laptops that have a hard time waking up in 14 seconds.
  • Everything is much snappier even under load. Apps load up much faster because their databases can be read faster, so you’re not waiting on disk.

I started fresh this time (migrating by hand) and the CPU is also a healthy 3.06 GHz Core 2 Duo, so a fresh system and a better CPU can obviously rig the results. However, there are plenty of findings to suggest that the gain is systemic and prevailing. Mail never loaded its main window so fast, and Mail needs to load a database. Many apps that for a few months with a fresh system would start in one dock icon bounce now start immediately, even before their first bounce has really taken off.

I’m also getting 8 GB RAM from OWC; partly to create a better environment for running Windows simultaneously, partly to stave off unnecessary swaps from the not-infinite SSD, but mostly because you can never have enough RAM.

Lastly, I am absolutely in love with the quality of this enclosure. The earlier MBPs will still beat many laptops, but they now seem like dinosaurs, made of damp cardboard and duct tape. The new MBP is solid, the rubber bezel around the very edge of the screen helps make the magnet latch snap the lid shut slightly less bewilderingly than for my old white MacBook, the speaker holes are small enough to impress (and avoid being filled by stray crumbs) and plenty enough to fill the Albert Hall, the glass trackpad is nice if equipped with a slightly too noisy click and the keyboard is the same I’ve loved since the MacBook. I still don’t like the black bezel around the display, and I don’t like the glossy display, and I still think it’s worth it because this is such a solid computer in every possible sense of the word.

MacBook Pro
Early 2008 (discrete enclosure)
MacBook Pro
Mid-2009 (unibody enclosure)
2.4 GHz Core 2 Duo (64-bit)3.06 GHz Core 2 Duo (64-bit)
3 MB L2 cache6 MB L2 cache
800 MHz FSB1066 MHz FSB
4 GB RAM (OWC)
 
4 GB RAM
Waiting on 8 GB (OWC)
 
250 GB HDD (5400 rpm)256 GB SDD
8x SuperDrive8x SuperDrive
 
NVIDIA GeForce 8600M GT
(256 MB GDDR3 RAM)
 
 
 
NVIDIA GeForce 9400M
(siphoning 256 MB RAM)
—and—
NVIDIA GeForce 9600M GT
(512 MB GDDR3 RAM)
15.4″ anti-glare display
1440 x 900
15.4″ glossy (reluctantly) display
1440 x 900
iSightiSight
 
FireWire 400 + 800FireWire 800
2 x USB 2.02 x USB 2.0
Optical digital/analog audio in/outOptical digital/analog audio in/out
Gigabit EthernetGigabit Ethernet
802.11n Wi-Fi802.11n Wi-Fi
Bluetooth 2.1 + EDRBluetooth 2.1 + EDR
DVI + DVI-VGA adapterMini DisplayPort
ExpressCard/34 slotSD Card slot
Kensington lock slotKensington lock slot
 
Trackpad with multi-touch
Up to 3 fingers supported by 10.5
Glass trackpad with multi-touch
Up to 4 fingers supported by 10.5
Backlit Aluminium keyboardBacklit roundrect-style Keyboard
300-cycle battery
Up to 5 hours (advertised)
1,000-cycle battery
Up to 7 hours (advertised)
 
2.45 kg2.49 kg

by Jesper at July 01, 2009 11:00 PM

rentzsch.com : tales from the red shed

PSIG 125: Thu Jul 2 2009

When:
Thursday, July 2nd, 2009 @ 7pm
Where:
Hotel Indigo. Use this link to get driving directions to the hotel to avoid a Google Maps bug or use these directions from the Arlington Park Metra station.
Schedule:
Show & Tell
We'll start out the meeting by going around the table and talking about what we're currently working on or learning about. Handouts are welcome, or bring along your Mac and we'll hook it up to the projector.

Book Reports
Bring along the book you're currently reading, or one of your old favorites. Hopefully the book would have some relevance to programming, but we're fairly open-minded. Just give us an overview the book's topic, and what you liked (or disliked) about it.

Topics
Victoria Wang will give an introduction to programming 2D animations and web-based games. She will focus on both the industry standard, ActionScript for Adobe Flash, as well as the hot open source alternative, Processing and its counterpart, Processing.js. Join us for some fun code and shiny demos!

Dinner
Please Bring Your Own Beverage. And food.

Looking for Presenters:
I'm always on the look-out for folks to want to give a talk for a meeting. The talk doesn't need to be long (10 minutes is fine if your topic is small or you just want to tease the group) and you don't need to be an expert (just tell us why you think your topic is cool or your experiences). Toss me an email and I'll assign you a chunk of time.

July 01, 2009 05:10 AM

June 30, 2009

PrEV

Uli's Web Site

How to Migrate Time Machine Backups to a Larger Disk

One day it's bound to happen: Your Time Machine Backup Disk is full and there are too many changes for Time Machine to make a new backup. Or you simply think you'd feel more comfortable having more older files. So you go out and buy that coveted 3TB FireWire drive that you always thought you'd want for your backups. But how to proceed? (...)

June 30, 2009 08:49 AM

June 29, 2009

Gus Mueller

FastScripts

I've been meaning to write this post for over a year now, and now that version 2.4 of Red Sweater's FastScripts is out I figure I've got no excuse.

So what is FastScripts? It's an application that sits unobtrusively in your menu bar, and gives you easy access to scripts via its menu bar icon, or a global keyboard shortcut. I tend to use it via its shortcuts.

Why should you use it? Well, how about I tell you why I use it? Basically, I use FastScripts as a replacement for Apple's Spaces, since I think Spaces sucks.

So, instead of having multiple spaces with Xcode in one space, or irc + iChat in another, and WebKit in its own; I create multiple scripts which hide all applications, and then brings forward just the ones I want. For instance, here is the script I've got set to control-keypad 2, which lists my "social" apps:

tell application "Colloquy" to activate

-- this delay lets me take my fingers off the hotkey
delay 0.2

-- hide all apps, from within Colloquy
tell application "System Events" to keystroke "h" using command down & option down

-- let the apps hide
delay 0.1

tell application "System Events" to set visible of process "iChat" to true
tell application "System Events" to set visible of process "Colloquy" to true
-- There's probably a better way to do all this, but I haven't found it yet.


Here's how to show the Finder, and hide all other apps (which I have set to control-keypad 7).

tell application "Finder"
    activate
    set visible of every process whose name is not "Finder" to false
end tell


And I've got scripts that do similar things for just VoodooPad or Acorn, and one for Xcode + Terminal, and one that kills VoodooPad and brings up Xcode, and since you can have applications defined by shortcuts as well in FastScripts, I've set some up for specific applications like Twitterrific, Mail, Xcode, etc.

I think this is a better way to manage multiple workspaces. The best part is nothing unexpected ever happens! If I click on WebKit in the dock, all my windows stay in one spot and WebKit just comes up. With Apple's Spaces, I wasn't ever sure what was going to happen.

Anyway, check out FastScripts, it's pretty rad. And I'll see if I can get Daniel to support JSTalk sometime soon as well.

June 29, 2009 06:54 PM

Alexandre Gomes

Mac OS X Tip – Type Or Edit Path In Finder

Sometimes I just want to type a path in Finder, or maybe paste it from somewhere. It can be painful slow to track the right folders 1 by 1. Surprisingly, its difficult to find this little trick via a web search, it seems everyone if focused of displaying the current path at the finder windows [...]

by alexmipego at June 29, 2009 02:57 PM

rentzsch.com : tales from the red shed

C4 Blitz Talks

For C4[3] (September 25-27 2009 in Chicago, registration opening “soon”) I'm introducing a new feature: Blitz Talks.

The idea is simple: micro-presentations just prior to the normal, full length ones.

Blatantly ripped off from O'Reilly’s Ignite, the Blitz Talk format is constrained and predefined: you get 20 slides. Each slide is displayed for 15 seconds before I advance to the next slide for you. That adds up to five minutes, total.

A lot of really interesting topics just don’t need the full 45-minute treatment, and I think Blitz Talks are a great way to pack even more content into C4.

Another facet to the Blitz Talks are how they're selected. Normally I ask specific people to talk about specific things at C4. With Blitz Talks, that’s turned around.

Instead, I invite you to submit your proposed 20 C4[3] slides to me by Monday, July 13th 2009. Please send me a private URL to your Keynote file, PowerPoint file or 280 Slides presentation, with what you're going to be saying stashed in the speaker notes.

I'll select about 10 talks. Selected speakers get early access to registration, ahead of when I open general registration. (For now, I've abandoned the C4 attendee lottery idea and am going with this instead — my intent is to “reward” folks passionate enough to contribute to C4).

For your actual presentation at C4, Victoria and I made an app that will display your slides along with an on-screen timer: Blitz.app. The elapsed-time indicator takes up the lower-right corner of your slides, so be sure to test your slides against it.

For now Blitz.app only accepts PDF files, so eventually your slides will have to make it into that format. I'm looking into reading Keynote 5 files directly, but I don’t know if that feature will be ready in time for C4[3]. Patches welcome.

June 29, 2009 08:24 AM

June 28, 2009

cocoamondo

Zoom Zoom

In my last post I presented some kode for creating a custom migration manager. The kode has some sweet syntax colour highlighting courtesy of a WordPress plugin called wp-syntax. To keep the original format of the kode, wp_syntax uses a scrollable div. This works, but isn’t ideal, especially when the content column is narrow, [...]

by Matthieu Cormier at June 28, 2009 11:46 PM

waffle

R

I managed to completely miss Jeff Atwood’s (pent-up) love letter towards the iPhone product, platform and experience. He argues, and he’s right, that the platform is good enough to kick ass, and that the hardware is starting to catch up with the promise of the software. (We won’t get there until we get better multi-tasking, but that requires yet more hardware; more resources, more battery and quicker computation. I’m guessing next year’s model will take some decisive steps in the right direction. Conceptually, the 3G S is a mere patch.)

Two years ago, I’d just tried the original iPhone myself:

The iPhone is not perfect. A first for any phone this side of, oh, 2003?, it doesn’t have MMS at all, so you can’t send images or audio to other cell phones without being convinced the other cell phones have set up email clients. As we all have heard, 3G would really be better for data transfer speed. And about four to five of the bundled applications are crap – the specific group varies depending on who you ask, but it generally includes the Notes app. And what kind of PDA has a YouTube client but not copying and pasting, or the ability to view Excel Office Open XML documents but not an accessible file system?

I am telling you today to shut up for a few seconds and stay for the ride. It is pretty much impossible to make the kind of conclusions I make about the iPhone’s UI and its role in making this sort of thing available for the first time to ordinary people without also making the comparison to the original Macintosh. It was far from perfect when it came out, which led a lot of people to discredit it. Within three years almost all of the “but it doesn’t have/do X” concerns were gone, and within ten years it had swayed every other competitor. I believe something similar is going to happen. It’s not a coincidence that the iPhone owners are – AT&T handcuffs aside – generally satisfied.

Except for the “accessible file system”, which was a bullshit substitution for “some sort of shared-ish general storage system”, every one of those things have been fixed. (I didn’t mention conventional Bluetooth file transfers, which is just about the only thing left to add to the platform itself.) They’re even slowly wiggling out of the AT&T handcuffs.

Two years. What’s happened in two years for everyone else? Some other companies have gotten around to providing an iPhone-like — and by that I mean usable — experience, but most still think that this will blow over. There’s a time and a place for simple phones with simple keypads and simple functionality, but they haven’t even really gotten that right. (That might sound a bit ridiculous since some of these companies have been making mobile phones for 40 years now. My point is that if you want a simple phone, you have to get a cheap phone, and if you want a good simple phone, you have to get an expensive phone and avoid the rest of it.)

I think it’s time to settle once and for all that ordinary phones (non-smartphones) are like DOS. They are a sequence of drilling down into textual menus, sometimes represented by 12 colorful icons or with four or five tabs, but it is still menus. It is perfectly serviceable for some tasks, and it’s not a broken approach, but for most tasks, we can do better. Dialing numbers or picking people to dial from a list by typing is maybe the only task these interfaces do graciously.

Once in this narrative, it just keeps coming. The Palm Pilot was the Xerox Alto. Ahead of its time, defining some of the core concepts of what a mobile GUI could look and feel like. Windows Mobile was… well, Windows. No one loved Windows until Windows 3.11, and that took eight years. The first undeniably good Windows was still wretchedly dual: Windows 95 was stuck between 16-bit and 32-bit, famously unable to be taken seriously as an OS by some people because of some DOS gunk at the bottom of the stack. Now everyone’s trying to patch Windows Mobile out of suckiness with their own abstraction layer on top, and any month now it might even start working.

Not everything’s a perfect fit to this mental model, but you have to agree that there are similarities. The two biggest changes are that Apple made a late entry and snagged substantial market share (like their MP3 player entry with the iPod) and that Palm came back with something that theoretically could beat everyone if well executed.

Jeff makes an interesting prediction: “I believe the iPhone will ultimately be judged a more important product than the original Apple Macintosh.” I agree with that and I hope it’s obvious why; the original Mac was a platonic product that eventually saw success with some professionals and developed at the pace of the industry, and the original iPhone was a platonic product that immediately went for market share and developed aggressively. I still get jeers for owning an iPhone, but there are fewer and fewer with any real substance to them because most of the gaps are being filled. Most of them are centered around the premise that I should own a Windows smartphone just because it’s Windows, which seems exactly like the kind of argument these people like to stick to owners of Apple products in general.

Since this is an iPhone post, I have a quota to meet around a certain subject (bear with me). The real shocker in Jeff’s post isn’t that the iPhone OS is less messy, or that having many apps is good, it’s that the oligarchy of the mobile phone market neutralized Apple’s flaws. Jeff is, like me, pacified with what the phone offers him, but he shouldn’t be happy with why that is. The carriers use pricing models fit for Kafka. They collaborate with mobile phone vendors to lock you into their platform under both their and the carrier’s control scheme. That this balance is apparently shifted towards the mobile phone end seems like a good development, until you realize that the carriers are working to reverse this and that you’re screwed either way.

Jeff ostensibly cares about software freedom in that he calls Macs expensive Mac OS X dongles. Mac OS X is infinitely more open than the iPhone OS in that while they don’t want you to install any of them on an arbitrary device, you can develop and use whatever you want for Mac OS X. Is his excuse seriously that the mobile phone market is already so far down the crapper that he doesn’t have to change his position?

by Jesper at June 28, 2009 06:39 PM

June 27, 2009

Uli's Web Site

Odd Post-WWDC 2009 Links

After WWDC, I'm hopelessly behind on my RSS feeds, so I thought I'd just post a few quick links I've seen, been pointed at or otherwise considered interesting, without much comment. Well, with as close to "without much comment" as I'll ever get ;-) (...)

June 27, 2009 11:49 AM

waffle

Worms for Mac

The confusingly named “Worms 2: Armageddon” (Worms 2 and Worms Armageddon were earlier, consecutive games at the series’ prime) was sneakily announced for Mac OS X in the description of its Xbox Live Arcade trailer. (Fire (again) and vertical levels? Neat.)

As someone who spent time trying to get one or the other variation of the great 2D Worms running under VMWare Fusion, I can appreciate that.

by Jesper at June 27, 2009 10:40 AM

Uli's Web Site

The Soul of a Macintosh Program

The Double-XX manual contains this choice bit of insightfulness in a chapter called The Soul of a Macintosh Program : In 1984 Macintosh pioneered a new way for computer programs to interact with the user. No longer (...) is the human user the slave of the computer, responding passively to computer demands (...). Instead the computer sits passively docile with your document shown on the screen pretty much as it will look in the finished output, and the user can freely choose to select and modify any part of it, mostly by clicking and dragging the mouse to directly manipulate the components of the document itself, or else to select any of the available menus to do what the user chooses (even if not related to the document, as in a desk accessory), uncoerced by any computer prompts. It later continues: (...) the Mac menus are always there, ready to be selected at any time. If a particular menu selection is totally inappropriate to some context, it is grayed out so it cannot be selected, but it is still there to give the user a comfortable feeling of familiarity. Only when the context is radically changed is the menu bar altered to reflect the new context - but even then the Apple, File, and Edit menus remain friendly landmarks in possibly unfamiliar territory. This is Macintosh, and Double-XX supports the concept fully. (...)

June 27, 2009 08:49 AM

waffle

Nosy Style Guide, Fit the First

Today’s short AppleInsider report, speculating on iMac price drops:

Roughly six week later at its annual developers conference, the Cupertino-based company announced price cuts of between $100 – $300 on its 13- to 17-inch unibody aluminum notebooks, all of which are now dubbed MacBook Pros.

Let’s see:

  • Every AppleInsider report must refer to Apple as “Apple”, “the [Mac, iPhone or iPod] maker” and “the Cupertino-based company“. One assumes that this is a habit acquired from fine journalism. AppleInsider might actually be high-end, and periodically unusually correct, for a rumors site. My guess is that they’re trying to fecundate this impression by making every article read as if the New York Times took the time to cover just this one story from the otherwise fetid heap. That includes using varying appellations, which in the New York Times helps add context, but in AppleInsider looks formulaic, which it is, and ridiculous, which it gets.

  • “[Price] cuts of between $100 – $300″. Bzzt. “[Price] cuts of between $100 and $300″, or “price cuts of $100 — $300″. (That’s also an em dash in my correction; paging Dr. Gruber…) If you use “between”, also using a range makes it tautological.

  • This one sentence references elapsed time (since a previous AppleInsider report), the venue of an announcement (WWDC), the location of the company (by appellation), the announcement itself (price cuts, the size range of the price cuts, the renaming of the MacBook) and the range of the affected products. It’s dense with information, but how much is actually relevantly connected, and how much is just newspaper-y looking text strung together? Simpler and therefore less copy is better, but this could have been extended to two or three sentences for clarity, and some detail could have been folded into a link; say, to AppleInsider’s copious coverage of the announcement. They are selling ads, after all.

But wait, there’s more!

iMacs were just recently refreshed in March but will see another update by fall, at which time they’ll also be repositioned as more affordable offerings. Apple is reportedly mulling similar 7% – 10% price reductions alongside the introduction of those models, people familiar with the company’s thinking say.

  • If the relative price reduction is important, and you just listed the range of absolute price reductions on the MacBook Pro series, why not compare the putative relative price reduction (for the iMac) with the actual relative price reduction (for the MacBook Pro)? The two are different creatures, but there are obvious comparisons to be drawn and ways to analyze it.

    Off the cuff: If the MacBook Pro is a high-end product with, likely, higher margins and they slashed the price by way less than 7%, that’d invalidate the theory (or make the move more “daring”); if they slashed the price by way more than 7%, that indicates that lowering the margin to that extent is something they’re willing to do.

  • Even without the big drop in price, you report this much as fact: “[the iMac line] will see another update by fall, at which time they’ll also be repositioned as more affordable offerings”. So they will seem more affordable. But if they won’t drop it by quite as much as 7% — 10% (that part’s reported as conjecture), what other lower drop could they pick? That’s not explored, even in passing, and this is supposedly the fact end of the stick.

(How’s my driving? More or less of this sort of thing?)

by Jesper at June 27, 2009 12:12 AM

June 26, 2009

waffle

A Whiter Shade of Pale

I still think “I Want You Back” is Michael Jackson’s biggest triumph. I never really liked his style as an adult (to be honest, I like his sister Janet’s songs better), but it takes a lot for someone as young as Michael — and the rest of the Jacksons — to pull this off at this age. I hope it had less of a role in later turning him into a weird Peter Pan-ish guy than everyone and their psychiatrist friends seem to believe.

Yeah, it’s cute. But that doesn’t mean it’s not a wonderful musical composition, executed with passion and in style, that could give the better part of the iTunes Top 100 (including today’s) a handsome run for its money.

by Jesper at June 26, 2009 07:04 PM

NSBlog

Friday Q&A 2009-06-26: Type Qualifiers in C, Part 1

Welcome back to another warm and fuzzy edition of Friday Q&A. This week I'm going to discuss the use of type qualifiers in C, a subject suggested by Nate Vander Wilt.
(Read More)

June 26, 2009 04:11 PM

Cocoa Is My Girlfriend

UITabBarController with UINavigationController Using Interface Builder

I’ve seen a good bit of sample code that shows how to implement using a UINavigationController in a view controller that is managed by a UITabBarController, but I haven’t seen much on how to do it with Interface Builder. Turns out that it’s pretty simple and I’m going to show you how.

TabBarNavigator Demo Project

To get started, create a new project in Xcode. Select the Tab Bar Application template and click Choose….

newprojectsmall

Name the project, TabBarNavigator and click Save

TabBarNavigator Project

TabBarNavigator Project

Change The Controller Type

  1. In Xcode, expand the Resources group in the Groups and Files view and double-click MainWindow.xib to open the xib in Interface Builder

  2. When Interface builder opens, change the xib View Mode to the tree view mode and expand your Tab Bar Controller item.
    MainWindow.xib

  3. Make sure that the Tab View Controller object is selected. In the object inspector switch to the Attributes tab and change the Class for the First controller from View Controller to Navigation Controller.
    Select Navigation Controller

  4. You will notice back in the MainWindow.xib window that the view controller for First View is now a UINavigationController. Expand the UINavigationController item now.
    New UINavigationController

  5. Now select the View Controller that is a child of our new UINavigationController. Click on the Identity tab of the object inspector and change the Class to FirstViewController.
    First View Controller Identity

  6. Click on the Navigation Item that is a child of the First View Controller and change the Title in the Attributes tab of the object inspector to “First”.
    Navigation Item Attributes

  7. Save your changes in Interface Builder, switch back to Xcode and Build & Go. You should see something like the following in your First view tab.
    First View in Simulator

  8. Notice that there is a Toolbar visible at the bottom of the first view. If you would like to hide this, go back into Interface Builder and select the Navigation Controller In the object inspector, uncheck the Shows Toolbar checkbox.
    Uncheck Shows Toolbar

Pushing a New Controller

Before we create a new view controller, let’s set up our FirstViewController to create an event that will trigger pushing the new controller. Since our FirstView was not created with a xib, let’s create one and then add actions and outlets to it.

  1. In Xcode, right-click or ctrl-click the Resources group and select Add | New File…. In the ensuing dialog, select View XIB in the User Interface section of iPhone OS templates and click Next. Name the XIB FirstView and click Finish.

  2. Double-click the new XIB to open it in Interface Builder and select the Identity tab after you have made sure that File’s Owner is selected. Change the Class field to FirstViewController.

  3. Ctrl-click and drag a connection from the File’s Owner object to the View object in the main window for our XIB and select view in the ensuing pop up menu.

  4. From the Object Library drag and drop a button onto the view. Double-click the button and give it the title “Push”. Save your changes in Interface Builder and switch back to Xcode.
    "Push" Button

  5. Open the file FirstViewController.h and add an action so that the header looks like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    #import <UIKit/UIKit.h>
     
    @interface FirstViewController : UIViewController {
     
    }
     
    - (IBAction)push:(id)sender;
     
    @end
  • Open the file FirstViewController.m and implement the action so that the code looks like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    #import "FirstViewController.h"
     
    @implementation FirstViewController
     
    - (IBAction)push:(id)sender
    {
     
    }
     
    - (void)dealloc {
        [super dealloc];
    }
     
    @end

    Note: You can safely delete all of the view controller template code which I have done in this example. Don’t worry. You won’t hurt anything.

  • We will come back to the push implementation after we have created our new view controller that we are going to push.

    Create the New View Controller

    We are now going to create the new view controller that will be displayed when we tap our “Push” button in the FirstViewController.

    1. In Xcode, right-click or ctrl-click the Classes group and select Add | New File…. In the ensuing dialog, select UIViewController subclass in the Cocoa Touch Class category of the iPhone OS templates. Make sure With XIB for user interface is checked and click Next. Name the file NewViewController.m and click Finish.

    2. For organization purposes, move the newly created NewViewController.xib file into the Resources group. Then double-click it to open it in Interface Builder.

    3. In Interface Builder, drag a UILabel onto the view and give it the title “New View Controller”. Save the file and switch back to Xcode.

    4. Open the file FirstViewController.m again and implement controller push code so that it looks like this:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      #import "FirstViewController.h"
      #import "NewViewController.h"
       
      @implementation FirstViewController
       
      - (IBAction)push:(id)sender
      {
        NewViewController *controller = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];
        [[self navigationController] pushViewController:controller animated:YES];
        [controller release], controller = nil;
      }
       
      - (void)dealloc {
          [super dealloc];
      }
       
      @end

      Don’t forget to #import “NewViewController.h”

    5. To connect our button to the push action, double-click the FirstView.xib file in the Resources group to open it in Interface Builder.

    6. Ctrl-click and drag a connection from the “Push” button in our view to the File’s Owner object and select push: in the ensuing pop up menu. Save your changes in Interface Builder. Switch back to Xcode and Build & Go.

    7. Additionally, you can give your New View Controller a title by implementing -viewDidLoad. Just open your NewViewController.m file and uncomment the -viewDidLoad code making it look like the following:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      
      #import "NewViewController.h"
       
      @implementation NewViewController
       
      - (void)viewDidLoad {
        [self setTitle:@"New View"];
        [super viewDidLoad];
      }
       
      - (void)dealloc {
        [super dealloc];
      }
       
      @end

    New View Controller

    And that’s pretty much all there is to it. There are a lot of steps here, but I think you’ll see that setting up your application to implement both a UITabBarController and a UINavigationController is pretty straight forward.

    Conclusion

    How best to organize your views in an iPhone application is a design process. Often applications will have a lot of data for your users to access and it will be up to you to find the best way to organize it. Being able to utilize both a tab bar and a navigation controller can really help in this process. Until next time.

    TabBarNavigator Demo Project

    by Matt Long at June 26, 2009 03:18 AM

    June 25, 2009

    waffle

    Stoned Wallaby Crop Circles

    You heard me: stoned wallaby crop circles.

    “The one interesting bit that I found recently in one of my briefs on the poppy industry was that we have a problem with wallabies entering poppy fields, getting as high as a kite and going around in circles,” Lara Giddings told the hearing. “Then they crash,” she added. “We see crop circles in the poppy industry from wallabies that are high.”

    Reality. It’s a wonderful thing.

    by Jesper at June 25, 2009 07:34 PM

    June 24, 2009

    waffle

    Curtains

    In an imaginary parallel universe where everything is the same but the Pirate Party doesn’t exist, I’d still be slightly optimistic about how the entire piracy vs private industry abuse fight has been going lately.

    Three, or maybe four or five of you, may not have already heard how Jammie Thomas-Rasset was sentenced to pay $1.92 million for having shared, and thus infringed on the copyrights on, 24 songs. This sum — $80,000 per song — is so unsubstantiatedly princely that even one of the artists involved in the evidence who happens to agree with the verdict on principle publicly shames the claim and distances himself from these shenanigans.

    The purpose of the ridiculous sums are two-fold:

    a) To set an example and coerce people out of widespread behavior
    and, to a lesser extent,
    b) To recuperate some of the losses from an alleged statistically reliable, obvious and therefore missed conversion from “illicit download” to “karma friendly purchase”.

    Both branches are demonstrably wrong. Let’s take a), the technical progress that enabled the behavior to appear can’t be rolled back, and people aren’t scared of being sued; in the extreme cases where they are, they are also either forgetful or resourceful. The funny business will continue no matter what happens.

    As for b), this subdivides further. The conversion may look obvious on paper given that the business model is established, but there are several possibilities. We all know by now that there’s no guarantee that any download would have resulted in a purchase or for that matter even precluded a purchase. There’s no model to calculate an accurate number, so one has to be pulled from… somewhere.

    And we also know that for the purported losses, which can’t be proven to not be imaginary, to be recuperated, if the given amounts are correct, they’d have to end up with way more money. The road towards that goal forks; either you search higher damages (which seems unlikely, given the unrest in everyone involved, including the judge, in accepting the sum as ‘fair’ already) or you file more cases, which to approach the sufficient volume would hold up courts that could have been righting more egregious wrongs, and which is practically hard since bringing each case involves effort and attorneys.

    There may also be a slight pulling sensation in your public perception.

    Let’s say even that the music industry is right: the claims are substantiated, and the sums, in the face of the sheer heinousness of the crime, are actually fair. Now what are they going to do? They’ve still got practicality, public opinion and increasingly their own pool of artists against them.

    And artists aren’t fools. Some, maybe even most (it’s hardly a homogenous group), may feel threatened by the direction reality is heading. But many are taking precautions, and many more still are actually being made tenable by the recent development. Did you know that when you buy a cassette tape or a CD-R in Sweden, a small portion of the price goes, ear-marked, to a special music industry organization for composers? Did you know that, for anyone to cash in on that, and by way of being a large organization named in a prominent law, in effect calling yourself a composer in the first place because you’ll have trouble and be at a competitive disadvantage selling anything otherwise, you’d have to join that organization and accept their terms? And did you know that up until very recently, that blocked you from placing your works under Creative Commons-like licenses in order to explicitly allow things like remixes and sampling which not only has the potential of spreading your work and giving you more consumers and customers, but actually on a macro scale, thanks to the way the human mind works, is what creation is about in the first place?

    I’m thinking about how this will play out into the future and it occurs to me that the wins are pyrrhic and elusive. The industry actually hurts less when they’re awarded losses. A reasonable win is too hard-fought and brings in too little money; other wins just turn your putative customers into new plaintiffs because they don’t want to deal with cold and bloodthirsty. The artists themselves are already headed en masse for the ejector chair and the good ship Independent.

    At some point, either politicians are going to have to be lured into penning the industry more and more odious laws (which, per previous paragraphs, won’t actually solve anything but will happily trash the system while waiting, and per the very first paragraph and related efforts will only actually work for so long), or the matter will have to be dropped. Lumps taken. Sour medicine tasted. Lesson learned. Democracy restored. Lemonade, finally, made.

    by Jesper at June 24, 2009 11:57 PM

    Cocoa Is My Girlfriend

    Launching: If it ain’t broke, don’t fix it

    This is the first in a short series on my adventures getting my software out the door. Rather than this first lesson be a lesson in what to do, here’s what not to do.

    Don’t order a DSL upgrade 7 days before your ship date.

    Yes, indeed. Last Wednesday morning, Qwest “upgraded” our DSL to 7Mbps. From then until Saturday lunchtime, I had 13 hours of solid connectivity, most of which I was asleep for. It did indeed get resolved and I am enjoying the new speed but it certainly wasn’t worth stress of trying to find connectivity while trying to ship. I went to Barnes and Noble, Krispy Kreme and the seminary library in search of the uplink. I also wasted a trip to Best Buy to get a new DSL modem which didn’t solve the issue, despite the Qwest tech’s assurances that it would. (That reminds me, I need to return it and get my $75 back.)

    Thinking about it, my rule should be generalized as:

    Don’t replace/upgrade/otherwise mess with any of your critical infrastructure within two weeks of your ship date unless it’s broke.

    After being burned by the DSL outage, I left well alone newly released updates to XCode, PHP, Safari and Java. I’ll get to those at a less critical moment.

    Well, lesson learned. I won’t be doing that again. Now if you’ll excuse me, I’m gonna download a movie from Netflix.

    by Fraser Hess at June 24, 2009 10:54 PM

    June 23, 2009

    Cocoa With Love

    Verifying that a string contains an email address using NSPredicate

    To celebrate the official release of iPhone OS 3.0 this week, I will show you how to verify that an NSString contains a syntactically valid email address using NSPredicate — a class that joins the iPhone SDK 3.0 as part of the Core Data additions. This code will work on Mac OS X too since, as with the rest of Core Data, NSPredicate has been part of Mac OS X since 10.4 (Tiger).

    Before I begin...

    I gave an interview to Anthony Agius of MacTalk.com.au this week. You can download the MP3 from their website. I'm hesitant to listen to my own voice but I think I talked about what it's like to be an independent Mac/iPhone developer in Melbourne, Australia.

    Back to predicates

    In programming, a predicate is a condition that returns true or false if the object it processes has the properties that the predicate describes. The key difference between a predicate and a regular boolean expression is that a predicate only considers the properties of one object, where a boolean expression may consider multiple, unrelated objects.

    Many programmers are familiar with predicates as used in SQL database queries. For example a query to extract the complete row from the "people" database table for every person named "John Smith" might look like this in SQL:

    SELECT * FROM people WHERE firstname = 'John' AND lastname = 'Smith'

    Everything after the "WHERE" is the predicate — it looks at properties of the row only and is either true (the row will be extracted) or false (the row will be ignored).

    Using NSPredicate to evaluate predicates

    In Cocoa, NSPredicate works in much the same way as the "WHERE" clause of SQL. The main reason that NSPredicate is being brought to the iPhone is that NSPredicate fulfils the same role in Core Data that "WHERE" clauses fulfil in SQL — to allow the persistent store to fetch objects that satisfy specific criteria.

    Imagine we had an NSDictionary created using the following method:

    - (NSDictionary *)personRowWithFirstname:(NSString *)aFirstname
        lastname:(NSString *)aLastname
    {
        return
            [NSDictionary dictionaryWithObjectsAndKeys:
                aFirstname, @"firstname",
                aLastname, @"lastname",
            nil];
    }

    we could test if a given row created by this method matched the predicate "firstname = 'John' AND lastname = 'Smith'" with the following:

    // given an NSDictionary created used the above method named "row"...
    NSPredicate *johnSmithPredicate =
        [NSPredicate predicateWithFormat:@"firstname = 'John' AND lastname = 'Smith'"];
    BOOL rowMatchesPredicate = [johnSmithPredicate evaluateWithObject:row];

    The string format used to construct an NSPredicate in Cocoa is very similar to the syntax of the "WHERE" clause in SQL. You can also construct this NSPredicate in code by building it from two NSComparisonPredicates and an NSCompoundPredicate.

    A more common use of NSPredicate is filtering — extracting rows that match an NSPredicate from a larger collection:

    // given an NSArray of rows named "rows" and the above "johnSmithPredicate"...
    NSArray *rowsMatchingPredicate = [rows filteredArrayUsingPredicate:johnSmithPredicate];

    This is then more like an SQL query where we have selected matching rows from the larger table of data.

    Note: NSPredicate handles filtering only. If you'd like to replicate SQL's "ORDER BY" clause, you can apply NSSortDescriptor as a separate step.

    Verifying an email address

    The "LIKE" comparison operator in NSPredicate (NSLikePredicateOperatorType) is commonly used as a convenient means of testing if an NSString matches a Regular Expression. It's advantage over full libraries with greater options and replacement capability is that it is already in Cocoa — no libraries, no linkage, no hassle.

    To test if an NSString matches a regular expression, we can use the following code:

    NSPredicate *regExPredicate =
        [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regularExpressionString];
    BOOL myStringMatchesRegEx = [regExPredicate evaluateWithObject:myString];

    The only question that remains is: what is a regular expression that can be used to verify that an NSString contains a syntactically valid email address?

    NSString *emailRegEx =
        @"(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
        @"~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"
        @"x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-"
        @"z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5"
        @"]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-"
        @"9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21"
        @"-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";

    This regular expression is adapted from a version at regular-expressions.info and is a complete verification of RFC 2822.

    This adaptation involved escaping all backslashes with another backslash (otherwise the NSString will try to interpret them before they are used in the Regular Expression) and escaping the ampersands (&) so it isn't interpreted as a Unicode escape sequence and caret (^) characters because — actually I have no idea why except that the expression wouldn't parse without it.

    The linked regular-expressions.info page recommends using slightly different regular expressions that force the top-level domain to be a country code or a known top-level domain. With the number of top-level domains due to increase in the near future, I'm not sure this is a good constraint to impose — since this check isn't intended to verify that the provided domain name is valid.

    Since this regular expression NSString is so long, I've split it over 7 lines. This is an underused feature in Standard C languages — if you split a string into pieces but put nothing except whitespace between the pieces, the compiler will treat it as one continuous string. You don't need to write an extremely long string on a single long line.

    Conclusion

    Just a few lines of code this week but I thought it would be good to draw attention to one of the minor additions making its way to the iPhone in SDK 3.0. I use NSPredicate all the time on Mac OS to perform searches, extract subarrays and perform quick Regular Expression tests. Without it, the only alternatives on the iPhone were methodical array iterations and manual comparisons in code.

    by Matt Gallagher (noreply@blogger.com) at June 23, 2009 06:35 AM

    Red Sweater Blog

    FastScripts 2.4

    FastScripts 2.4 is out, with an important change in the evaluation terms.

    Use FastScripts for free, for as long as you like. All features are enabled and you may define up to 10 keyboard shortcuts. If you decide you want unlimited keyboard shortcuts, purchase a license to remove that limitation.

    I have been thinking for some time of eliminating FastScripts Lite. Customers found it confusing to differentiate between the versions, and I found it tedious to artificially maintain two versions. With the new, liberal evaluation terms in FastScripts 2.4, all of the old Lite functionality and much more is now included for free in the full version.

    This new version also includes a software update mechanism so you can be sure to stay up to date with new releases as I update the application. So whether you’re an existing paid user, or a Lite user, be sure to download this release!

    If you’ve been thinking of giving FastScripts a try, now is a great time to give it a spin. I hope you will enjoy what it has to offer!

    by Daniel Jalkut at June 23, 2009 12:25 AM

    June 22, 2009

    ridiculous_fish

    I Didn’t Order That, So Why Is It On My Bill, Episode 1

    C++ was designed to ensure you "only pay for what you use," and at that it is mostly successful. But it’s not entirely successful, and I thought it might be interesting to explore some counterexamples: C++ features you don’t use but still negatively impact performance, and ways in which other languages avoid those issues.

    So here’s the first episode of I Didn’t Order That, So Why Is It On My Bill, to be continued until I can’t think of any more.

    Inline Functions

    The C++ standard says this:

    A static local variable in an extern inline function always refers to the same object.

    In other words, static variables in inline functions work just like static variables in any function. That’s reasonable, because that’s probably what you want. That’s what statics are for, after all.

    But wait, it also says this:

    An inline function with external linkage shall have the same address in all translation units.

    That’s borderline silly. Who cares what the address of the function is? When’s the last time you used the function pointer address for anything except calling it? Dollars to donuts says never. You aren’t using a function pointer as a hash table key. You aren’t comparing the same function via pointers from different files. And if you did, you’d probably have the good sense to ensure the functions are defined exactly once (i.e. not inline). You can easily live without this feature.

    So hopefully I’ve established that you don’t use this feature. Now I have to show how you’re paying for it anyways. Well, this feature complicates linking. Ordinarily, there should be only one copy of a function, and if the linker finds more than one it gives you a multiple definition error. But with inline functions, the compiler is expected to generates multiple copies of the code and it’s up to the linker to sort it out. See vague linkage.

    So the linker has more work to do, and link time is increased. Again, who cares? Linking occurs after compilation, and I promised you a runtime cost. But ah – link time is runtime when we’re using dynamic libraries!

    Every time you start up a program, the linker has to make sure that your implementation of vector<int>::capacity() has the same address as the vector<int>::capacity() defined in libWhoGivesAHoot.dylib just in case you decide to go and compare their function pointers.

    And it gets a little worse. You know how class templates have to live in header files? The function definition goes right there in the class declaration, and that makes them inline automatically. So nearly every function in a template gets this inline uniquing treatment. Every template function in a class that cannot or should not be inlined – because its address is taken, because it is recursive, because you’re optimizing for size, or simply because it’s too darn big and inlining is counterproductive – will incur a launch time cost.

    The situation is dire enough that gcc added a "-fvisibility-inlines-hidden" flag. This is a minor violation of the C++ standard, but will improve launch time in cases with a lot of dynamic libraries. In other words, this flag says: I’m not using this feature, please take it off my bill.

    C does not have this problem. Why not? C (in the C99 standard) has very different semantics for inline functions. To oversimplify a bit, a C99 inline function acts as sort of an alias for a real function, which must be defined once and only once. Furthermore, inline functions can’t have static variables, or have their addresses taken. This approach is more limiting, but doesn’t require the linker to bend over backwards, and so avoids the performance penalty of the C++ approach.

    by admin at June 22, 2009 09:32 PM

    June 20, 2009

    waffle

    Piraten, Bitte

    Following this week’s atrocious sell-out of freedoms and human rights in Germany under the pretense of “thinking of the children”, Social Democrat and Bundestag representative Jörg Tauss has resigned from the party in protest. He will now join Piratenpartei, the German Pirate Party, and act as the first Pirate Party national parliament representative ever, although not as elected from that party.

    In the interests of covering every angle, there’s more that seems relevant to the story. One of the traditional crutches to sell the populace on the importance of censorship has been to prevent the spread of child pornography. Indeed, this week’s law was largely pushed by the motive that censorship will prevent the spread of child pornography, even if such a benefit would come at the cost of fundamental freedoms and be completely foreign to an open, modern democracy. However, Wikipedia’s Jörg Tauss article reveals some interesting prior entanglement.

    I haven’t even heard of Jörg Tauss before today, so I’m not going to guess one way or the other. Chances are that Herr Tauss is opposing the censorship law for all the right reasons, just like the majority of the German electorate are; it’s in fact very likely. But given the background, a distant possibility becomes stronger: I can’t rule out that Tauss is guilty of the charges and is trying to save his own bacon by adopting a sensible position for his own gain. In that case, it’s regrettable, and a sad entry in the row of historic “firsts” that Pirate Parties have been stacking up lately. I know nothing, however, but I don’t want to be coy and selective in my reporting.

    by Jesper at June 20, 2009 07:58 PM

    June 19, 2009

    waffle

    And Now, It’s Time for the Future

    And that’s all I’m going to say about that for now.

    by Jesper at June 19, 2009 08:54 PM

    Cocoa With Love

    Revisiting an old post: Streaming and playing an MP3 stream

    Given the attention it received and the number of bugs I know it contained, I wanted to revisit an old post of mine: Streaming and playing an MP3 stream. In this post, I'll talk about the problems the original contained, how I fixed those problems and I'll present the updated result.

    Introduction

    Last September, I wrote a post titled "Streaming and playing an MP3 stream". The post was largely an experiment — I just wanted to see if I could play a streaming MP3 by quickly adapting Apple's AudioFileStreamExample to accept an HTTP data stream.

    Unexpectedly, the post became one of my most popular. The attention quickly revealed the limitations in my approach:

    • The blend of Objective-C and C was muddled and led to a situation where neither were being used cleanly.
    • The boolean flags I copied from the original example were a bad way to describe the playback state and lots of situations were not covered by these flags.
    • Sending notifications to the user-interface on a thread that isn't the main thread causes problems.
    • The extra thread I added (the download thread) was never thread-safe.

    I've finally decided to take the time to present a solution to these issues and present an approach which is a little more robust and a little easier to extend if needed.

    You can download the complete AudioStreamer project as a zip file (around 110kB) which contains Xcode projects for both iPhone and Mac OS. You can also browse the source code repository.

    Limited scope

    One point should be clarified before I continue: this class is intended for streaming audio. By streaming, I don't simply mean "an audio file transferred over HTTP". Instead, I mean a continuous HTTP source without an end that continues indefinitely (like a radio station, not a single song).

    Yes, this class will handle fixed-length files transferred over HTTP but it is not ideal for the task.

    This class does not handle:

    1. Buffering of data to a file
    2. Seeking within downloaded data
    3. Feedback about the total length of the file
    4. Parsing of ID3 metadata

    These things often can't be done on streaming data, so this class doesn't try. See the "Adding other functionality" section for hints about how the class could be reorganised to handle some of these features.

    Taking code out of C functions

    Since I had borrowed the AudioFileStream and AudioQueue callback functions from Apple's example, they were Standard C.

    My first change was to make these 6 callback functions (7 including the CFReadStream callback) little more than wrappers around Objective-C methods:

    void MyPacketsProc(
        void *inClientData,
        UInt32 inNumberBytes,
        UInt32 inNumberPackets,
        const void *inInputData,
        AudioStreamPacketDescription *inPacketDescriptions)
    {
        // this is called by audio file stream when it finds packets of audio
        AudioStreamer* streamer = (AudioStreamer *)inClientData;
        [streamer
            handleAudioPackets:inInputData
            numberBytes:inNumberBytes
            numberPackets:inNumberPackets
            packetDescriptions:inPacketDescriptions];
    }

    At a compiled code level, this is a step backwards: all I've done is slowed the program down by an extra Objective-C message send.

    Technically, a C function that takes a "context" pointer (like the inClientData pointer here) is not significantly different to a method. What a method does is makes data hiding and data abstracted actions easier. Within a method, you can easily access the instance variables of an object and you don't need to explicitly pass context into each function.

    This is the cliché argument in favor of object-orientation — but it isn't why I reorganized these functions and methods.

    The honest reason why I did it is aesthetics: it is easier to read a class that is implemented using Objective-C methods alone — it's more consistent. I chose to move towards an Objective-C aesthetic and away from the Standard C aesthetic of the CoreAudio sample code to promote consistent formatting, consistent means of accessing state variables, consistent ways of invoking methods and consistent ways of synchronizing access to the class.

    Describing state

    With the majority of code now inside the class, I was in a better position to start handling changes through methods rather than direct member access.

    My original approach to state came from Apple's original example. This example had just one piece of state: a bool named finished (which indicated that the run loop should exit).

    The problem with this flag is how simple it is. It is unable to distinguish between the following:

    1. End of file, normal automatic stop.
    2. The user has asked the AudioStreamer to stop but the AudioQueue thread has not yet responded.
    3. An error has occurred before the AudioQueue thread is created and we must exit.
    4. We are stopping the AudioQueue for temporary reasons (clearing it, changing device, seeking to a new point) but we don't want the loop to stop.

    For Apple's example, there was no problem: the first case was the only one that ever occurred.

    As a hasty solution, I had added started and failed flags but these really only covered the first and third case adequately.

    In the end, I realized that the AudioStreamer needed much more descriptive state where every combination of progress within each thread had a different position:

    typedef enum
    {
        AS_INITIALIZED = 0,
        AS_STARTING_FILE_THREAD,
        AS_WAITING_FOR_DATA,
        AS_WAITING_FOR_QUEUE_TO_START,
        AS_PLAYING,
        AS_BUFFERING,
        AS_STOPPING,
        AS_STOPPED,
        AS_PAUSED
    } AudioStreamerState;

    and when stopping, one of the following values would also be needed:

    typedef enum
    {
        AS_NO_STOP = 0,
        AS_STOPPING_EOF,
        AS_STOPPING_USER_ACTION,
        AS_STOPPING_ERROR,
        AS_STOPPING_TEMPORARILY
    } AudioStreamerStopReason;

    In this way, the state always describes where every thread is and the stop reason explains why a transition is occurring.

    Combining this with an error code that replaces the old failed flag, I now have a complete desription of the state.

    By cleaning up the state of the object, I was able to make the object capable of state transitions that weren't previously possible including pausing/unpausing and returning to the AS_INITIALIZED state after a stop (instead of requiring that the class be released after stopping).

    Notifications

    In the old version of the project the only way for the user-interface to follow the playback state was to observe the isPlaying property on the object which reflected the kAudioQueueProperty_IsRunning property of the AudioQueue.

    This observing was handled through KeyValueObserving. I'm a big fan of KeyValueObserving for its simplicity and ubiquity but this was not the correct place to use it.

    KeyValueObserving always invokes the observer methods in the same thread as the change. Since all changes in AudioStreamer happen in secondary threads, this means that the observer methods were getting invoked in secondary threads.

    Why is this bad? A minor drawback is simply the unexpectedness for the observer but the biggest reason was that the sole purpose of observing this property was to update the user-interface and the user-interface on the iPhone cannot be updated from any thread except the main thread. Even on the Mac, performing updates off the main thread can have unexpected and glitchy results.

    The solution is to retain the NSNotificationCenter of the thread that first calls start on the object and use this center to send messages as follows:

    NSNotification *notification =
        [NSNotification
            notificationWithName:ASStatusChangedNotification
            object:self];
    [notificationCenter
        performSelector:@selector(postNotification:)
        onThread:[NSThread mainThread]
        withObject:notification
        waitUntilDone:NO];

    Don't invoke postNotification: directly from the secondary thread as, like most methods, it is not thread safe and it could be in use from the main thread.

    Thread safety

    Despite adding an extra thread on top of Apple's AudioFileStreamExample, I never really spent any time thinking about thread safety — a reckless approach to stability. In my defence Apple's example wasn't exactly cautious with its threads and would quit while the AudioQueue's thread was still playing the last buffer.

    The most efficient approach to threading is to carefully enter @synchronized (or NSLock or pthread_mutex_lock) in a tight region around any use of a shared variable.

    Unfortunately for the AudioStreamer class, almost everything in the class is shared. Instead, I decided to go for the decidedly less efficient approach of running almost everything in the class within a @synchronized section, emerging only at points when control must be yielded to other threads.

    The drawback is that the code rarely runs simultaneously on multiple threads (although threading here is for blocking and I/O, not for multi-threaded performance reasons so that's not a probem). The advantage with this heavy-handed locking approach is that the only threading condition that may cause problems are deadlocks.

    When do deadlocks occurs? Only when you're waiting for another thread to do something while you're inside the synchronized section needed by that other thread. The simple solution: never wait for another thread inside a synchronized section.

    AudioStreamer has three situations where 1 thread waits for another:

    1. The run loop (the AudioFileStream thread waits for any kind of control communication from the main thread or playback finished notification from the AudioQueue thread).
    2. The enqueueBuffer method (AudioFileStream thread waits for the AudioQueue thread to free up a buffer).
    3. Synchronous AudioQueueStop invocations (waits for the AudioQueue to release all buffers).

    The first two points are easy: perform these actions (any any method invocation which invokes them) outside of the @synchronized section.

    The final point is harder: the synchronous stop must be performed inside the @synchronized section to prevent multiple AudioQueueStop actions occurring at once. To address this, the release of buffers by the AudioQueue (in handleBufferCompleteForQueue:buffer:) must perform its work without entering the @synchronized section (although it's allowed to use the queueBuffersMutex as normal since that isn't used by anything else during a synchronous stop).

    Of course, every time the @sychronized section is re-entered, a check must be performed to see if "control communication" has occurred (the class checks this by invoking the isFinishing method and exiting if it returns YES).

    Adding other functionality

    Get metadata

    The easiest source of metadata comes from the HTTP headers. Inside the handleReadFromStream:eventType: method, use CFReadStreamCopyProperty to copy the kCFStreamPropertyHTTPResponseHeader property from the CFReadStreamRef, then you can use CFHTTPMessageCopyAllHeaderFields to copy the header fields out of the response. For many streaming audio servers, the stream name is one of these fields.

    The considerably harder source of metadata are the ID3 tags. ID3v1 is always at the end of the file (so is useless when streaming). ID3v2 is located at the start so may be more accessible.

    I've never read the ID3 tags but I suspect that if you cache the first few hundred kilobytes of the file somewhere as it loads, open that cache with AudioFileOpenWithCallbacks and then read the kAudioFilePropertyID3Tag with AudioFileGetProperty you may be able to read the ID3 data (if it exists). Like I said though: I've never actually done this so I don't know for certain that it would work.

    Stream fixed-length files

    The biggest variation you may want to make to the class is to download fixed-length files, rather than streaming audio.

    To handle this, the best approach is to remove the download from the class entirely. Download elsewhere and when "enough" (an amount you should determine on your own) of the file is downloaded, start a variation of the class that plays by streaming from a file on disk.

    To adapt the class for streaming from a file on disk, remove the CFHTTPMessageRef and CFReadStreamRef code from openFileStream and replace it with NSFileHandle code that uses waitForDataInBackgroundAndNotify to asynchronously stream the file in the same way that CFReadStreamRef streamed the network data.

    Once you're streaming from a file, you'll probably want to permit seeking within the file. I've already put hooks within the file to seek (set the seekNeeded flag to true and set the seekTime to the time in seconds to which you want to seek) — however, the mechanics of seeking within the file would be dependent on how you access the file.

    Incidentally, the AudioFileStreamSeek function seems completely broken. If you can't get it to work (as I couldn't) just seek to a new point in the file, set discontinuous to true and let AudioFileStream deal with it.

    Handling data interruptions

    At the moment, if the AudioQueue has no more buffers to play, the state will transition to AS_BUFFERING. At this point, no specific action is taken to resolve this situation — it assumes that the network will eventually resume and requeue enough buffers.

    I actually expect there will be cases where this action is insufficient — you may need to ensure that the AudioQueue is paused until enough buffers are filled before resuming or even restart the download entirely. I haven't experimented much since it is easiest with streaming audio just to stop and start new.

    Incidentally, if you're curious to know how many audio buffers are in use at any given time, uncomment the NSLog line in the handleBufferCompleteForQueue:buffer: method. This will log how many 1 kilobyte audio buffers are queued waiting for playback (when the queue reaches zero, the AudioStreamer enters the AS_BUFFERING state).

    Conclusion

    You can download the complete AudioStreamer project as a zip file (around 110kB) which contains Xcode projects for both iPhone and Mac OS. You can also browse the source code repository.

    The functionality of this new version has not changed greatly — my purposed was to present a version that is more stable and tolerant of unexpected situations, rather than add new features.

    As before, the AudioStreamer class should work on Mac OS X 10.5 and on the iPhone (SDK 2.0 and greater).

    The source repository is hosted on github so you can browse, fork or track updates as you choose. I will likely update again in future (I can't imagine I've written this much code without causing more problems) and this way, you can see the changes I've made.

    I hope this post has shown you a number of problems that can happen when code is written hastily. This doesn't mean you should always avoid hastily written code (timeliness and proof of concepts are important) but it does mean you should be practised at refactoring code and not simply slap poor fixes onto code that doesn't cleanly solve a problem in the first place.

    by Matt Gallagher (noreply@blogger.com) at June 19, 2009 06:59 PM