It’s convenient to use git’s commit hash as a versioning tool. Here’s an easy way to integrate this with Xcode.
Bonus: This one doesn’t actually change the Info.plist file, so after a compile there are no dirty files git would want to check in.

The first method works for regular apps. At the end I show a option if you’re building a framework/static library.

Easiest way: Use the Info.plist preprocessor. Add this as a Run Script phase at the top of your target (after Target Dependencies).

We’re using the output of git describe as the full version string (which can be anything, according to Apple), and the count of the total commits for CFBundleVersion, which should be numeric, else certain functions like Finder’s ability to find the lastest app version might break. Counting git commits is not the best way, but it works.

Then, enable “Preprocess Info.plist File” and add “Info.plist Preprocessor Prefix File” to “InfoPlist.h”.

Almost done! Add GIT_VERSION and GIT_COMMIT_COUNT to your Info.plist file:

Finally, you need to create the InfoPlist.h file once, else Xcode doesn’t compile at all.

That’s it! Note that you probably don’t want to use “1.x.x-00-g34g3343″ as a version for the AppStore. In this case, make a target for publishing and change the git describe to “git describe –abbrev=0 –tags” (this will return the latest tag – you DO tag your code, right?)

If you are doing more tricky things, like compiling your own static framework (I provide one for PSPDFKit), you need stronger weapons. Add this to your build script. No need for custom Info.plist preprocessing in this case. (I manually copy Info.plist into the directory, so the original plist file never gets changed.)

Thanks for everyone on Twitter helping me to get this right! This blog post on CIMGF was also pretty helpful.

Of you like posts like this, you should follow me on Twitter.

If you call an optional delegate protocol, and it grows larger and larger, you find yourself write variations of this ugly block all over your code:

if ([self.delegate respondsToSelector:@selector(pdfViewController:didDisplayDocument:)]) {
[self.delegate pdfViewController:self didDisplayDocument:self.document];
}

I consider this code smell, and it doesn’t get easier once you decide to change the delegate. While developing PSPDFKit (a commercial, extremely fast iOS pdf framework), i found myself in the situation of polluting my code with those delegate calls all over the place. There has to be a better way.

During my Twitter research, I fond some experiments like using an NSProxy or using _cmd as a shortcut. Using _cmd would imply that you name your delegate caller the same as your delegate method. THATS A VERY, VERY BAD IDEA. It’s often practical to extend a class and set the delegate to itself – in fact that’s the suggested way to customize PSPDFViewController. Using the _cmd trick would imply that the user calls [super ---] on every delegate – forgetting that would work most times, except when he tries to nil out the delegate on viewWillDisappear, and still receive messages (because there is no caller any more) – leading to very confusing errors.

A pattern that’s common in Apple classes is to pre-analyze the delegate, and remember which methods are implemented and which aren’t. I did some profiling and discovered that this is about twice as fast. In reality, you may not care, delegates usually are not called in tight loops, where you could actually gain some feelable performance with this task.

But we gain something else: clarity. Here’s my proposal:

// pre-process delegate
- (void)setDelegate:(id)delegate {
if (delegate != delegate_) {
delegate_ = delegate;

delegateFlags_.delegateDidDisplayDocument = [delegate respondsToSelector:@selector(pdfViewController:didDisplayDocument:)];
delegateFlags_.delegateDidShowPage = [delegate respondsToSelector:@selector(pdfViewController:didShowPage:)];
}
}

// single entry point to call delegate
- (void)delegateDidShowPage:(NSUInteger)page {
if (delegateFlags_.delegateDidShowPage) {
[self.delegate pdfViewController:self didShowPage:page];
}

// put this in header
struct {
unsigned int delegateWillDisplayDocument:1;
unsigned int delegateDidDisplayDocument:1;
unsigned int delegateWillShowPage:1;
unsigned int delegateDidShowPage:1;
unsigned int delegateDidChangeViewMode:1;
unsigned int delegateDidTapOnPage:1;
unsigned int delegateDidTapOnAnnotation:1;
unsigned int delegateWillLoadPage:1;
unsigned int delegateDidLoadPage:1;
unsigned int delegateWillUnloadPage:1;
unsigned int delegateDidUnloadPage:1;
} delegateFlags_;

So, this is much more code, what’s better?

  • Instantly see which methods are implemented by the delegate.
  • Calling [self delegateDidShowPage:1]; instead of the 3-line respondsToSelector improves code readability.
  • Single calling function for delegates. Again, helps for debugging.
  • Delegates can be changed much easier.
  • Twice as fast!

Bonus feature: Because there’s now a single entry point, it’s easy to add a one-time deprecation warning (like an NSLog) to the delegate caller. That would be a PITA if you have multiple entry points.

I also experimented with further improvements, and you can squeeze out even more performance by using c-functions, and again more if you inline them. But it’s usually not worth it and the calling syntax doesn’t look nice.

Btw, I used mach_absolut_time to measure execution time. Don’t use NSDate for it, it’s too unprecise. (Some technical stuff: I used a tight 1-million for-loop to get some milliseconds to measure, and disabled optimization so that the if couldn’t be pre-optimized)

I’m open to comments. Is there a better way? Waste of time? Hit me up on Twitter! @steipete

Hacking on Chameleon

23 Mar
2011

Chameleon is a clean-room implementation of UIKit. So you can just compile your iOS App to the mac. And boy are there some iOS apps out there that I want on my mac (even better: on the dashboard!)

Currently it’s about 70% complete, and simple projects already compile. And the pull requests are already underway.
And because it’s from a working, living, money generating project, it won’t vanish anytime soon. Thank you, Iconfactory!

I added some initial helper macros for the parts for better code conditionals in PSFoundation (Categories, Macros and other useful stuff) And fixed the parts that don’t compile at all. This is going to be great!

The hardest part will be coding a replacement for NSFetchedResultsController; I started a stub w/o event handling…

Apple’s ballsy move

16 Feb
2011

If you’re not living in the Apple Bubble, you can boost your knowledge about Apple’s latest ballsy move.

In a nutshell, Apple now wants 30% of everything you purchase or use with an iOS device. And you are not allowed to make it more expensive to compensate the losses. If your margin is less than those magic 30%, you’re screwed.

Yes, for customers (short thought) it’s a good thing. You no longer have to give away your details (like name, address, credit card) and instead can use a payment provider you already trust (Apple) which handles everything and does not give away your contact details.

“Our philosophy is simple—when Apple brings a new subscriber to the app, Apple earns a 30 percent share; when the publisher brings an existing or new subscriber to the app, the publisher keeps 100 percent and Apple earns nothing,” – Steve Jobs

To keep a 70/30 cut for selling apps is one thing – here Apple offers service, like reviewing (hoho), distributing the app through their high-speed network, advertisement (if you’re lucky) and the whole AppStore-Infrastructure.

What do they do with In-App purchases? Handling your payments. No hosting, no additional service. That’s just not worth 30%. Google today launched Google One Pass, a solution for offering digital content – they take much more reasonable 10%. Paypal takes 3%.

Everyone’s talking about ‘publishers’ with these new App Store rules. What about vendors? They’re the ones getting royally screwed. – Jim Dovey, former developer of Kobo Books.

Many publishers and developers are screaming now, that Apple destroys their whole business. It’s because most businesses don’t have a 30% span they can share. So Apple is, in fact, asking them to lose money.

It’s also about process. Setting up IAP is a pain. Keeping it in sync with your library is even worse. And there are also limits – IAP allows up to 3500 items – Amazon Kindle currently has about 2,5 MILLION items.

I don’t see Amazon giving Apple 30% for just being on the iPad/iPhone. Furthermore, they would have to offer IAP at the same price to just stay in the store. Their current solution, which is offering a link to the Amazon Store that opens in Safari, is no longer allowed. (No external linking to the site shop).

So their only viable option: remove the App from the AppStore. Same for Netflix, Hulu, Pandora, Kobo, Zinio Magazines and many other services. This is bad for customers, and bad for Apple. And it makes me angry and sad. Damn it, it’s my device, I paid $$$ on it, and now they are taking away my apps. Not fair. It’s also utmost greedy – Apple’s not the company that would in any way need this extra money.

I genuinely hope that they end this madness.


(picture by Chi King)

In the end, money is the motivation why we all work. I earned good money at my last company, so why quit and go freelance? Cause I care about my work, and with freelancing you have a choice what and with whom you work for.

It wasn’t always easy, and I learned a lot. In fact, I am still learning so much every day. Fortunately, the mobile space is growing so crazy fast – most of the times I just have to say “no” to inquiries cause I’m already that overbooked. And being damn good at it supposedly helps, at least some customers argue. But to be really good at work, some factors need to be there:

Freedom to decide. Not every aspect, but its so tiresome if costumers think they know better, when in fact they simply don’t. Some freedom in features, design assures that you are able to care for the project. Let loose, give us some latitude. Read the rest of this entry »

top

Switch to our mobile site