Let's say I'm writing an RSS reader. (With apologies to Brent Simmons.)

And let's say that it will support a number of different services. In order to make it easier to write different implementations for each service, I might write Feed and Folder protocols to allow them to share a common interface. The Folder protocol might look something like:

@protocol Folder <NSObject>

@property (readonly, strong) NSArray<id<Feed>> *feeds;
- (void)addFeeds:(NSArray<id<Feed>> *)feeds;
/**/

@end

With this protocol, I can implement concrete classes for each service, such as LocalFolder, FeedBinFolder, FeedlyFolder, etc.

But how do I test them?

Being Folder-compliant, all instances of these classes must maintain a common set of guarantees. For example, adding feeds with addFeeds: should cause those feeds to be accessible via the feeds property of the folder. That test could look like:

- (void)testAddFeed {
    id<Folder> testFolder = [self folder];
    id<Feed> testFeed = [self feed];
    [testFolder addFeeds:@[testFeed]];
    XCTAssert([testFolder.feeds containsObject:testFeed]);
}

I'd like to run this test against all the different types that conform to Folder, and this is made possible by test subclassing.

Here's a test superclass, filled with the tests I'd like to run on all my Folder-compliant types, configured by default to test LocalFolder:

@implementation FolderTests

- (id<Folder>)folder {
    return [LocalFolder new];
}

- (id<Feed>)feed {
    return [LocalFeed new];
}

- (void)testAddFeed {
    id<Folder> testFolder = [self folder];
    id<Feed> testFeed = [self feed];
    [testFolder addFeeds:@[testFeed]];
    XCTAssert([testFolder.feeds containsObject:testFeed]);
}

- (void)testAddThousandsOfFeeds {
    [self measureBlock:^{
        /**/
    }];
}

@end

The interface describes the methods that subclasses should overload:

@interface FolderTests : XCTestCase

- (id<Folder>)folder;
- (id<Feed>)feed;

@end

To test the other Folder-compliant types, I implement subclasses of FolderTests like this one, for FeedBinFolder:

@interface FeedBinFolderTests : FolderTests
@end

@implementation FeedBinFolderTests

- (id<Folder>)folder {
    return [FeedBinFolder new];
}

- (id<Feed>)feed {
    return [FeedBinFeed new];
}

- (void)testFeedBinSpecific {
    /**/
}

@end

At this point, I've written a total of three test methods. Running the test target, the console shows:

Test Suite 'All tests' started at 2015-07-31 11:30:55.855
Test Suite 'FolderTests.xctest' started at 2015-07-31 11:30:55.857
Test Suite 'FeedBinFolderTests' started at 2015-07-31 11:30:55.857
Test Case '-[FeedBinFolderTests testAddFeed]' started.
Test Case '-[FeedBinFolderTests testAddFeed]' passed (0.000 seconds).
Test Case '-[FeedBinFolderTests testAddThousandsOfFeeds]' started.
Test Case '-[FeedBinFolderTests testAddThousandsOfFeeds]' measured [Time, seconds] <…snip…>
Test Case '-[FeedBinFolderTests testAddThousandsOfFeeds]' passed (0.492 seconds).
Test Case '-[FeedBinFolderTests testFeedBinSpecific]' started.
Test Case '-[FeedBinFolderTests testFeedBinSpecific]' passed (0.000 seconds).
Test Suite 'FeedBinFolderTests' passed at 2015-07-31 11:30:56.351.
     Executed 3 tests, with 0 failures (0 unexpected) in 0.492 (0.494) seconds
Test Suite 'FolderTests' started at 2015-07-31 11:30:56.351
Test Case '-[FolderTests testAddFeed]' started.
Test Case '-[FolderTests testAddFeed]' passed (0.000 seconds).
Test Case '-[FolderTests testAddThousandsOfFeeds]' started.
Test Case '-[FolderTests testAddThousandsOfFeeds]' measured [Time, seconds]  <…snip…>
Test Case '-[FolderTests testAddThousandsOfFeeds]' passed (0.259 seconds).
Test Suite 'FolderTests' passed at 2015-07-31 11:30:56.611.
     Executed 2 tests, with 0 failures (0 unexpected) in 0.259 (0.260) seconds
Test Suite 'FolderTests.xctest' passed at 2015-07-31 11:30:56.611.
     Executed 5 tests, with 0 failures (0 unexpected) in 0.752 (0.755) seconds
Test Suite 'All tests' passed at 2015-07-31 11:30:56.612.
     Executed 5 tests, with 0 failures (0 unexpected) in 0.752 (0.757) seconds

I wrote three test methods, but XCTest ran five tests. If I wrote the most basic FolderTests subclass possible for FeedlyFolder, the effective test count would go from five to seven. If I then added a testRemoveFeeds test to FolderTests, it would be inherited by the test subclasses as well, raising the count further to ten.

This approach takes advantage of a powerful yet little known feature of XCTest; creating a test case subclass causes that class to inherit and run all of its ancestors' tests. This is really useful pattern for testing functional protocol conformances, such as our working example, or high level black box testing for classes that support configuration, like a database engine or compiler.


The example code behind the snippets in this post can be found here.

concrete protocols

I didn't know the term "concrete protocol" until fairly recently, but it was pretty easy to understand: a concrete protocol comes with default implementations for some of its methods. Foundation already flirts with the idea of concrete protocols with its collections' subclassing strategies—if you want to build your own array, you only have to implement -count and -objectAtIndex: in your NSArray subclass in order to support all of the other instance methods provided by the class. Written as a concrete protocol, it might look something like this:

@protocol NSArray <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@required
- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;

@provided
- (NSString *)componentsJoinedByString:(NSString *)separator;
- (BOOL)containsObject:(id)anObject;
- (NSString *)description;
- (NSString *)descriptionWithLocale:(id)locale;
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level;
- (id)firstObjectCommonWithArray:(NSArray *)otherArray;
- (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range;
- (NSUInteger)indexOfObject:(id)anObject;
- (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range;
- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject;
- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range;
- (BOOL)isEqualToArray:(NSArray *)otherArray;
- (id)firstObject;
- (id)lastObject;
- (NSEnumerator *)objectEnumerator;
- (NSEnumerator *)reverseObjectEnumerator;
// two dozen more methods…


Subscriptions and locks commonly come with APIs that require balanced calls, but they represent a subclass of a larger pattern of managing resources that must be freed. In the case of global notifications, the observer causes system resources to be allocated for notification dispatch, and that all needs to be cleaned up when the observer is no longer interested.

Another pattern of resource allocation is when dealing with file descriptors, which are easy to forget to close in sufficiently complicated codebases. A solution to this problem is an idiom known as Resource Acquisition Is Initialization, which NSFileHandle implements1. There are still some areas where this paradigm isn't present in Apple's APIs, such as NSDocument and NSMachPort, but today we're going to look at NSTimer.


1 NSFileHandle also implements close, the use of which leaves a useless dead object. If you find yourself using close, you have architectural problems.

ARC obliterated entire classes of bugs by automating memory management in acyclic contexts and providing zeroing weak references. Thanks to this, many classes no longer need to perform any cleanup during object destruction. The most common reason for dealloc these days? Notifications.

Broadcast messaging is vital to today's applications, but NSNotificationCenter has resisted modernization. Among its modern failings, its API enables fragile usage and it stores unsafe references to observers.

Karl and I were discussing the error-prone nature of APIs that require bracketed calls, like mutexes that conform to NSLocking1 and addObserver:selector:name:object:/removeObserver:name:object:. He posed the question: how do we get rid of bracketing APIs? To start with, how can we make NSNotificationCenter support weak observers?


1 Less error-prone approaches for mutexes include @synchronized and the modern APIs that take blocks

It's been a while since a photo post from our Europe trip, and the Derry portion would be best shared with a story. Especially like it because it illustrates how we travel. Not in vacation, but in life.

We arrived in Derry by bus, and made our way to the hostel to drop off our backpacks before exploring. An old city by anyone's standards, Derry was a focal point for The Troubles, during which time the original city walls were augmented with solid metal and chain link spans to keep the nationalists and the unionists apart, with checkpoints at the openings. If you're paying attention to your surroundings, it's an unsettling place. The banners celebrating the "City of Culture's" upcoming events struck me as an intentional distraction to attract tourism, a veneer over a far more interesting place.

While walking around the old walls that encircle the old city, we came across some youths that were burning a car. A fire truck had responded, and they'd forced it to turn back with thrown rocks. One of them threw a bicycle through the windshield, pulling it out soon after but not before the grease in the bearings had ignited. The others were kicking at the doors and body panels, jumping on the horizontal surfaces, and generally enjoying the destruction. I was carrying a 50 at the time, so I had to go down the hill and get a closer look. I made myself obvious before shooting, and they took the opportunity to cover up their faces.

Shortly after taking that photo my camera and I almost got taken out by a kid armed with a stick, and that emboldened the rest of them. I got out of there as calmly as possible to avoid inciting a chase as they yelled and threw rocks at me. Back at the top of the wall, one of the locals introduced himself to us, having decided from his vantage point that we were his kind of crazy. He worked as a community youth organizer—he recognized some of the kids from his program—with the goal of peacefully bridging the boundaries in the city. Basically, reducing the incidence of this kind of stuff. We got a walking tour from him that easily beat the other walking tours of the city.

Night fell, we ate and returned to the hostel to sleep. Morning came, and we descended the stairs to the lounge to discover that it had been ransacked. The hostel had been burgled and all of the electronics stolen, including the phone I'd hidden behind the computer to charge. Everyone was a bit off-balance, and we got to talking about our experiences the day before. Someone recommended a pub for us to visit. We noted it, and walked to the police station to file a report first.

We went to the police station to file a report and it was one of the oddest complexes I've ever visited, a modern fortress. If you arrived by car, you were allowed through an armoured blast door and wedge barrier into an enclosed space with another armoured blast door, which you weren't allowed through until the police were sure the vehicle was safe. The history of the Irish car bomb suddenly felt viscerally familiar. Walk-ups were subject to slightly more relaxed security, though I noticed that there was always a minimum of bulletproof glass between us and the officers on duty. We filed an unremarkable report that would never help us get any closer to our stuff.

The pub that we'd been directed to was fairly deep in a nationalist area of the city, and it fell silent the moment we entered. The bar was dark wood, the wall behind it mirrored, and the barkeep and clientele decidedly Irish. A massive flag of Ireland hung on the wall opposite the bar. There were two free stools, so we sidled up and ordered pints of Guinness and Smithwick's. The man sitting next to me broke the silence once we'd had a sip, and asked where we were from. Cognizant of our experiences over the last 24 hours I said, "I'm Canadian," to which he immediately replied, "oh, happy day!" and everyone in the pub fell back into easy conversations with each other. They paid for our drinks.

"

We shared our travel experiences thus far in Ireland, having visited both the nation and the territory, and got a local's perspective on things in return. The kids were mostly upset because they'd grown up in angry households, but the parents were violent for a cause; kids these days are angry, but the violence is senseless, untargeted. People were still fighting for nationalism, and a new form of IRA was coming about, but they were extremists and the real fight was being waged mostly with currency and politics, not weapons. As we asked questions, they increasingly responded with not answers, but stories. They made a point to note that they were all peaceful these days. What a strange thing to say. My instincts told me that we were onto something; at this point the parlance was especially curious, authoritative. Having made enough hints without a negative reaction, my new barmate took us to the flag on the wall and moved it aside, revealing a collage of photos from the 70s, 80s, and 90s. Promonently placed was a photo of him, a lit Molotov in one hand as he ran towards a British tank to scoop a child from its path. It looked like an image search for The Troubles in Derry, and the faces in those photos were recognizably sitting at the bar behind us. We learned history from the people who made it.


“It’s really hard for the government to censor things when they don’t understand the made-up words or meaning behind the imagery,” said Kevin Lee, COO of China Youthology, in conversation at the DLD conference in Munich on Monday. “The people there aren’t even relying on text anymore It’s audio, visual, photos. All the young people are creating their own languages.”Mike Isaac, AllThingsD via Bruce Schneier

Combined with an earlier article written by Schneier, speak to an unsurprising and systematic lack of understanding of technology by the government, and I think this scales well to any person in a position of leadership who doesn't really care about the issues at hand. Cybersecurity initiatives are a great way for representatives to gerrymander benefits for their constituents, but I'm not convinced that many of them actually understand what they're talking about.

My dad, a professor of engineering at SJSU, does care about the topics he teaches, and I'm often the target of his questions. Some time ago, he asked me to formalise a definition for The Cloud. First, my father is a more advanced troll than I ever realised.Second, exploring this was a journey though people's misunderstandings of technology—as you'd expect from formalising any buzzword.

The cloud used to be the server side of a client-server relationship, with an emphasis on server clusters and massive scalability. As the term caught on and became a buzzword, and as many of my friends and I were working at VMware at the time—a company that was in a unique place to provide private cloud solutions—we quickly started using The Cloud as an expletive when something was broken or someone was making buzzword gumbo. Thus I can now define The Cloud as:

  1. The component in a product or service which, when broken, cannot be fixed by even the most competent end user.
  2. Magical marketing fairy dust.