Protocol Oriented Programming

There was a really great talk at WWDC this year around Protocol-Oriented Programming in Swift. It did get me thinking though about how this is different from what we have today in ObjC, or even in a language like C

There is also a really good blog post talking about some of this by Marcel Weiher that makes for some good reading as well.

Ok, so the heart, as I understood the talk, is about thinking of your types as in the form of protocols instead of base classes. The fundamental idea is to get rid of one of the really nasty problems of OOP – implicit data sharing. That's great because that problem sucks.

It turns out, we can do this today in ObjC with one caveat – default protocol implementations. This is a feature that is new with Swift 2.0 and apparently wasn't worth bringing back to ObjC.

This code is inspired heavily by the Crustacean demo app that goes along with the WWDC video.

ObjC

Let's start with the renderer protocol:

@protocol KSRenderer <NSObject>
- (void)moveTo:(CGPoint)position;
- (void)lineTo:(CGPoint)position;
- (void)arcAt:(CGPoint)center
       radius:(CGFloat)radius
   startAngle:(CGFloat)startAngle
     endAngle:(CGFloat)endAngle;
@end

Ok. that looks simple enough.

Our sample KSTestRenderer will look like this:

@interface KSTestRenderer : NSObject<KSRenderer>
@end

@implementation KSTestRenderer
- (void)moveTo:(CGPoint)position
{
    printf("  moveTo(%f, %f)\n", position.x, position.y);
}

- (void)lineTo:(CGPoint)position
{
    printf("  lineTo(%f, %f)\n", position.x, position.y);
}

- (void)arcAt:(CGPoint)center
       radius:(CGFloat)radius
   startAngle:(CGFloat)startAngle
     endAngle:(CGFloat)endAngle
{
    printf("  arcAt(center: (%f, %f), radius: %3.2f,"
           " startAngle: %3.2f, endAngle: %3.2f)\n",
          center.x, center.y, radius, startAngle, endAngle);
}
@end

Alright, all looking good so far. Now it's time for all of the shape code.

@protocol KSDrawable
- (void)draw:(id<KSRenderer>)renderer;
@end

@interface KSPolygon : NSObject<KSDrawable>
@property (copy, nonatomic) NSArray<NSValue *> *corners;
@end

@implementation KSPolygon
- (instancetype)init
{
    if (self = [super init]) {
        _corners = [[NSMutableArray<NSValue *> alloc] init];
    }

    return self;
}

- (void)draw:(id<KSRenderer>)renderer
{
    printf("polygon:\n");
    [renderer moveTo:[_corners.lastObject pointValue]];
    for (NSValue *value in _corners) {
        [renderer lineTo:[value pointValue]];
    }
}
@end

@interface KSCircle : NSObject<KSDrawable>
@property (assign) CGPoint center;
@property (assign) CGFloat radius;
@end

@implementation KSCircle
- (void)draw:(id<KSRenderer>)renderer
{
    printf("circle:\n");
    [renderer arcAt:_center radius:_radius startAngle:0.0f endAngle:M_PI * 2];
}
@end

@interface KSDiagram : NSObject<KSDrawable>
@property (copy, nonatomic) NSArray<id<KSDrawable>> *elements;
- (void)add:(id<KSDrawable>)other;
@end

@implementation KSDiagram
- (instancetype)init
{
    if (self = [super init]) {
        _elements = [[NSMutableArray alloc] init];
    }

    return self;
}

- (void)add:(id<KSDrawable>)other
{
    [(NSMutableArray *)_elements addObject:other];
}

- (void)draw:(id<KSRenderer>)renderer
{
    for (id<KSDrawable> drawable in _elements) {
        [drawable draw:renderer];
    }
}
@end

Finally, we get to the usage code:

KSCircle *circle = [[KSCircle alloc] init];
circle.center = CGPointMake(187.5f, 333.5f);
circle.radius = 93.75f;

KSPolygon *triangle = [[KSPolygon alloc] init];
triangle.corners = @[ [NSValue valueWithPoint:CGPointMake(187.5f, 427.25f)],
                      [NSValue valueWithPoint:CGPointMake(268.69f, 286.625f)],
                      [NSValue valueWithPoint:CGPointMake(106.31f, 286.625f)] ];

KSDiagram *diagram = [[KSDiagram alloc] init];
[diagram add:circle];
[diagram add:triangle];

KSTestRenderer *renderer = [[KSTestRenderer alloc] init];
[diagram draw:renderer];

The output of this code is:

circle:
  arcAt(center: (187.500000, 333.500000), radius: 93.75, startAngle: 0.00, endAngle: 6.28)
polygon:
  moveTo(106.309998, 286.625000)
  lineTo(187.500000, 427.250000)
  lineTo(268.690002, 286.625000)
  lineTo(106.309998, 286.625000)

Of course, one of the "big wins" was the ability retro-fit a class to make apply conformance to some protocol. This is done in ObjC through a class extension.

First, we'll update the protocol so that it has something that can be opted-into. Note that this is where ObjC and Swift deviate. In ObjC we'll have to mark the selector as optional because we cannot define a default implementation for it.

@protocol KSRenderer <NSObject>
- (void)moveTo:(CGPoint)position;
- (void)lineTo:(CGPoint)position;
- (void)arcAt:(CGPoint)center
       radius:(CGFloat)radius
   startAngle:(CGFloat)startAngle
     endAngle:(CGFloat)endAngle;

@optional
- (void)circleAt:(CGPoint)center
          radius:(CGFloat)radius;
@end

Next, and again because Swift has a feature that ObjC doesn't, we need to update the draw: selector on the KSCircle class to handle selectively calling this optional circleAt:radius:.

- (void)draw:(id<KSRenderer>)renderer
{
    printf("circle:\n");
    if ([renderer respondsToSelector:@selector(circleAt:radius:)]) {
        [renderer circleAt:_center radius:_radius];
    }
    else {
        [renderer arcAt:_center radius:_radius
             startAngle:0.0f endAngle:M_PI * 2];
    }
}

Lastly, a new extension to the KSTestRenderer is in order to retro-fit the class with new functionality.

@interface KSTestRenderer (Hacky)
@end

@implementation KSTestRenderer (Hacky)
- (void)circleAt:(CGPoint)center radius:(CGFloat)radius
{
    printf("  circleAt(center: (%f, %f), radius: %3.2f)\n",
           center.x, center.y, radius);
}
@end

Now the output of our program is this:

circle:
  circleAt(center: (187.500000, 333.500000), radius: 93.75)
polygon:
  moveTo(106.309998, 286.625000)
  lineTo(187.500000, 427.250000)
  lineTo(268.690002, 286.625000)
  lineTo(106.309998, 286.625000)

As you can see, the way in which a circle is rendered is now different because of our class extension. Making a CGContextRef would be as straight forward as applying the protocol extension to it and implementation the methods as done in Swift, except… that is a Swift-only feature too that extensions can be applied to non-ObjC types.

C

Things are a little more interesting in the C is unable to extend classes without creating a new type. Though… I'm not sure that is necessarily a bad thing (more on that later).

Anyhow, here's the full (very rough) code sample for C

struct Renderer {
    virtual void moveTo(CGPoint position) = 0;
    virtual void lineTo(CGPoint position) = 0;
    virtual void arcAt(CGPoint center, CGFloat radius, CGFloat startAngle, CGFloat endAngle) = 0;

    virtual void circleAt(CGPoint center, CGFloat radius) {
        arcAt(center, radius, 0.0f, M_PI * 2);
    }
};

struct TestRenderer : public Renderer {
    void moveTo(CGPoint position) {
        printf("  moveTo(%f, %f)\n", position.x, position.y);
    }

    void lineTo(CGPoint position) {
        printf("  lineTo(%f, %f)\n", position.x, position.y);
    }

    void arcAt(CGPoint center, CGFloat radius, CGFloat startAngle, CGFloat endAngle) {
        printf("  arcAt(center: (%f, %f), radius: %3.2f, startAngle: %3.2f, endAngle: %3.2f)\n",
               center.x, center.y, radius, startAngle, endAngle);
    }
};

struct Drawable {
    virtual void draw(Renderer &renderer) = 0;
};

struct Polygon : public Drawable {
    std::vector<CGPoint> corners;

    void draw(Renderer &renderer) {
        printf("polygon:\n");
        renderer.moveTo(corners.back());
        for (auto p : corners) { renderer.lineTo(p); }
    }
};

struct Circle : public Drawable {
    CGPoint center;
    CGFloat radius;

    void draw(Renderer &renderer) {
        printf("circle:\n");
        //renderer.arcAt(center, radius, 0.0f, M_PI * 2);
        renderer.circleAt(center, radius);
    }
};

struct Diagram : public Drawable {
    std::vector<Drawable *> elements;
    void add(Drawable *other) {
        elements.push_back(other);
    }

    void draw(Renderer &renderer) {
        for (auto e : elements) {
            e->draw(renderer);
        }
    }
};

struct TestRendererExtension : public TestRenderer {
    void circleAt(CGPoint center, CGFloat radius) {
        printf("  circle(center: (%f, %f), radius: %3.2f)\n", center.x, center.y, radius);
    }
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Circle circle;
        circle.center = CGPointMake(187.5f, 333.5f);
        circle.radius = 93.75f;

        Polygon triangle;
        triangle.corners.push_back(CGPointMake(187.5f, 427.25f));
        triangle.corners.push_back(CGPointMake(268.69f, 286.625f));
        triangle.corners.push_back(CGPointMake(106.31f, 286.625f));

        Diagram diagram;
        diagram.add(&circle);
        diagram.add(&triangle);

        TestRenderer renderer;
        diagram.draw(renderer);
    }
    return 0;
}

The above code shows the default implementation of circleAt that can be applied to all types that inherit this base class.

NOTE: A protocol (or interface) is really just a definition of functionality with no member data.

In the C

TestRendererExtension renderer;
diagram.draw(renderer);

Wrapping Up

I think the important takeaway from the Swift talk is not really about a "new paradigm" of programming, but rather showing a better way to compose software using techniques that we already use day-to-day. It makes it easier to do the better thing (get rid of accidental data sharing between types) and reducing the boiler-plate code required to do it.

There is one thing I'm worried about though: the class extension seems to be creating a very similar problem in that we are getting rid of unintentional data sharing between type hierarchies and replacing it with potentially unintentional functional changes in our programs.

Imagine a more complicated set of protocols and types interacting and along comes a protocol extension for a type that overrides a default function for the protocol and now your output went from:

circle:
  arcAt(center: (187.500000, 333.500000), radius: 93.75, startAngle: 0.00, endAngle: 6.28)

To this:

circle:
  circleAt(center: (187.500000, 333.500000), radius: 93.75)

To me, this is smelling a lot like the fundamental issue we saw with data sharing between types – unintended side-effects. For this reason alone, I think I prefer the C

Protocol Oriented Programming

Swift Throws – I Get It

It took me a little while for me to really appreciate the design of throws. At first, it was sheer disbelief that the Swift team created exceptions. Then it was mild irritation that a Result<T, E> type wasn't used.

Now, I really like it.

I see a lot of discussion about it (and some with comments on my blog) and different proposals being made. One of the things I keep seeing being missed though, is that throws forces you to be explicit about handling, ignoring, or bubbling the error back up.

The thing that really won me over was remembering how many times I had to link to this guide: ObjC Error Handling. For me, I've been used to it for so long that it's easy to forget just how much convention there is there. I've also seen some pretty amusing code that is attempting to use NSError but it being so wrong.

It is a good thing that Swift has both codified and simplified the error handling conventions of Cocoa.

Also, there is no more ambiguity on how to handle this case:

- (int)ohCrapAScalar:(NSError **error) {
    // So... what magic value do I use this time?
}

With Swift, it's a non-issue (unless you're bridging with ObjC – rdar://21360155):

func scalarNoProblem() throws -> Int {
}

The other complaint I see is around async… well, I honestly thing that is a point-in-time problem. I think Swift will get something like await, and it seems it would be natural to have this:

try {
    await try someAsyncThrowingCode()   
}
catch {
    fatalError("crap")
}

Of course, that doesn't cover all async cases, but you get the idea.

Swift Throws – I Get It

Xcode UI Testing

Let's talk briefly about testing.

Xcode 7 is coming out with a great feature, XCTest UI tests. Now, some of you might not know, but we've been able to do UI testing for iOS apps for quite a while now using Instruments and the UIA Instrument. If tried that, you know that tool was terrible.

Now, UI testing is a fine approach to testing, but don't be fooled by the awesome demos – UI testing is the most expensive way to author tests. They are the hardest to maintain, they are the hardest to debug, and they are the slowest to run. Now, that does not mean that you should not write them, they just shouldn't be your primary way of testing your app.

Let's look at a simple test from the Lister demo app.

func testExample() {
    let app = XCUIApplication()
    app.tables.staticTexts["Groceries"].tap()

    let addItemTextField = app.tables.textFields["Add Item"]
    addItemTextField.tap()
    addItemTextField.typeText("Burgers")
    app.typeText("\r")

    XCTAssert(app.tables.cells.textFields["Burgers"].exists)
}

The test simply taps on the "Groceries" list, adds a "Burgers" item, and verifies that the item is indeed in the list.

Notice a problem?

Now, this is where we can get into a bit of a philosophical debate about testing and verification. The question is, do we want the UI tests to only verify that the UI is correct? Or, do we want our UI tests to validate that both the UI and the model is correct?

For me, I've seen far too many bugs where the UI was updated but the model didn't update to be satisfied with only validating the UI in our tests.

So, what do we do? The primary problem is that these UI tests are running outside of the process, and in the case of tests on the device, they aren't even running on the same machine. Don't worry, we have options!

The basic requirement is to be able to send a message of some sort to the application under test (AUT). Our options:

  1. Make use of the Accessibility APIs
  2. Make use of the network

My philosophy for building test infrastructure is to build it the cheapest, most simplest way possible for your needs at the time. When you have expensive test infrastructure, it can be a significant cost to update as your needs change later, and they always do.

So, instead of building up a mini-server that runs in your app, I would just fake it and create a UIView/NSView with a simple text field that's shown in response to some gesture. The purpose of this view is to provide a mechanism to provide a command layer into your product accessible via your test.

A command might even be as simple as a string: "get model items". When ENTER is pressed, you'd have a handler for the event that would handle each command and replace the contents of the text field with the result, such as: "Burgers, Apples, Oranges, Bananas, Milk, Bread".

Now, in your test, you can simply get the value of the text field and XCTAssert that the value matches the string.

You might be thinking that this is a bit of a hack, and well, you're right. However, it ends up being extremely cheap to maintain and update this model as it is so extremely easy to get up and running. Adding commands is literally as simple as registering the command name with a block on the view controller as well. And, if you really want to, you can compile out all of this stuff in App Store builds.

Xcode UI Testing

Swift v2.0 Error Handling – Revisit

Last night I posted my initial thoughts about Swift's approach to error handling. I've softened a little to the approach as I got to play with it a bunch this morning, including re-visting what a Result<T, E> looks like in Swift.

It's only after going through that excercise again that reminded me just how clunky dealing with enum values are, especially those that contain values, that I can appreciate why the Swift team went in the direction they did.

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

enum Result<T, E> {
    case Ok(T)
    case Error(E)
}

func base() -> Result<(), MyCustomError> {
    return Result.Error(MyCustomError.Dance)
}

func handle() {
    let result = base()

    switch (result) {
    case .Ok(_):
        println("required...")

    case let .Error(error):
        switch (error) {
        case MyCustomError.Happy:
            print("Happy error")

        case MyCustomError.Dance:
            print("Dance error")
        }
    }
}

Or, with the Swift error handling approach.

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

func base() throws {
    throw MyCustomError.Dance
}

func handle() {
    do {
        try base()
    }
    catch MyCustomError.Happy {
        print("Happy error")
    }
    catch MyCustomError.Dance {
        print("Dance error")
    }
    catch {
        print("catch all, because no types")
    }
}

Of course… those aren't our only options though. See, there was this lovely little keyword guard that was also introduced to us. So, by putting some better smarts into Result<T, E>, we can get something that looks like this:

enum MyCustomError: ErrorType {
    case Happy
    case Dance
}

enum Result<T, E> {
    case Ok(T)
    case Err(E)

    // what if these were simply generated for all enums?
    var ok: T? {
        switch self {
        case let .Ok(value): return value
        case .Err(_): return nil
        }
    }

    var err: E? {
        switch self {
        case .Ok(_): return nil
        case let .Err(e): return e
        }
    }
}

func base() -> Result<(), MyCustomError> {
    return Result.Err(MyCustomError.Dance)
}

func handle() {
    let result = base()

    guard let value = result.ok else {
        switch result.err! {
        case MyCustomError.Happy:
            print("Happy error")

        case MyCustomError.Dance:
            print("Dance error")
        }

        return
    }

    print("value: \(value)")
}

Swift's throws approach is still more terse, but provides no type-safety on the error type. It might be possible to stream-line the approach above some more, but it feels better to me. I never liked exceptions used as code flow control and the do-try-catch approach seems to just beg for that.

Eh… I'll keep noodling.

Swift v2.0 Error Handling – Revisit

Swift v2 – Error Handling First Impressions

UPDATE June 9th, 2015: Go watch the 'What's New in Swift' talk. It answered nearly all of my concerns below.

Swift v2.0… honestly, I was a bit surprised by many of the updates to the language, especially the error handling. There's a session on Tuesday to talk about many of the changes in depth, but I'm pretty mixed about the update.

The worst update, in my opinion, is the error handling. It's basically a pseudo-exception system.

enum SomeException: ErrorType {
    case Oops
}

func bad() throws {
    throw SomeException.Oops
}

func good() {
    do {
        try bad()
    }
    catch SomeException.Oops {
        print("There was an exception")
    }
    catch {
        print("This is required because...")
    }
}

Ok, so what do we have up there? Well, we have a function bad that is marked as throwing an exception, and we have a function good that makes use of calling that function and handling the exception.

Already I'm seeing problems that I don't know if can even be solved.

  1. No type information about the type of errors that can happen from bad. This strikes me as extremely odd from a language that is all about type-safety.
  2. A catch-all catch statement is required – I don't know if Swift can even fix as it doesn't seem to have enough information to determine all possible types to be thrown, especially for deep function calls.
  3. Verbose with needing both do to scope your catch calls with and try to prefix the call to functions that can throw.

If you want to simply pass the buck on the error, that's pretty simple to do:

func good() throws {
    try bad()
}

Now… this is where things get interesting. There is a whole section about stopping the error propogation. If you know (famous last words…) that the function you are calling will not throw, you can do this:

func goodish() {
    try! bad()
}

This is the rough equivalent of force-unwrapping your optionals. This is not great, in my opinion. The really bad part is that if you use code that does this, there is no way for you handle the exception that it can throw.

func goodish() {
    try! bad()
}

func nogood() {
    do {
        try goodish()
    }
    catch {
        print("sorry, never going to happen")
    }
}

Let's say that goodish actually does throw in an edge-case that the programmer missed. Well, if that's the case, too bad for you; I sure hope you have access to that code so you can fix it. In fact, the code above will issue you a compiler warning because goodish() isn't marked as throws.

Ok, so you get the idea to try this:

func better() throws {
    try! bad()
}

func nogood() {
    do {
        try better()
    }
    catch {
        print("sorry, never going to happen")
    }
}

Nope… still going to get a runtime error.

The reason? I'm speculating here, but it seems like Swift is simply adding a bunch of compiler magic to inline the error information as a parameter to the function. That information is thrown away when you call try! and will not be propogated out. The nice benefit of this is you can get an error-handling system that has great runtime performance. However, the downside, is that you've built a system that codifies that correctness is up to the programmer now and not verifiable by the compiler as much as it could have been.

I would have much rather seen a system that codifies the error as the return type and forces handling of it, as in Rust. I thought that was the direction we were heading with Optional<T> when it was introduced with Swift 1.0.

Who knows, maybe it will grow on me, but I think I may be sticking with the Result<T, U> pattern.

Swift v2 – Error Handling First Impressions