For Subclass Eyes Only: Swift, UIGestureRecognizer, and the Protected Extension Pattern

One of my favorite features that Jamin Guy and I shipped in Riposte, an App.net client we made for iPhone, was our Screen Brightness Gesture. Part of the Riposte Pro set of upgrade-only features, the Brightness Gesture enabled you to quickly fade your screen’s brightness up or down by dragging with three fingers anywhere on the screen. It worked just like a dimmer switch for a dining room chandelier.

You have to understand how awesome this was during the iOS 6 days, before the Control Center pane from iOS 7. As far as I know, we are still the only app to ever provide this feature. It was convenient and fun.

An Unfixable Bug

One drawback with the Brightness Gesture was that if you performed the drag gesture over a table view, the content underneath your finger tips would jiggle a bit before our brightness gesture recognizer took over control of touch event handling. At the beginning of the gesture, the table view would scroll about 25 to 50 points before we could disable scrolling. We mitigated this as much as we could, but the first shipping version of it had a dissatisfying amount of jiggle.

Michael "Jury" Jurewitz challenged us to find a way around this behavior. I spent the better part of a week trying everything I could think of: using a UIView subclass to contain the entire view hierarchy, scroll view delegate methods, etc. The irreducible problem was caused by the way touch events get routed through competing gesture recognizers. There was no way to prevent a touch event from being initially handled by UITableView’s pan gesture recognizer. I could cancel scrolling once it was underway, but I couldn’t prevent it from starting in the first place.

But I didn’t want to give up. There was one thing I hadn’t tried yet: method swizzling.

Swizzling Saves the Day

Finally I tried swizzling various methods on UIScrollView and UIPanGestureRecognizer. I found that swizzling two methods on UIPanGestureRecognizer provided the best solution with the least amount of intrusion into the touch event sequence. I swizzled:

- (CGPoint)translationInView:(UIView *)view;

and

- (CGPoint)velocityInView:(UIView *)view;

These methods determine the vector of a pan gesture at any given point in time. In my swizzled implementation, I returned the original implementation’s values in every case except where (in pseudocode):

(self.numberOfTouches > 2) && (self.view.class == UIScrollView)

When these conditions were met, I returned CGPointZero. This worked flawlessly. Table views remained motionless while dragging up and down with three fingers. It continued to work flawlessly after lots of testing, even after iOS 7 shipped. The jiggle was corrected, and the brightness gesture felt better than ever.

Dirty, Dirty Swizzling

I know, I know. Swizzling is dirty and wrong. No seasoned and serious developer would ever use method swizzling or private APIs1 in a production application, right? Maybe. In most cases, a satisfying user experience trumps all other concerns. But I wanted to have someone a lot wiser than me take a look at my solution and offer advice.

At WWDC in 2013, I had that opportunity. I was fortunate enough to meet one of the engineers who had actually worked on UIGestureRecognizer. I demoed the Brightness Gesture for this person and the jiggle problem it caused. In less than a minute, they rattled off a series of suggestions, listing everything that I had spent a week trying to figure out. Before I could respond to each suggestion with, “No, I’ve already tried that,” this engineer immediately came to the same conclusions. Finally they said to me:

I’ve got it! All you need to do is override this one method…

The method name was not one I recognized. The engineer sighed.

Oh yeah. You’re right. It was one of the last methods I wrote before moving on to other stuff and I forgot to publish it.

The engineer hinted that the UIKit team would look into publishing that method in the future. It and another related method eventually found its way into the UIGestureRecognizerSubclass.h header in iOS 7:

@interface UIGestureRecognizer (UIGestureRecognizerProtected)

// same behavior as the equivalent delegate methods, but can be used by subclasses to define class-wide failure requirements
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);

By simply overriding shouldRequireFailureOfGestureRecognizer: in our custom pan gesture recognizer subclass, we could guarantee that a three-finger pan would always cause any other pan recognizer to fail — and without swizzling any methods.

The “Protected Extension” Design Pattern

An interesting thing to note about the UIGestureRecognizerSubclass.h header is that it defines what could be called a protected extension for UIGestureRecognizer subclasses. The following comment can be found at the top of the header file:

// The extensions in this header are to be used only by subclasses of UIGestureRecognizer. 
// Code that uses UIGestureRecognizers must never call these.

According to this design pattern, the parent class (in this case UIGestureRecognizer2) has a private implementation of some complex logic that depends upon a handful of extension methods overridden by its subclasses. Other classes that are simply using a gesture recognizer should never call these methods since that could trigger unwanted effects. But the methods still need to be exposed in a semi-public fashion to allow subclasses to provide their override implementations.

This design pattern has lots of potential uses. One example (similar in spirit, although it doesn’t use a protected extension per se) is NSURLProtocol, which can only be used via a concrete subclass of the semi-abstract parent class. My RSS app Unread uses a subclass called UNRImageURLProtocol to route all image download requests through my own image caching system. A subclass of NSURLProtocol has to override several essential methods in order to be used. Mattt Thompson at NSHipster wrote a very informative article on this topic.

One Client, Many RSS Services

One of the architectural challenges I faced early on when developing Unread was the fact that there are so many different RSS services to integrate with. While broadly similar in user-facing features, there are numerous, non-trivial API differences that affect common tasks like refreshing all your feeds or marking an article as read. When a user pulls to refresh, a view controller needs to know how trigger a refresh call but it shouldn’t need to know what kind of RSS service is being used behind the scenes.

My final design followed the protected extension pattern. Every signed-in account owns an instance of a class called UNRClient. This class exposes some simple public methods like:

- (void)refresh:(UNRClientCompletionBlock)completion;

- (void)markArticleAsRead:(UNRArticle *)article;

- (void)markArticleAsUnread:(UNRArticle *)article;

UNRClient is mostly a concrete class. It handles things like keeping track of refresh state, access to API credentials, and — most importantly — queued and active API tasks. But every RSS service has a different set of steps to follow when refreshing. So in practice, UNRClient is only used when subclassed. A FeedWrangler account has UNRFeedWranglerClient, a NewsBlur account has UNRNewsBlurClient, and so forth.

UNRClient has a protected extension in a separate header file with required semi-private methods like:

- (UNRClientTaskGroup *)taskGroupForRefreshingAllFeeds:(void(^)(UNRClientTaskGroupCompletionItem *item))completion;

- (UNROutboundArticleStatusChangeItem *)changeItemForMarkingArticleAsRead:(NSString *)articleID;

I won’t go into the nitty-gritty details of UNRClientTaskGroup or UNROutboundArticleStatusChangeItem, but suffice it to say that these objects encapsulate units of work in an abstracted form. UNRClient knows how to enqueue these units of work, but it doesn’t ever need to know what happens inside them. Internally, the parent class has logic like this (simplified):

- (void)refresh:(UNRClientCompletionBlock) completion {
    UNRClientTaskGroup *group;
    group = [self taskGroupForRefreshingAllFeeds: completion];
    [self enqueueTaskGroup:group];
}

The magic happens in that call to taskGroupForRefreshingAllFeeds:. The super implementation does nothing. Subclasses override that method to return a task group (itself a concrete subclass of a semi-abstract parent class) configured to refresh all the feeds for its RSS service of choice.

To save myself from future mistakes, the super implementation looks like this:

- (UNRClientTaskGroup *)taskGroupForRefreshingAllFeeds:(UNRClientCompletionBlock)completion {
    NSAssert(NO, @"UNRClient subclasses must override taskGroupForRefreshingAllFeeds: without calling super. ");
    return nil;
}

This way, if another developer comes behind me and adds a new RSS service, she’ll quickly encounter an exception whenever a required protected method hasn’t been implemented by the new UNRClient subclass.

The beauty of this overall design is that the parent class is the only place where the complex, reusable logic is implemented. Any given subclass’s .m file is very short — just a handful of overrides of the protected extension methods.

Swift and Access Control Keywords

Swift is language in flux. It changes every few weeks. Apple recently added three access control keywords:

public
internal
private

These keywords are used to limit the visibility of any class, function, method, or property. Here’s how they work (as of August 6th, 2014):

Public access enables entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use public access when specifying the public interface to a framework.

Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.

Private access restricts the use of an entity to its own defining source file. Use private access to hide the implementation details of a specific piece of functionality.

So, for example, a Swift class might mix public and private methods as follows:

public class Vehicle {

    public func accelerate() {
        self.pushGasPedal()
    }

    private func pushGasPedal() {
        // do stuff
    }
}

Any class or module could instantiate a Vehicle and call:

var aVehicle = Vehicle()
aVehicle.accelerate()

But the following code would not compile if used outside of the Vehicle source file:

var aVehicle = Vehicle()
aVehicle.pushGasPedal()

Swift and the Protected Extension Pattern

Swift is still in beta, but there is no clean way — yet — to use the protected extension pattern in pure Swift code — not even with the new access control keywords. To illustrate why this is the case, let’s extend the Vehicle example above.

First, let’s define an enum for transmission types:

enum Transmission {
    case Automatic
    case Manual
}

Next, let’s implement the acceleration logic in the parent class:

public class Vehicle {
    private func transmissionType() -> Transmission {
        assert(false, "Subclasses must override.")
        return Transmission.Automatic
    }
    private func pushGasPedal() {
        // push the pedal
    }
    private func shiftUpOneGear() {
        // shift the gear up
    }
    public func accelerate() {
        var type = self.transmissionType()
        switch type {
        case .Automatic:
            self.pushGasPedal()
        case .Manual:
            self.pushGasPedal()
            self.shiftUpOneGear()
        }
    }
}

Finally, let’s implement two subclasses of Vehicle, overriding the one required method for subclasses:

public class FamilyCar: Vehicle {
    override private func transmissionType() -> Transmission {
        return .Automatic
    }
}

public class SportsCar: Vehicle {
    override private func transmissionType() -> Transmission {
        return .Manual
    }
}

This approximates the protected extension pattern as close as Swift will allow. Here are the reasons why the alternatives won’t work:

Declarations from extensions cannot be overridden **yet**

It’s that dangling “yet” that encourages me to believe that Apple’s engineers are aware of the design patterns like the protected extension pattern and will update Swift to support them.3 Personally, I’d like to see three more keywords: required, protected, and testable.

Usage of these would look like this:

public class Vehicle {
    required func transmissionType() -> Transmission {
        // subclasses must override without calling super
    }
    protected func didBeginDriving()  {
        // Any subclass source file can see this
    }
   testable func moveForwardThirtyFeet() {
        // do something inherently testable that 
        // should not be made public.
    }
}

Back to UIGestureRecognizer

A funny side-effect of Swift’s current access control options (or so I imagine) is that the UIGestureRecognizerSubclass.h extension header is not included in the UIKit Swift framework. In order to compile code that uses those methods, you have to #import that header file inside an Objective-C Bridging Header for your Xcode project.4


  1. Cough-cough—Vesper 1.0’s application background—cough-cough. 

  2. Or some other responsible controller class privately used by UIKit. 

  3. Another casualty of the current access control options is that private methods aren’t unit testable. This is a big problem in my opinion. Testable methods are often unsafe to expose publicly. 

  4. Thanks to Ash Furrow for the clarification on this. 

|  6 Aug 2014