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

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

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.

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];

}
Text

Drawing single-line-height text without abandoning CTFramesetterRef and CTFrameRef

Core Text is lightweight and all awesome. But we have this pesky little problem with multilingual layouts — different fonts are created by different vendors and typographers, and they often mismatch in terms of metrics. Some are taller, some shorter; some have huge ascender and descenders, some relatively tiny and tame. Things are quite manageable with just one language, but it’s not the case for me.

We need layouts with even baselines, which runs orthogonal to how the CTFramesetter works. The frame setter calculates typographic bounds, adds up ascender, descender and leading, then use the whole sum as the height of the line. This is straightforward as in it works like movable type, but also painful as it can wreak havoc with baseline alignment.

No, you don’t have to roll your own CTFramesetter implementation. That’s too much pain. Re-implementing something when you can hack and use all the free stuff? No way, that’ll take a whole week.

Creating a well-hacked attributed string

To ensure that your Core Text label draws even line heights, make sure the attributed string you feed into your label is well-hacked:

- (NSAttributedString *) attributedStringForString:(NSString *)aString font:(UIFont *)aFont color:(UIColor *)aColor {

    if (!aString)
        return nil;

    float_t lineHeight = aFont.leading;

    id fontAttr = [NSMakeCollectable(CTFontCreateWithName((CFStringRef)aFont.fontName, aFont.pointSize, NULL)) autorelease];
    id foregroundColorAttr = (id)(aColor ? aColor.CGColor : [UIColor blackColor].CGColor);
    id paragraphStyleAttr = ((^ {

        CTParagraphStyleSetting paragraphStyles[] = (CTParagraphStyleSetting[]){
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(float_t), (float_t[]){ 0.01f } },
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(float_t), (float_t[]){ lineHeight } },
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(float_t), (float_t[]){ lineHeight } },
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierLineSpacing, sizeof(float_t), (float_t[]){ 0.0f } },
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierMinimumLineSpacing, sizeof(float_t), (float_t[]){ 0.0f } },
            (CTParagraphStyleSetting){ kCTParagraphStyleSpecifierMaximumLineSpacing, sizeof(float_t), (float_t[]){ 0.0f } }

        };

        CTParagraphStyleRef paragraphStyleRef = CTParagraphStyleCreate(paragraphStyles, sizeof(paragraphStyles) / sizeof(CTParagraphStyleSetting));
        return [NSMakeCollectable(paragraphStyleRef) autorelease];

    })());

    NSAttributedString *returnedString = [[[NSAttributedString alloc] initWithString:aString attributes:[NSDictionary dictionaryWithObjectsAndKeys:
        fontAttr, kCTFontAttributeName,
        foregroundColorAttr, kCTForegroundColorAttributeName,
        paragraphStyleAttr, kCTParagraphStyleAttributeName,
        [NSNumber numberWithInt:kCTUnderlineStyleSingle], kCTUnderlineStyleAttributeName,
    nil]] autorelease];

    return returnedString;

}

Think of the many 0.0f as CSS Reset.

Drawing the attributed string with even baselines

If you’re creating an UILabel subclass, this would probably work. Without drawing adjustments, fonts with slightly different ascender and descender values will twiddle around. Which makes an unholy, unsightly mess.

Note that ctFrame is a private property and irCTFrameEnumerateLines is a simple convenience wrapping around functions that operate on a CTFrameRef:

- (void) drawTextInRect:(CGRect)rect {

    if (![self isShowingRichText]) {
        [super drawTextInRect:rect];
        return;
    }

    CTFrameRef usedFrame = self.ctFrame;
    if (!usedFrame)
        return;

    CFRetain(usedFrame);
    CGContextRef context = UIGraphicsGetCurrentContext();   
    CGContextConcatCTM(context, CGAffineTransformMake(
        1, 0, 0, -1, 0, CGRectGetHeight(self.bounds)
    ));

    __block CGFloat usableHeight = CGRectGetHeight(self.bounds);
    CGContextSetTextMatrix(context, CGAffineTransformIdentity); 
    irCTFrameEnumerateLines(usedFrame, ^(CTLineRef aLine, CGPoint lineOrigin, BOOL *stop) {

        usableHeight -= self.font.leading;

        CGContextSetTextPosition(context, lineOrigin.x, usableHeight - self.font.descender);
        CTLineDraw(aLine, context);

    });

    CFRelease(usedFrame);

}

Appendix

gh://schwa/CoreTextToy

Text

Dealing with side effects of UIView animation

UIView’s animation methods (both the -beginAnimations:context:/-commitAnimations, and the blocks-based APIs introduced in iOS 4) do a lot of work for you. They are implicit, and all super effective.

Occasionally (and inevitably) you’ll hit cases where you wish you could work around it without losing all the inference you’ve been enjoying. Fortunately, this seems to work:

[UIView animateWithDuration:3.0 animations: ^ {

    // Assuming that aView is contained by aContainerView

    CGRect fromFrame, toFrame;
    fromFrame = aView.frame;

    [aContainerView doSomethingThatChangesFramesForAllTheSubviews];

    toFrame = aView.frame;
    aView.layer.frame = fromFrame;

    aView.frame = toFrame;

}];

The idea is to work exactly one level lower; all the UIViews wrap around CALayers, or subclasses of CALayer; all the properties set and retrieved on the view reflects on its hosting layer.

I do not understood how exactly UIView’s implicit animations work yet, but it seems like the class does some impromptu KVO on the view level, queue changes up, and stitch CABasicAnimation objects on the respective layers; so presumably this works because:

  1. A property change on the hosting layer is not picked up by inferencing code.
  2. The change allows the next line changing the property on the view to get picked up.
Link

This is a PDF file I created for Matt Gemmell’s Touch Notation suitable for print.

Text

Harm-free Class Swizzling with NSKeyedUnarchiver

I came across a problem where we need to subclass UINavigationBar in an UINavigationController. Conventionally, one may subclass the navigation controller, and commence hacks. Fortunately, NSKeyedArchiver saves the day:

self.window.rootViewController = (( ^ {

    //  Since it is totally unsafe to modify the navigation controller, the best way to swizzle-in a custom navigation bar subclass is to use NSKeyedUnarchiver, providing a custom class for any object whose class is named UINavigationBar.

    UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:presentedViewController] autorelease];
    NSData *navControllerData = [NSKeyedArchiver archivedDataWithRootObject:navController];

    NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:navControllerData] autorelease];
    [unarchiver setClass:[WANavigationBar class] forClassName:@"UINavigationBar"];

    //  The root object is keyed “root”. :D
    UINavigationController *swizzledNavController = [unarchiver decodeObjectForKey:@"root"];
    [swizzledNavController setViewControllers:navController.viewControllers];

    return swizzledNavController;

})());

WANavigationBar inherits from UINavigationBar, and overrides -drawRect: with an empty implementation.

Obviously, if you were already setting your navigation controller using Interface Builder (i.e. you can actually see it in Interface Builder), then the best way requiring no swizzling code at all is to find the navigation bar instance, then set its class to your own subclass in the Identity inspector.

Text

Providing default user defaults

NSUserDefaults is fun and practical. Assuming you’ve got an IRDefaults.plist in your app bundle, this registers everything in the dictionary as default user defaults:

void IRRegisterUserDefaults () {

    NSURL *defaultsURL = [[NSBundle mainBundle] URLForResource:@"IRDefaults" withExtension:@"plist"];
    NSData *defaultsData = [NSData dataWithContentsOfMappedFile:[defaultsURL path]];
    NSDictionary *defaultsObject = [NSPropertyListSerialization propertyListFromData:defaultsData mutabilityOption:NSPropertyListImmutable format:nil errorDescription:nil];

    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsObject];

}
Text

Customizing UITabBar (Or, tenuous principles to UIKit customization)

UITabBarController is cool, but (un)fortunately it has only one flavor. People might resort to brute-force, re-creating the entire controller from scratch. This is extremely troublesome: a) it takes a lot more effort; b) it needs reverse engineering; c) quirk-for-quirk compatibility is not fun.

Most of the time, you’ll want to skin the thing, not create a new thing that works like the old thing, and then skin that new thing. If you can’t skin it (because the view hierarchies are private) thru UIKit, work around UIKit. Work from Core Animation.

Use a fully black CALayer on the tab bar’s maskLayer in a custom subclass. Then use NSKeyedUnarchiver and NSKeyedArchiver to swizzle the tab bar (thanks cocoa-unbound) with your own subclass.

If things proceed correctly, you’ll get a tab bar that is transparent, but still handles all the touch events, and works “correctly” in all cases. Now go on and intercept all its delegate calls, reconfiguring your own faux tab bar overlay whenever necessary.

Morale:

  • Don’t hack view hierarchies; if it looks hack-ish, there’s probably an easier way.
  • Stealing Apple code, and avoiding writing code, is extremely fun.
Text

Managing iOS Crash Reports with PLCrashReporter

I often hear that when your product grows so big you need to assess and prioritize bugs based on the amount of reports per bug you get. (I do hope that you’ll never have too many bugs and need to prioritize them!)

This is how we currently manage bugs:

  • Bugs are caught by PLCrashReporter. I’ve got a fork somewhere as well for Lion + Xcode 4 + iOS targets.
  • I’ve got an UIApplication+CrashReporting accessory in IRFoundations/IRAccessories which does all the work in the iOS app.
  • Most iPads and iPhones have email accounts set up, so it is used for reporting.
  • Emails are then sent to a shared email account, and Redmine is set up to fetch all of them, which are converted to actual tickets in a special project.
  • We run a forked PLSymbolicate which relies on gem ruby_protobuf and spotlight.