Link

The Sliding Split View Controller is a tiny class implementing sliding pane navigation sported by the Facebook, Path, and Sparrow apps on iOS. (Many other apps have them, but these are the ones on my iPhone right now.) The controller is extremely simple — it holds a masterViewController and a detailViewController. By default, the Master view controller is hidden, but you can show it, and animate the process optionally, like the many methods:

- (void) setShowingMasterViewController:(BOOL)showingMasterViewController animated:(BOOL)animate completion:(void(^)(BOOL didFinish))callback;

- (void) setMasterViewController:(UIViewController *)toMasterVC animated:(BOOL)animate completion:(void(^)(BOOL didFinish))callback;
- (void) setDetailViewController:(UIViewController *)toDetailVC animated:(BOOL)animate completion:(void(^)(BOOL didFinish))callback;

The reason behind copious callbacks is that manually tracking every single possible animations is tedious or impossible without hacks — already so with the Map View — and stacking multiple animations usually won’t work well in a serious setup. So, I’ve got a rule of thumb that all the methods which could animate should accept a callback block.

A particular project of mine uses this controller, and I have been getting some interesting feedback regarding its interaction with different iOS controls (the Table View, the Map View, miscellany controls like Buttons and Sliders, and other gesture recognizers in custom views).

Deep down, it uses View Controller Containment introduced in iOS 5, and the whole thing was a blast to build both in terms of development speed and feeling. New code feels good.

Going forward, these issues are on my mind regarding the project:

  • Minor UX issues like the Master view controller showing through when the Detail pane is dragged leftwards needs to be resolved thru more application-centric work.
  • It needs hysterisis and bounceback to feel more realistic.
  • It needs some better API regarding custom presenting / dismissal animations.
  • It probably needs a way to extend support for a second Master view controller shown on the right.
  • The sample app probably needs to be done, in terms that supporting a reference implementation for a common Master–Detail navigation structure might be utterly needed for new people to utilize this class efficiently.

I’m confident that these problems are either already solved, or trivial (though tedious) to implement. We’ll see where it goes, and please drop me a line if you plan on using it in your next project.

Text

Writing SenTestCase for asynchronous callbacks

Let’s say you have a method definition looking like this, that you’d like to test:

[self doSomethingWithCompletion:^(BOOL finished){
    [weakSelf doSomethingElse];
}];

A naïve way to test it would look like:

@interface NaiveTestCase : SenTestCase
@end

@implementation NaiveTestCase

- (void) testSomething {
    [self doSomethingWithCompletion:^(BOOL finished){
        STAssertTrue(finished, @"Operation must finish");
        [weakSelf doSomethingElse];
    }];
}

@end

This might even work, as long as the block is executed immediately and not concurrently, like in the case of -enumerateObjectsUsingBlock. However, if the block is a callback — for example, from API response over the Internet — it’ll usually not return immediately. And before the assertion statemaents are hit, your method ends, so the block is never really tested.

The initiative is to prevent the method from exiting before the callback is hit — prevent the method from exiting, unless explicitly told so — while at the same time not blocking the main thread, so the callbacks made on the main thread would still work.

One way to do this is to create an NSOperation subclass that takes a block, whose parameter is a callback block. Once code in the block is done, it calls the callback block provided by the operation, which mutates the operation’s internal state and make it return appropriate values for -isExecuting and -isFinished:

AsyncOperation *op = [AsyncOperation operationWithBlock: ^ (AsyncOperationCallback callback) {
    [something doSomethingWithCompletion: ^ (BOOL didFinish) {
        callback();
    }];
}];

That’s a simplified version of IRAsyncOperation. (I’m pretty sure there’s other, better stuff ;) Then, put the operation on an implicitly created operation queue, don’t wait until finished:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
[queue addOperations:[NSArray arrayWithObject:op] waitUntilFinished:NO];

Since every single operation queue implicitly creates threads (the maximum number is defined by maxConcurrentOperationCount, so setting that to 1 guarantees serial execution), it moves the code which generates asynchronous callbacks on another thread for free.

Then, prevent the method from exiting early with a nice Run Loop hack:

while (queue.operationCount)
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:5]];

References

Link

Melbourne is not as far as SF.

Text

My opinion on PaintCode

PaintCode is awesome. That’s what my 2010 self would say to myself. Unfortunately, my 2012 self thinks that it’s not going to work on a multiple-member team where roles and capabilities are diverse, and here’s why.

First, there is teamwork.

With PaintCode, you’ll dictate what tools your designers use, and frankly that is boneheaded and totally backwards. A great designer produces standard-format end results using her tools of choice. PaintCode is never going to be as flush and posh as Photoshop and Illustrator, and that along is a big no-no.

Frankly, if Time • Talent ∝ Skill-level, I would rather get a dedicated designer, rather than a multi-hat, given constant bandwidth, throughput and talent. People with multiple talents are rare, and locating them is not a scalable business. (They are ephemeral, latch on them while you can.)

Second, there is directness.

With a bunch of images, you scan a directory in Finder, and you know how every single of them looks like. With code, you must run them, which means it’ll take longer for you to find the piece of code you need, and longer for you to replace or iterate, because it forces a recompile rather than a re-copy.

With drawing code, you can not diff changes visually. With tools like Kaleidoscope (which I have bought a license) you can visually compare revisions in images. With UI code, it’s hard — you get a text diff, and will need to spin up a second thread in your brain drawing the results, out of thin air. It might also bug out, and has a chance of not quite matching what comes out from the real device. ;)

Rebutting the myth of size

Who cares if your app is 20MB instead of 10MB. Let’s go on a thought experiment:

  • Code-based drawing is fun. But it’s slow to draw every time.
  • Instead of drawing every time, why not draw it one time and cache the results.
  • Instead of calling drawing code once, lazily, on demand, why not just put the results in a persistent file.
  • Why not just use pre-rendered images and load them at launch time.

We end up back at square one. Drawing code may dramatically optimize the size of the app bundle, but size does not matter. Bundle size takes back seat to user experience. If you really need to scale, why not use a PDF.

Reflections

I feel anxious and happy, simultaneously, for effort that has been put into PaintCode. On one hand it’s a new experiment tredding unknown territory, on the other hand its current incarnation allows good-enough results for simple things, but might never scale pass that pinnacle. Many libraries suffer from this problem, too: they make ordinary things easy to accomplish, but you end up hitting walls after walls when you want to do something different. Go use something else, they will say. What’s going to happen next? Adobe gets a bucket of gold on an Illustrator or Photoshop license, and you’ll bundle images with your code.

Enterprisy, one may quip. But any piece of enterprisy software that sells well, fulfills its mission. Life is short, use it well.

Link

IRDroplet is a Dropbox iOS SDK wrapper and has recently been bumped to use the 1.1 SDK, which uses oAuth. Let’s say your user needs to link her Dropbox account with the app. Here’s what happens within an app using the old SDK:

  • User taps button to link.
  • View appears — with an option to register a new account as well.
  • User fills form, and is done with it.

With the new SDK, this happens:

  • User taps button to link.
  • Either the Dropbox app is launched, or the user is sent to Mobile Safari for credentials (different things may happen on different devices depending on the environment).
  • User fills form in either a native app or a Web app, then is sent back to the source application.

This is what happens if you use IRDropletLinkAccountViewController:

  • User taps button to link.
  • View appears.
  • User fills form, and is done with it.

That’s pretty self-explainatory.

p.s. oAuth works extremely well for Web apps because they run in a browser, and modern browsers support tabbed browsing. On a mobile device with only one screen, and often a screenful of content at one time, oAuth is horribly broken.

Text

Managing content size of UIViewController + UITableView in popover

There seems no reason to manually set and observe things again. Provlded that you wrap it in a navigation controller, this works:

+ (NSSet *) keyPathsForValuesAffectingContentSizeForViewInPopover {

    return [NSSet setWithObjects:

        @"tableView.contentInset",
        @"tableView.contentSize",

    nil];

}

- (CGSize) contentSizeForViewInPopover {

    return (CGSize){

        320,
        self.tableView.contentInset.top + self.tableView.contentSize.height + self.tableView.contentInset.bottom

    };

}

Related:

Text

Creating retain cycles by misusing assertions

I’d like to talk about the dreaded retain cycles that often happen unexpectedly, using this wrong sample:

__block __typeof__(self) nrSelf = self;
self.onSomething = ^ (BOOL someCondition) {
    NSParameterAssert(!someCondition);  //  Wrong!
    [nrSelf doSomethingElse];
};

The block causes self to leak. Reason? Check how NSParameterAssert is expanded in NSException.h and compare it with NSCParameterAssert. You’ll see that all of them ultimately gets expanded to NSAssertionHandler method calls:

  • NSAssert, NSAssert1, … call -handleFailureInMethod:object:file:lineNumber:description:.
  • NSCAssert, NSCAssert1, … call -handleFailureInFunction:file:lineNumber:description:.

When you use NSAssert, ultimately the expanded code contains a reference to self and _cmd, inside whatever method the line was used. This also prohibits use of NSAssert in a block, because it creates a retain cycle. Just use NSCAssert which looks slightly wonky but does not require being used within an Objective-C method, and does not allow the programmer to accidentally create retain cycles.

Text

Scroll View quirks, Event handling, and the Run Loop

Many iOS apps are built upon table views that need to be updated on-demand. This article presents a rather simple solution to the problem:

Update the scroll view, continuously, but only when the user is not touching it, so the scrolling and bouncing is not obstructed (and broken) by the updates.

I originally tried a makeshift solution, a couple of years ago, including a UILongPressGestureRecognizer, an ad-hoc dispatch queue, and a couple of lines calling dispatch_suspend() and dispatch_resume(). It was bad.

The new way is simple — do the work thru another layer of indirection:

[self doSomethingWithNetworkDataWithBlock: ^ (id results) {

    CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^ {

        //  All the actual fun happens here.

    });

}];

For more information, take a look at the Threading Programming Guide. What needs to be told, has been excellently told in the piece.

This is what happens when you scroll a scroll view. Note how the run loop exits then re-enters the default mode. I’ve also updated the UIScrollViewExperiments repository with a sample project, UIScrollView-RunLoopExperiments.

Link

Just updated my C3PO fork with some added info in its README. The script was written by Jamie.

On the vouching part, check the PBXProject object in your pbxproj file, and you’ll see a knownRegions. That is an exhaustive list of locales your app is expected to serve, and if anything doesn’t pass muster your app is broken.

Oh, and even if you can’t use a framework target without (seriously) hackery, you can use a bundle.

Link

I’ve also updated documentation for IRDeployment. Now the ever-changing part of the CI workflow — the provisioning profile and the developer identity — is hosted as a Gist, and life is good.

IRDeployment is basically a script that builds an iOS project using xcodebuild, and punts it on TestFlight.

Link
Text

Eventually, people get better. A matter of time frames.

Text

Tidying HTML on iOS

This snippet saved me a couple of hours wrestling with libXML. If performance is not that important you can use JavaScript:

NSString *tidyString = [self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:

    @"(function tidy (string) { var element = document.createElement('DIV'); element.innerHTML = string; return element.innerHTML; })(unescape(\"%@\"));",
    [aString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]

]];
Text

Polyfill in Objective-C

This is mostly a snippet, taken from that kitchen sink project, to illustrate the point. The idea is:

  • Make your baggage small.

  • Test for method existance and framework version if you want it to be safe, because it’s human behavior to publicize an existing private API. Method might exist, but with an incomplete implementation that works differently.

  • Use class_addMethod() for this.

  • Provide your own overridding method so you can steal encoding info from the compiler.

  • Don’t know if that’s mandatory, but I do believe the __attribute__((constructor)) is called after +load. Here’s from the documentation for NSObject:

    The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.

    On Mac OS X v10.5, the order of initialization is as follows:

    1. All initializers in any framework you link to.
    2. All +load methods in your image.
    3. All C++ static initializers and C/C++ __attribute__(constructor) functions in your image.
    4. All initializers in frameworks that link to you.

      In addition:
    • A class’s +load method is called after all of its superclasses’ +load methods.
    • A category +load method is called after the class’s own +load method.
    • In a +load method, you can therefore safely message other unrelated classes from the same image, but any +load methods on those classes may not have run

Here it is:

static void __attribute__((constructor)) initialize() {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    if ([[[UIDevice currentDevice] systemVersion] localizedCompare:@"5.0"] == NSOrderedAscending) {

        Class class = [UIImage class];

        if (!class_addMethod(class, @selector(initWithCoder:), class_getMethodImplementation(class, @selector(_irInitWithCoder:)), protocol_getMethodDescription(@protocol(NSCoding), @selector(initWithCoder:), YES, YES).types))
            NSLog(@"Error swizzling -[UIImage initWithCoder:].  Expect mayhem.");

        if (!class_addMethod(class,  @selector(encodeWithCoder:), class_getMethodImplementation(class, @selector(_irEncodeWithCoder:)), protocol_getMethodDescription(@protocol(NSCoding), @selector(encodeWithCoder:), YES, YES).types))
            NSLog(@"Error swizzling -[UIImage encodeWithCoder:].  Expect mayhem.");

    }

    [pool drain];

}
Link

I’m curious.