close
The Wayback Machine - https://web.archive.org/web/20121206061028/http://furbo.org/

iPhone 5 Scuff Remover

Removes unsightly scratches, dings and scuffs IN MERE SECONDS! *

Image

Get yours NOW!

And we think you’re going to love it.

Especially since you know who isn’t here.

* black iPhones only

Retina for Masochists

Today we released an update for xScope that supports the Retina display. As I alluded to on Episode 14 of The Talk Show, this update was harder than most. The 68k to PowerPC, Carbon to Cocoa, and PowerPC to Intel transitions were no walk in the park, but this update really kicked my butt. Here’s hoping that sharing some of the things I learned along the way will help you with your own Retina work.

For most developers who are working strictly in window points, an update for the Retina display is a fairly straightforward process. xScope, however, does a lot of work with both pixels and points. And that’s where the fun begins…

Mouse Input

The first gotcha I encountered while doing the Retina update was with mouse input using NSEvent’s +mouseLocation. The team at Apple has done some amazing work making sure output looks stunning on the Retina display, but being able to get high-resolution input is definitely lacking.

There are two problems at play here. The first is that mouse coordinates can be reported for coordinates that do not exist on any attached screen. The second is that the NSPoint does not contain enough resolution to address every pixel on screen.

To deal with the first problem, I used an NSEvent category that clamps +mouseLocation results to valid coordinates.

For the second problem, the only workable solution was to capture the +mouseLocation and then track -keyDown: events so the arrow keys can home in on the destination pixel. Yes, kids, that’s what we call a painful fricken’ hack.

Window Positioning

The next big headache was caused because you can’t set a window’s frame using non-integral points.

xScope does this a lot. The best example is with the Ruler tool: the origin of the ruler can be positioned to both even and odd pixels on screen. The red position indicators are also small windows that point to individual pixels in the ruler window.

The workaround is to make an NSWindow that’s larger than you need and then adjust the bounds origin of the NSViews it contains. The pain here is that it immediately introduces a dependency between windows and views. For example, the position indicator windows need to know if the view they’re hovering over has had its bounds origin shifted.

Pixel Alignment

There are many cases where xScope has to align to a pixel boundary. I found myself using this pattern many times throughout NSView -drawRect: code:

CGFloat backingScaleFactor = [self.window backingScaleFactor];
CGFloat pixelWidth = 1.0 / backingScaleFactor;
CGFloat pointOffset = lineWidth / 2.0;

The pixelWidth tells you how wide a single pixel is in points, while the pointOffset can be used to align a coordinate so that it straddles the Quartz drawing point:

NSBezierPath *line = [NSBezierPath bezierPath];
[line setLineWidth:pixelWidth];
[line moveToPoint:NSMakePoint(point.x + pointOffset, 0.0);
[line lineToPoint:NSMakePoint(point.x + pointOffset, 100.0);
[line stroke];

Another common pattern was to use the backingScaleFactor to align a coordinate to the nearest pixel in the view:

NSPoint point;
point.x = floor(x * backingScaleFactor) / backingScaleFactor;
point.y = floor(y * backingScaleFactor) / backingScaleFactor;

Of course you can do much the same thing with NSView’s -centerScanRect:, but in my experience it’s much more common to need aligned NSPoint values when you’re doing custom drawing. Creating an NSRect just to align the origin is a pain.

Flipped Coordinates

As Cocoa developers, we’re used to the pain and suffering caused by flipped view coordinates. Retina support can add a new dimension to this headache when you’re dealing with pixel coordinates.

Say you have a flipped NSView with an origin of 0,0 and dimensions of 1440 x 900 (e.g it covers the entire Retina screen in points.) Y coordinates on the screen can range in value from 0.0 to 899.5. When those coordinates are flipped, the range of values then become 0.5 to 900.0: which is off by a pixel (half point) if you’re trying to address the full range of view coordinates. The solution is to adjust the flipped coordinates like this:

NSRect windowRect = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 0.0, 0.0)];
NSPoint viewPoint = [self convertPoint:windowRect.origin fromView:nil];

CGFloat backingScaleFactor = [self.window backingScaleFactor];
CGFloat pixelWidth = 1.0 / backingScaleFactor;
viewPoint.y -= pixelWidth;

I’ve always wondered why there’s this odd note in the +mouseLocation documentation:

Note: The y coordinate of the returned point will never be less than 1.

Even though it’s a lie, the extra pixel does make the coordinate flip work correctly—and now you have to take care because that pixel can be a fractional point.

Summary

Hopefully my trials and tribulations will help you in your own development. The good news is that the beautiful results on the Retina display make all this hard work worthwhile.

The First Apple Channel

Dear Tech Media,

While you’re looking for meaning in the shadows of an Apple press invite, you’re missing something important: Apple is producing content for its own distribution channel.

For the month of September, Apple is letting customers view live shows through a combination of apps, the web, and Apple TV. It’s the fourth year of the iTunes Festival in London, but this is the first year that it’s been broadcast via iTunes.

Why is this important? Let’s look at what this means for the various players involved:

Artists

As an app developer, I know what it’s like to be featured by Apple in one of its promotions. It sells a lot of product. And that, in turn, funds our creative efforts.

I’m sure the featured artists will gain fans as a result of their performances. I’ve watched a few shows and have already seen some bands that I’ll be keeping my eye on.

A lot of these artists are also probably working with Apple for the first time and getting a feel for what a more direct relationship with a distributor feels like.

Customers

As a customer, I’m all too familiar with the hassles and restrictions on digital content. It’s an eye opener to be able to play this content wherever and however I want. No crap, just good shows.

Tickets for the events are also free: seeing your favorite band in a small venue where all you have to buy are the drinks? Sign me up!

Apple has chosen the artists wisely. I couldn’t care less about some of the bands, but you should have seen my niece’s eyes light up when I told her that she could watch a free One Direction show on September 20th. Talk about keeping your customers happy!

Media Industry

The iTunes Festival shows everyone above what a world without a middle man would be like. We’re loving it: they’re fearing it.

Apple

You need an iTunes account to view these shows. If you didn’t have one already, you’ll certainly get one to see your favorite band.

The best viewing experience for these shows is on a $99 Apple TV. That’s less than the cost of a couple of tickets to see the big name acts. The drinks aren’t watered down, either.

It also sets a precedent for the future. Could this be akin to HBO creating premium content for it’s subscribers? Or Netflix producing its own shows to make it’s streaming service more desirable?

Apple first got its feet wet in the content business with music in iTunes. What we’re seeing here may be the company’s first effort in the video business.

Updated September 6th, 2012: I’ve heard from several sources that last year’s iTunes Festival was an iPad-only app (with AirPlay capabilities.) Apple has taken small, calculated steps with the Apple TV platform and this is another example of that approach.

Responding to App Store Reviews

When developers talk about wanting to respond to reviews, many of them haven’t thought through the social implications of what that means. Matt Gemmell has. As Marco Arment points out, replying publicly also leaves iTunes (more) open for abuse by unscrupulous or uninformed developers.

One idea I’ve had is giving developers the ability to add a support link to a review. This helps both the developer and customer in several ways:

  • The customer who reported the problem could be notified that a support link was added to their review and would be directed to a site which is designed to help them out. This could also lead to direct contact if there are other issues to be resolved.
  • Potential customers that are reading reviews can see how a developer responds to problems. If you come across a product with lots of support links, you know that’s a developer who cares about his customers.
  • Putting customer service front and center in iTunes makes it desirable for developers to create and maintain sites that provide helpful information. There are far too many products where the customer support link just goes to a product page that’s unhelpful.

Of course, restrictions would be needed to prevent abuse of these external links. For example, Apple could decide to only allow links to a developer’s support domain. There could also be limits on the number of support links a developer has at their disposal (like promotion codes, we would then use them judiciously.)

Finally, these thoughts only cover the information we exchange with the customers publicly. I still think there are cases where private contact via email is vital.

Debugging Core Data Objects

If you’re working on an app that uses Core Data, it’s inevitable that you’ll end up in the debugger and need to dig around in the object graph. You’ll also quickly realize that Core Data’s -description of an object isn’t terribly helpful:

(lldb) po myListObj
(List *) $19 = 0x08054ac0 <List: 0x8054ac0> (entity: List; id: 0x8074280 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/List/p175> ; data: <fault>)

Core Data’s documentation is excellent, but surprisingly doesn’t cover some of the tricks you can use to examine managed objects in the debugger.

The first trick is to fire a fault on the object using -willAccessValueForKey. After that, you can see what’s really there:

(lldb) po [myListObj willAccessValueForKey:nil]
(id) $20 = 0x08054ac0 <List: 0x8054ac0> (entity: List; id: 0x8074280 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/List/p175> ; data: {
    containerId = "8E4652D3-5516-4186-B1C9-DDBE41E108CF";
    createdAt = "2009-10-08 15:17:53 +0000";
    itemContainers = "<relationship fault: 0x8020850 'itemContainers'>";
})

You might also be surprised when you try to access one of the properties of the object:

(lldb) po myListObj.containerId
error: property 'containerId' not found on object of type 'List *'
error: 1 errors parsing expression

Remember that these properties are defined as @dynamic and there’s a lot of work done by Core Data at runtime to provide the implementation. The solution here is to use the KVC accessor -valueForKey: to get the object’s value:

(lldb) po [myListObj valueForKey:@"containerId"]
(id) $26 = 0x08069b60 8E4652D3-5516-4186-B1C9-DDBE41E108CF

Often, you’ll want to examine the relationships between objects. As you can see in the output above, the attribute itemContainers, which is a to-many relationship, is a fault. To fire the fault, get all the objects from the set:

(lldb) po [[myListObj valueForKey:@"itemContainers"] allObjects]
(id) $23 = 0x0d0667b0 <__NSArrayI 0xd0667b0>(
<Container: 0x807a180> (entity: Container; id: 0x801bc50 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/Container/p1> ; data: {
    containerId = TestContainer;
    items = "<relationship fault: 0x805b100 'items'>";
    state = "(...not nil..)";
    type = 4;
})
)

Finally, you may be using Transformable attribute types. The state attribute above is an example. If you’d like more information than (...not nil...), use the KVC getter and you’ll see that it’s an empty NSDictionary:

(lldb) po [[[[myListObj valueForKey:@"itemContainers"] allObjects] lastObject] valueForKey:@"state"]
(id) $29 = 0x0807dd30 {
}

(lldb) po [[[[[myListObj valueForKey:@"itemContainers"] allObjects] lastObject] valueForKey:@"state"] class]
(id) $30 = 0x01978e0c __NSCFDictionary

It’s taken me months to learn these tricks, so hopefully this short essay helps you come up to speed more quickly than I did!

ARC and copy

Like many of you, I’ve recently starting coming to terms with automatic reference counting (ARC) in Objective-C. For the most part, it’s gone remarkably smoothly. The only hard part is remembering to not type autorelease!

ARC lets us get rid of the retain/release pairs in our code. But can you spot the bug in the following code?

@interface MyObject : NSObject

@property (copy) id ivar;

@end

@implementation MyObject

@synthesize ivar = _ivar;

- (id)initWithIvar:(id)ivar
{
  self = [super init];
  if (self != nil) {
    _ivar = ivar;
  }
  return self;
}

@end

The ivar instance variable is a strong reference, not a copy. Since I think it’s a bad idea to use accessors during -init the copy semantics defined by the @property are never used and ARC happily retains the reference instead of copying it. Also of note: the static analyzer doesn’t think there’s anything wrong with the code above, so you won’t see any warnings that you’ve screwed up.

In my case, this initializer led to a bug where several worker threads were modifying the same instance variable and, well, you know how that goes. The fix, as usual, was painfully simple:

    _ivar = [ivar copy];

It’s important to remember that ARC primarily affects how we use -retain and -release. As Matt Drance points out the way -copy works hasn’t changed significantly. Just because we can forget about typing “retain” and “release”, doesn’t necessarily mean that we can forget to type “copy”, too.

Updated May 4th, 2012: A Radar about the static analyzer not issuing a warning has been filed: rdar://11386493

iTunes Manglement

I think we can all agree that iTunes is in need of a major overhaul. So why isn’t it happening?

Apple has shown no fear of rethinking and innovating with the user interfaces that manage our own personal data. Both the iMovie and iPhoto apps are great examples of this: they’ve gotten much simpler to use over the years (especially as they’ve moved to the iOS platform.)

This makes me think that there may be another factor that’s holding back iTunes; and I fear that it’s contractual.

Much of iTunes functionality is based around content that Apple or the user doesn’t own. And as we all know, the media companies that own the content are particularly paranoid about how digital assets are managed. In the 10+ years that iTunes has been in existence, I’m sure there’s a tangled web of legal obligations that makes improvements a huge technical headache.

To give you an idea of how painful this must be, imagine being a developer at Apple and having to consult this before implementing or improving a feature in iTunes. And when you’re done wrapping your head around those conditions, make sure you have thought about restrictions in other parts of the world. Having fun yet?

Core Data without Fetch Requests

If you follow me on Twitter, you’re probably aware of a really nasty problem I encountered with versioned Core Data models. Suffice it to say that was two days of hell caused by a single Fetch Request in a .xcdatamodeld file.

The irony of it all is that I no longer use Fetch Requests this way: the queries defined in the model file weren’t being used so I was happy to delete them all.

In my opinion, fetch requests that are stored as a part of the managed object model have some downsides:

  • You need to carry around an instance of NSManagedObjectModel in order to access the requests.
  • A text-based search (such as Find in Workspace from Xcode’s Search Navigator) doesn’t find the requests.

Instead, I use a category for NSManagedObjectContext that adds some nice functionality for creating fetches and executing them purely with code. The original idea and implementation comes from Matt Gallagher.

A picture’s worth a thousand words, so let’s look at how this works in code. Instead of creating an “existingPartner” fetch configured as:

Fetch all: Partner objects where:
Expression: name == $NAME

I write this code:

NSArray *objects = [managedObjectContext fetchObjectArrayForEntityName:@"Partner" withPredicateFormat:@"name == %@", name];

If I need the managed objects in sorted order, there’s this variant:

NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];

NSArray *objects = [managedObjectContext fetchObjectArrayForEntityName:@"Partner" usingSortDescriptors:sortDescriptors withPredicateFormat:@"name LIKE %@", pattern];

Getting count entity objects is just as easy:

NSNumber *salaryLevel = [NSNumber numberWithInteger:1000000];
NSUInteger count = [managedObjectContext countForEntityName:@"Employee" withPredicateFormat:@"salary > %@", salaryLevel];

After reading about how NSExpression can be used to significantly speed up fetches against attribute values, I extended Matt’s NSManagedObjectContext+FetchAdditions category. This lets you do things like this:

NSNumber *marketingPayroll = [managedObjectContext fetchObjectArrayForEntityName:@"Employee" usingAttribute:@"salary" andFunction:@"sum:" withPredicateFormat:@"department == %@", marketing];

One thing to keep in mind when you’re using NSExpression against a SQLite store: it only works on data that has been persisted. Since the underlying implementation relies on SQL functions such as min(), max() and sum(), any managed objects that you have in memory (because the context hasn’t received a -save) won’t be included in the results.

One final trick I use is to keep the code for these fetches as close to the objects and attributes as possible. I’ve found it very handy to create class methods in the NSManagedObject subclasses. For example, using our partner name example from above, I’d implement the following class method for the Partner object:

+ (Partner *)fetchInManagedObjectContext:managedObjectContext withName:(NSString *)name
{
	Partner *result = nil;

	NSArray *results = [managedObjectContext fetchObjectArrayForEntityName:@"Partner" withPredicateFormat:@"name == %@", name];
	if ([results count] > 0) {
		result = [results lastObject];
	}

	return result;
}

Then when I need a Partner with the given name, I use:

Partner *partner = [Partner fetchInManagedObjectContext:managedObjectContext withName:@"CHOCK GLOBAL INDUSTRYS INK"];

I hope this category cleans up your code as much as it has mine. Feel free to use this code in any way you see fit.

VMware for developers

Many of us rely on VMware Fusion for testing our products both on older and newer versions of Mac OS X. Your development machine may be running Lion, but it’s incredibly handy to run both Snow Leopard and Mountain Lion on the same machine.

With the recent release of Mountain Lion DP2 some problems cropped up with Fusion 4.1 (you’ll need a developer account to view that link.) Luckily the new VMware Fusion Technology Preview 2012 makes it possible to run the latest Mountain Lion release without any problems. This preview release also allows you to keep Fusion 4.1 installed if you encounter any problems in other virtual machines: the only requirement is that you can only run one version at a time.

Also worth nothing: if you used the bug in VMware Fusion 4.1 to create a virtual machine that uses the client version of Mac OS X 10.6, you’ll be disappointed as soon as you try to restart that VM in the Technology Preview. During the reboot, you’ll see a window pop up with the message:

The guest operating system is not Mac OS X Server. This virtual machine will be powered off.

You’ll need to keep VMware Fusion 4.1 around if you want to apply Software Updates to your test environments.

I wish Apple would relax this restriction with the 10.6 clients: we still have products that rely on older versions of Xcode. Apple themselves even have sample code that can’t be opened in Xcode 4, preventing developers from exploring older, but useful, projects.

Sandboxing

The recent release of xScope 3.0 is our first product to use the new application sandbox that will soon become a requirement for submission to the Mac App Store. I’d like to share some experiences and advice on how to use it in your own products.

First off, Ivan Krstić and the rest of the team at Apple have done a great job in making the whole process easy to implement. Adding entitlements and signing your code will be the least of your worries as you transition to the new sandbox.

Of course there are some applications that have a harder time than others: primarily if those apps require access to all or part of the filesystem (think about syncing data with Transmit, for example.) Apps that make use of AppleScript for inter-app communication will also have a difficult time: this includes our Take Five app. Apple is actively listening to developers who are encountering these types of issues, so if you haven’t filed a Radar yet, quit bitching.

Speaking of Radar, we encountered a fairly nasty problem after launching xScope. Many of our customers are designers and developers who love SSDs. It’s common to use a symlink in your Home folder to put big datasets like Pictures, Music and Movies on a separate hard drive. When you do this, folder access in the application sandbox container breaks. A small number of users who use symlinks are also getting crashes after launching the app that was downloaded from the Mac App Store:

xpchelper reply message validation: sandbox creation failed: 1002
Container object initialization failed: The file couldn’t be opened.

We also encountered a problem when using Sparkle to update an app running in a sandbox: an app can’t update its own binary. Changing Sparkle so that it uses an XPC service is a major architectural change, so we decided to remove the sandbox for the version we distribute on the website.

Besides being the path of least resistance, it also gives us a version of xScope that doesn’t run into the sandbox bugs reported above. I highly recommend that you give yourself this option for any customers that experience sandbox related problems.

All things considered, adding an application sandbox has been a fairly smooth transition. But it’s also clear that we’ve only just begun putting the genie back in the bottle.

Updated January 27th, 2012: The bug reported above is a duplicate of Radar 9865143.