movq

Wherein I Move a Lot of Words Around

Immutable Mutable

Type 'NSMutableArray' does not conform to protocol 'MutableCollectionType'

I really just have no words for that.

Mixed Objects in Swift Collections

At first the idea that a collection in Swift could only be one kind of object bothered me, but I’m slowly seeing a useful pattern emerging from this — one that Obj-C made tedious due to its grafting of an pass-by-reference-only object pattern on top of another language.

In the specific case that I just solved, for example, I have a table controller with a static list of choices and actions that would be performed when clicked. In Obj-C I’d probably do something like:

NSArray *choices = @[
    @{ @"name": @"Item 1", @"image": imageForItem1, @"action": @"action1" },
    …
];

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    …
    NSDictionary *choice = _choices[indexPath.item];
    [self performSelector:NSSelectorFromString(choice[@"action"])];
    …
}

… or something equally unsafe outside very specific parameters.

Swift, however, makes that a bit more difficult in a lot of ways. However, when writing in Swift it’s temping to do it in “The Swift Way” as well (namely so I can stop writing question marks and wrapping code in conditional optional statements). Therefore I wound up with something more like:

enum MenuAction : Int {
    case Action1
    case Action2
    case Action3
}

struct MenuChoice {
    var name : String
    var image : UIImage
    var action : MenuAction
}

let choices : [MenuChoices] {
    MenuChoice(name: "Action 1", image: imageForAction1, action: .Action1),
    …
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let choice = choices[indexPath.item]
    switch choice.action {
    case .Action1:
        action1()
    case .Action2:
        action2()
        …
    }
}

It’s certainly more safe. It’s quite a bit more verbose and explicit, but those are also the things that make it more safe. Could it be done in Obj-C like this? Mostly, yes. However, you can’t shove a struct into an NSArray and declaring one-off classes for such things is a bit tedious. Compare the above Swift to the following Obj-C syntax:

typedef NS_ENUM(NSUInteger, MenuAction) {
    MenuActionAction1,
    MenuActionAction2,
    MenuActionAction3,
};

@interface MenuChoice : NSObject
@property (nonatomic) NSString *name;
@property (nonatomic) UIImage *image;
@property (nonatomic) MenuAction action;
@end

@implementation MenuChoice
@end

First, there’s the Foundation macro NS_ENUM to deal with. You have to use this one if you want your enum to work in Swift as well as Obj-C. Then there’s the ever-present nonatomic keyword for properties, and then the repetitive @property itself.

Notice that MenuAction, being an NSUInteger, doesn’t get the * to say it’s a pointer. You’ll have to catch that every time you pull this trick, and you’ll get it wrong at least once.

Lastly, there’s the empty implementation to trigger the generation of the properties.

It’s kind of messy, no? Compare, again, to Swift’s version:

enum MenuAction : Int {
    case Action1
    case Action2
    case Action3
}

struct MenuChoice {
    var name : String
    var image : UIImage
    var action : MenuAction
}

Much cleaner, and it does the same thing. Exactly the same thing. Mostly the same thing. There’s one hidden feature of the Swift version: there will not be a nil for any of those properties of MenuChoice. If the struct exists, there will be a valid value inside.

I’m not sure how sold I am on Swift for a lot of things — the loss of dynamism means a lot of things are now less possible — but there are certainly places where it brings a lot of sanity, and that much I do like. I’m still writing about 50/50 between the two languages, but for a language to get to 50% this fast is noteworthy.