The Elegant Chaos Blog

December 14, 2011

Ambientweet 1.0.2 is now available from the Mac Application store.

This is a minor revision, with a fix to a bug that caused it to stall and stop refreshing tweets after an hour or so.

more...

Let’s say that you are building with a modern SDK, but you are targeting some older platforms. For example, you’re using the iOS 5.0 SDK, but you want your app to run on iOS 4.2.

When you do this, you have to take care not to call routines which are only implemented on iOS 5. You can set your deployment target to 4.2, but that won’t prevent you from calling 5-only routines, they will simply be weak linked and will be nil on platforms that don’t support them.

Occasionally I slip up on this, and during testing I discover that I’m using a routine that doesn’t exist everywhere.

The simple solution when this happens is to stop using the routine in question, and find some workaround, but that’s a bit rubbish. After all, the routine has probably been added precisely because it’s useful.

Also, the routine, when it does exist, might be better than your workaround. Apple wrote it, so it must be good right? So you’d prefer to only use the workaround where necessary.

Finally, this is a temporary problem that will probably go away at some point. The routine is present on new systems, and eventually time will move on and you’ll stop supporting the ones where it is missing.

So rather than changing your code, how about finding a way to add the missing implementation when necessary? Luckily, Objective C is wonderfully dynamic, so this is entirely feasible. Here’s a little example taken from ECFoundation.

UIColor has a method getRed:green:blue:alpha: which only seems to be present in iOS 5 (although the header helpfully fails to mention this). How can we arrange to add an implementation for it, if it’s missing?

The first thing we want to do is add the replacement implementation. We can put this anywhere really, but for neatness it might as well go into a private category on the class we’re fixing up. The actual code here doesn’t really matter for the purposes of the example - the point is that you’ve now got a method implementation that you can install if you need to:

- (BOOL)ecGetRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha
{
	const CGFloat* components = CGColorGetComponents(self.CGColor);
	BOOL ok = components != nil;
	if (ok)
	{
		*red = components[0];
		*green = components[1];
		*blue = components[2];
		*alpha = components[3];
	}
	
	return ok;
}

So how to we do the installation? We only want to do it once, and we want to do it before the routine is called, otherwise it won’t be much use.

This sounds like a job for the +initialize method, but there’s a slight problem. What happens if you add an initialize method in a category and there’s already one defined in the system elsewhere. Presumably you get a name clash and one version won’t get called. Nasty.

Luckily, there’s also the +load method. This gets called super early, and has some interesting properties. Here’s a quote from a great Mike Ash article:

An interesting feature of +load is that it’s special-cased by the runtime to be invoked in categories which implement it as well as the main class. This means that if you implement +load in a class and in a category on that class, both will be called. This probably goes against everything you know about how categories work, but that’s because +load is not a normal method. This feature means that +load is an excellent place to do evil things like method swizzling.

What a piece of luck! The system frameworks will be loaded by the time it runs, so we can test for the routine’s presence, and if it’s not there, add it:

+(void)load
{
	SEL apiSelector = @selector(getRed:green:blue:alpha:);
	if (![self instancesRespondToSelector:apiSelector])
	{
		SEL replacementSelector = @selector(ecGetRed:green:blue:alpha:);
		Method replacementMethod = class_getInstanceMethod(self, replacementSelector);
		IMP replacementImplementation = method_getImplementation(replacementMethod);
		const char* encoding = method_getTypeEncoding(replacementMethod);
		class_addMethod(self, apiSelector, replacementImplementation, encoding);
	}
}

[Updated Feb 2012 to fix some github links that have changed]

more...

Apple’s bug reporting system (imaginatively called Apple Bug Reporter to the outside world, but known as Radar internally I believe), is essentially closed.

What this means is that you can’t check up on a bug that you didn’t originate, you can’t share your bug report with other developers for testing, discussion or corroboration, and you can’t search for existing or similar issues.

This really really sucks. Recently I’ve been filing quite a few bugs, mostly on the development tool that I use most often - Xcode.

Previously I’ve been quite bad at reporting bugs. Two of the main reasons for that were lack of time, and lack of feedback.

Lack of time boils down to the fact that in the past I generally didn’t work for myself, and I was usually massively overworked / or at least my time was greatly over-committed by my bosses. This lead to the perpetual feeling that I was running to catch up and had no justification to indulge myself with anything time consuming that wasn’t worth the effort.

Generally I was actually very good at resisting the (mostly illusory) sense that it wasn’t worth doing something that had long-term benefits, because of short-term pressure. However, often I didn’t bother filing bugs to Apple, because of the combination of my lack of time with the second problem - lack of feedback.

Filing bugs in Radar quite often feels like sending text direct to /dev/null, in terms of the sense one gets that the effort spent doing it is worthwhile.

One reason for this is that the interface for doing so is incredibly primitive, and consequently filing bugs feels slow and awkward.

The other reason is that, as a developer, I’m only too aware of the need to not only be precise in the report, but to include lots of information. It’s hard to find the time to do this properly at the best of times (indeed many full-time testers that I’ve worked with seem to find it impossible, despite it being their job, but that’s another story). It’s even harder to find the will to do it if you can’t check first whether you’re wasting your time reporting something that already has 1000 duplicate reports.

Now that I’m indy (yay!), I have been making more of an effort. If I find a bug, or have a feature suggestion, I try to report it.

It still feels painful though. When I do report an issue, I often don’t supply nearly as much information as I’d like if I was the developer on the other end, because I still feel that I haven’t got the time to waste on taking a punt that someone somewhere actually gives a damn about what I’m saying.

Lest anyone should misunderstand, it’s not that I think that the Apple engineers, chained to their desks in their darkened coding dungeons in Cupertino, don’t care. I’m sure that they very much care (well, most of them), but most of the time the bug reporting system is a bloody great big emotion filter that manages to prevent any hint of that caring seeping out across the divide back to us mere mortals.

There are many things that Apple could do to improve this process.

When it comes to the reporting of bugs, the web interface could be better. Standalone Mac/iPhone/iPad applications could be written which can automatically fill in most of the contextual information that one needs to provide. The “Report A Bug” menu command could become standard in Apple applications, and a de-facto standard everywhere, with coding support to make it easy to implement.

Best of all though, the bug reporting system could be partially opened up.

I don’t want some sort of open source anarchic free-for-all where any Tom, Dick or Harry can get into a flame war with the developer responsible for implementing a feature or fixing a bug.

At the very least though I’d like to be able to do some sort of web search on a problem description and be told “45 issues match this problem” - even if I couldn’t see those issues.

Better still, I’d like to be shown the summaries of those bugs, if not the full bug reports.

Of course it would have to be possible to mark bugs as private (if they contain proprietary information), and in any case I’d only expect to be able to do this after logging in with my developer id - so Apple would have full control over what level of access I was given to a particular bug. Perhaps based on some internal concept they had of my level of experience, trustworthiness, the help I’d given tracking down previous bugs, etc.

Ideally I’d also like to be able to explicitly share bugs with colleagues, by attaching them to the bug so that they have full access.

And I’d like the bug reporting system to be integrated with the developer forums so that it was possible to have a forum thread linked to one or more bugs in a way that allowed a discussion surrounding a particular issue to be clearly associated with the bug id.

I’d like to be able to +1 a bug as a very quick indication that I also had it, or +1 a feature request as a way of adding my vote to it.

This stuff isn’t rocket science. I know that Apple has a lot of important stuff to do, but the strength and helpfulness of the developer community is one of it’s hidden assets. It would be great if Apple could show us diligent bug reporters a little bit of love.

more...

December 03, 2011

A couple of new betas are available from the beta software page.

Neu 1.2b7 includes a couple of minor tweaks to the UI, and a revised manual. I know that there are a few typos to fix in the manual, but I’d definitely appreciate any other feedback on it, or the changes in Neu 1.2. I’m hoping to submit this version to the App store soon.

Ambientweet 1.0.2b1 fixes a stall that has been reportedly happening in Ambientweet - where it simply stops working after an hour or two. Again, all feedback greatly appreciated.

more...

November 24, 2011

I’m pleased to announce that over this weekend, we’re running a 50% off offer on Neu. For this period it will be available for the bargain price of $5!

We’re also holding Ambientweet at it’s current price (free!) over this period - after which it will actually cost something!

Go get em whilst they’re hot…

more...