Swift Diary #7: Protocols, Arrays, and Casting

I mentioned before that I’m making heavy use of protocols in my new apps.

Here’s what got me today:

Let’s pretend I’m writing an email client. (This is an example I use on my blog for historical reasons, and should not be taken as indicative.)

I have a tree of Node objects. Each Node has a represented object, which is a data model object. There’s a NodeRepresentedObject protocol.

There are also more-specific protocols. EmailMessage, for instance, is a protocol because I might need different concrete classes for different types of accounts (Gmail, IMAP, POP, etc.).

My class Mailbox has its internal array of email messages. It also has a children variable which is part of the NodeRepresentedObject protocol. Here’s the relevant code:

protocol NodeRepresentedObject : class {
  var children: [NodeRepresentedObject]? {get}
}

protocol EmailMessage : NodeRepresentedObject {
  // stuff here…
}

class Mailbox : NodeRepresentedObject {
  var messages = [EmailMessage]()
  var children: [NodeRepresentedObject]? {
    get {
      return messages
    }
  }
}

The error is: 'EmailMessage' is not identical to 'NodeRepresentedObject'.

That’s true: it’s not. But an EmailMessage is a NodeRepresentedObject, so it ought to work just fine.

I tried this:

return messages as [NodeRepresentedObject]

The error: '[EmailMessage]' is not convertible to '[NodeRepresentedObject]'.

Why not? Again — it is a NodeRepresentedObject.

The best way I’ve found to deal with this is to map it.

return messages.map { return $0 as NodeRepresentedObject }

That seems crazy. I’m using map to create an array whose members are absolutely identical to the original array. And if it would let me do that, why not allow my original solution? (Or at least the second solution?)

Swift Diary #6: Window Controller Initialization

I’m working on UI code — and the below took me a half hour and quite a bit of frustration to figure out, so I thought I’d better write it up.

Here’s the deal: I want my window controllers (and view controllers too) to know about their nib name, but the outside world should not know about their nib name.

Here’s how I handle this in Objective-C: the NSWindowController’s subclass has an init method that references the nib name.

- (instancetype)init {
  return [self initWithWindowNibName:​@"SomeNibName"];
}

To instantiate the window controller from elsewhere, I just do [SomeWindowController new]. (Could be alloc/init, but I like the fewer characters this way.)

Here’s how to do it in Swift:

convenience init() {
  self.init(windowNibName: "SomeNibName")
}

And then I can instantiate the window controller via SomeWindowController(), and all’s well.

Now that I have the answer, it looks so simple. Many Bothans died, etc.

PS Yes, I’m using storyboards, except for the few things that should be nibs.

Swift Diary #5: Yet More Uses for KVC

I’ve argued before (here and here) that pure Swift objects and structs need KVC or something like it.

I keep running across real-world uses. I present two more.

Templates

In NetNewsWire you could create a custom theme, which was a CSS file, an HTML template, and, optionally, some assets.

Inside a template you could type things like [[newsItemTitle]], and at render-time that string would be replaced with the title.

How this was done:

The template renderer took two objects:

  1. A string — the template itself. (The thing that included some HTML and code like [[newsItemTitle]].)

  2. An object, typed as id.

The template renderer worked by scanning the template. Every time it found something in double-brackets — [[whatever]] — it pulled out the whatever part (call it the “tagName,” for lack of a better word) and got the replacement string like this:

replacement = [obj valueForKey:tagName];

Then it would replace [[whatever]] with the value of replacement.

What was cool about this was that the template renderer didn’t know anything about HTML or RSS. It didn’t know anything whatsoever about the passed-in object that generated the replacement strings. The system was uncoupled and reusable.

It knew how to scan through a string and replace [[something]] with a call to valueForKey:, and that’s it.

Would I love to be able to do the same thing with a Swift struct as the object? You bet. This is a great case for a struct.

Object Comparison and Updating

Say I’m writing an RSS reader or Twitter client — or something like Vesper or Glassboard — where the app makes calls to a web service to get a bunch of objects, then it compares those incoming objects to objects it already has and updates them as needed.

This code doesn’t have to be a pain to write. In fact, you can write it once and reuse it in every single app.

You need a comparison method that takes two objects — typed as id — and a set of property names to check.

+ (void)updateExistingObject:​(id)existingObject withServerObject:​(id)serverObject propertyNames:(NSSet *)propertyNames;

Loop through propertyNames. For each:

id oneExistingValue = [existingObject valueForKey:​onePropertyName];
id oneServerValue = [serverObject valueForKey:​onePropertyName];
if (![oneExistingValue isEqual:oneServerValue]) {
  [existingObject setValue:oneServerValue forKey:​onePropertyName];
}

Note that existingObject and serverObject don’t have to be the same type. serverObject could even be a dictionary. Whatever. It doesn’t matter, as long as the property names match up.

Also note that you could easily extend this to return a BOOL saying whether or not existingObject actually changed. This can be very useful if you need to conditionally save a database or plist or update the UI.

You can go a step further and have it return the set of property names that changed. If you have a custom persistence system, this could mean less work your database has to do when updating existingObject in the database. (For instance: you could generate SQL that updates the values only for those changed property names.)

This is also just a few lines of code away from a syncing system like the one in Vesper. In Vesper the various properties have a modification date property. (For instance: note.text and note.textModificationDate.)

The above code would be modified like this:

  1. In the case where values aren’t equal, it then constructs the modification date property name from the property name (by appending @"ModificationDate", so that you have textModificationDate and similar).

  2. Then it does valueForKey: on both objects to get that date. It compares the dates. Whichever value has the later date is the winning value.

And then you could write 100 apps that all use this same code.

(To bring it back to Swift: serverObject ought to be a struct.)

Trade-offs

The compiler isn’t going to catch some possible errors. But I’m willing to put up with that because of the other benefits — these systems are uncoupled and highly reusable, and they mean writing and maintaining less code.

I don’t suggest for one second that KVC should be used everywhere and all the time. I love having the compiler catch bugs. But there are often some small-but-important spots like these where KVC is the best choice.

Update 10:25 am

I had thought it was obvious, but beyond the scope of this post, to point out that in the first pattern you need some constraints and you need to handle undefined keys.

This is pretty easily done. The object that generates replacement strings could be a dictionary. Or you could override valueForKey and valueForUndefinedKey. That object certainly should not have any methods other than what’s needed to generate the replacement strings.

In the end you might think it’s safer to base the system entirely on dictionaries and use objectForKey instead (especially when there’s a chance that the templates include user data). Totally fair.

Swift Diary #4: KVC Again

Al Skipp wisely writes (in reply to my post about KVC):

It's still the case that Stringly typed code is a pain in the Aris to write/debug/maintain.

True. So true.

But let me remind you of a place where most iOS and Mac developers do it all the time: Interface Builder.

Interface Builder is a Very Fancy XML Editor

Some developers eschew Interface Builder (IB) altogether. That’s fair. I myself don’t automatically reach for it — there are times when a UI is so simple that it doesn’t need it, or it’s too dynamic to be expressed well in IB. So I at least think about it before I go there. But I do use it plenty often.

And I’d bet that most developers use IB at least a little bit, if not a lot, in their apps. (Though maybe not for games.)

And what you do you do in IB? You type (or copy-and-paste) in the class name of your view controller. You click checkboxes and set attributes. In the end you’re just setting a bunch of strings that gets stored in an XML file.

In the end, you get things like this:

<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="gear" imagePosition="only" alignment="center" borderStyle="border" inset="2" id="1194" customClass="TransparentButtonCell">

You don’t want to do edit that directly, of course, so you have IB. But this is still what you end up with.

Runtime

Nib loading uses KVC. Though it may not use setValue:forKey: directly, it does the conceptual equivalent.

That does not mean that you can only interact with your objects via valueForKey: and setValue:forKey:, of course. It’s just that instantiating uses KVC. Your own code is still foo.bar = 7 and so on.

The alternative to KVC (or something much like it) — commence shuddering now — would be code generation instead. (Those of you who’ve been through that on other platforms, come out from under your desk. It’ll be okay.)

Trade-offs

Of course, it is a pain when you’ve mistyped — or just plain forgot to type — the name of a class in IB. That’s a drawback of any system like this.

But we’re willing to make the trade-off because the productivity gain we get from using IB is worth the occasional issue like this. The nice thing about bugs like that is that they’re usually caught very quickly, and are just a normal part of the development process. It’s what we do for a living.

If IB can do it, why can’t I?

It’s a pretty nice system. I get to declare things rather than write code — and though I have to take care that my declarations are correct, I still end up saving time and writing and maintaining less code. It’s a win.

The Core Data modeler is another example. I don’t have a handy example, but I bet it generates XML (or maybe JSON or, more likely, plists). And the runtime uses KVC and KVO (and method generation, which is another topic) to make it all work.

It’s only natural that I might want to write similar systems for my own use. And I have. In the case of the database and model object code that I’ve written, I don’t have a fancy modeler — I just edit a plist. Though editing is more raw, the concept is the same: I type in class names and map database columns to types and property names.

And my code — which I can reuse unchanged in as many apps as I want to — uses KVC to put it all together. As IB does.

The rest of my code — the code outside my database code — doesn’t use KVC. I still write foo.bar = 7, rather than [foo setValue:@7 forKey:@"bar"]. I can rely on the compiler to tell me when I’ve made an error in the rest of my code.

So, in the end, this stringly-typed database/model-object code is way less of a pain to write, debug, and maintain.

In fact, I don’t touch it at all, since it’s debugged and working.

But I’d still love to make it work with pure Swift objects and structs.

Swift Diary #3: Arrays, Protocols, and Pointer Equality

Let’s say I’m writing a Finder replacement. (Disclaimer: previous statement may or may not be true.)

The common representation of the file system in a GUI app is a tree. A common pattern, then, is to create a tree data structure. The tree is made of Node classes, and each Node has a representedObject which conforms to the NodeRepresentedObject protocol.

A representedObject may be a Folder or a File. All the Node class knows is that its representedObject conforms to NodeRepresentedObject.

Each Node has zero or more children. The children are Node objects also. The children’s representedObjects are a mix of Folders and Files.

* * *

Here’s the trouble I ran into. Given a representedObject and an array of Nodes, I wanted to find the Node with that representedObject.

Things to know: the representedObjects are uniqued (a file system folder has one Folder object; a file system file has one File object). And, because they’re uniqued, they’re also objects rather than structs.

Because this is true, I can use pointer equality on representedObjects.

The Objective-C code might look like this:

- (Node *)existingChild​WithRepresentedObject:​(id<NodeRepresentedObject)​representedObject {
  for (Node *oneNode in self.children) {
    if (oneNode.representedObject == representedObject) {
      return oneNode;
    }
  }
  return nil;
}

Pretty simple.

* * *

My first go at doing this in Swift looked like this:

func existingChild​WithRepresentedObject​(representedObjectToFind: NodeRepresentedObject) -> Node? {
  for oneChild in children {
    if oneChild.representedObject === representedObjectToFind {
      return oneChild
    }
  }
  return nil
}

This didn’t compile: I get the error error: binary operator '===' cannot be applied to two NodeRepresentedObject operands.

“But,” I thought, ”shouldn’t pointer equality always work?”

Well, no. Not if representedObject is a struct.

So it occurred to me that I have to tell Swift that these are both objects, and it works:

if (oneChild.representedObject as! AnyObject) === (representedObjectToFind as! AnyObject)

* * *

But I don’t like using as! — as Rob Rhyne writes, “But every time you type !, someone at Apple deletes a Radar.”

So I figured there must be a better way. And there is.

  1. Keep the original line as it was: if oneChild.​representedObject === representedObjectToFind.

  2. Make NodeRepresentedObject conform to the AnyObject protocol, which lets the compiler know that representedObject is an object, so that pointer equality testing will work:

protocol NodeRepresentedObject: AnyObject {

It works. See the gist.

* * *

Here’s the part I don’t totally like: to make this work, it required bringing in Objective-C. AnyObject is an @objc protocol.

So here’s my question: is there a way to do this without bringing in Objective-C?

(I feel like there are two Swifts. Swift + Objective-C is like writing Objective-C with a new syntax. Pure Swift is another thing. I’m pragmatic, and I’ll use the former as needed — but I want to learn the pure Swift solutions.)

* * *

Update 10:15 am: the answer came quite quickly from Matt Rubin on Twitter and others:

try: protocol NodeRepresentedObject: class { … }

Done! It works.

* * *

Update 10:40 am: and there’s also this, which I didn’t know, from Joe Groff on Twitter:

AnyObject is marked '@objc' for obscure implementation reasons, but it is a native Swift feature.

How to Hang Mail, TextEdit, and Contacts

We often get crash reports where the crash happens outside our code and we can’t reproduce it. (Like every developer, I’d bet.)

While our users often tell us what they were doing when submitting a crash log — thanks! it helps a ton — those steps don’t always have the one key thing we need to be able to reproduce the crash.

But today we got one from a helpful OmniGraffle user: hit the Return key really, really fast.

We tried it, and we reproduced the crash. Then reproduced it in other of our apps, then reproduced it in Mail, TextEdit, and Contacts. (And stopped there.)

So I decided to title this post “How to Hang Mail, TextEdit, and Contacts,” because it’s more universal than “How to Crash OmniFocus.” But I’d bet this works on a fair number of apps, Apple’s and not Apple’s.

Before you try it, fair warning: you will have to force-quit TextEdit. When I did this on 10.11, things went weird and I had to reboot.

Here’s what to do:

  1. Launch TextEdit.

  2. Start a new document.

  3. Type a few characters.

  4. Save it to your Desktop.

  5. Choose File > Export as PDF… and have it export to your Desktop.

  6. Choose File > Export as PDF… again — and select the file you just exported to. (As if you’re going to overwrite it.)

  7. Hold down the Return key. TextEdit will hang after a few seconds. You’ll have to force-quit it.

  8. Open Console. You’ll see something like this:

7/22/15 3:28:25.441 PM TextEdit[866]: *** Assertion failure in -[NSWindowCentricRemoteView maintainProcess​NotificationEventMonitor:], /SourceCache/​ViewBridge/​ViewBridge-105/​NSRemoteView.m:1356

Note: to make this work, you may have to have your Keyboard key rate set fairly high in System Preferences > Keyboard. (Mine is at second-fastest.)

Also note: the PDF part is a red herring. It has nothing to do with PDF. That just happened to be a handy example.

Also note: you can do the same thing use Save As… in TextEdit and get a slightly different weird behavior.

rdar://21949944

Update 4:20 pm

It may not hang on 10.11 b4. You might see some display strangenesses — like black rectangles where a sheet used to be. And you may see notes in Console from com.apple.appkit.xpc.​openAndSavePanelService.

Swift Diary #2: KVC

I was very glad to hear that performSelector and friends are supported in Swift in the latest Xcode 7 beta. But what I want to talk about today is a similar thing: Key-Value Coding (KVC).

I use KVC in a critical part of my apps: the part that creates model objects from database rows.

The database code is generalized and shared by multiple apps. I create a mapping (using a plist) that maps SQLite table names to property names and types.

The object creation code goes through the mapped property names and does the right thing for each name. In the end it boils down to something like this:

[modelObject setValue:​someValueFromDatabase forKey:​propertyName];

(This even works for non-object properties, which is brilliant on the part of KVC’s designers.)

I can do this in Swift, too. (Note that this is unchecked code, and I could have the syntax wrong.)

modelObject.setValue​(someValueFromDatabase, forKey:propertyName)

There are plenty of other places where KVC makes sense. (It’s part of a hearty, dynamic breakfast.)

Another example might be mapping NSTableView cells to values — a columns’s identifier might be configured to be the same as a property name, and so it’s easy to get the desired value:

modelObject.valueForKey​(columnIdentifier)

The coupling here is quite loose, and the compiler isn’t going to tell you if you’ve mistyped a property name. You’ve got all kinds of rope here.

But I’m totally comfortable with that. Here’s the deal I’m willing to make: to make my code more elegant (read: smaller, easier to maintain, reusable), I’m willing to let it crash if I’ve configured things incorrectly.

A bug here — say there’s a typo in a property name in my database mapping — will mean an instant crash, as the object has no such property name. I’m good with that. Crashes like that have zero chance of living beyond the time it takes me to build and run.

* * *

If this is available in Swift, then why am I writing about it?

Because this is not a pure Swift thing: it’s a Swift + Objective-C thing. You can’t do this with structs, for example.

I want to get the advantages of structs. There are plenty of cases where my model objects could be structs. Structs are cool.

We’re in a transition period right now. Swift needs Objective-C so that people who rely on the dynamic nature of Objective-C — like me, like many developers — can write their apps.

It would be best, though, if Swift didn’t have to carry Objective-C baggage with it forever in order to make it adequate for the job of app-writing. I don’t think it will, even.

So my case is that Swift itself needs things like this. If not a clone of KVC, then something very much like it.

Manton News

The esteemed Mr. Reece has gone indie!

Just the other day I described Manton and me as litter-mates. I’m proud of him for taking this leap.

Swift Diary #1: Class or Struct from String

Like Mike Ash and his Friday Q&A blog posts, I take requests from readers. Unlike Mike Ash, all I have is questions.

This one comes from my co-worker Jim Correia, and is related to a question I posted on Twitter last night about instantiating an object whose class is known at runtime rather than compile time.

For reference, here’s the answer to last night’s question.

Jim takes this one step further: what if all you have is the class name as a string?

This isn’t an academic question — it’s something app developers do. For example, do a Show Package Contents on any Omni app and look inside Info.plist — you will likely find class names in the plist. (Example: see OFControllerClass in OmniFocus’s plist. Also look inside the OIInspectors array.)

This is a useful pattern when you have a general framework of some kind and you want to configure it for a specific app. The framework doesn’t know which specific classes to use, so you tell it.

And then, in the code, at some point it comes down to doing an NSClassFromString(s) to get a class, and then instantiating an object.

I want to do the same thing in Swift, but with additional criteria:

  • I don’t want to have to mark the class an Objective-C class. I want to use pure Swift.

  • I want to be able to use structs interchangeably with classes. Whether the thing is a class or a struct is a detail that’s private to the class or struct, and not something the general framework needs to know.

I realize that this may seem marvelously unsafe to some people. But, in practice, it’s not. And it’s super-common. (And it’s not terribly different from working with Interface Builder, where you configure by typing in class names.)

I’ll update this post with the answer. (I assume there is one.)

* * *

Update 10:05 am: @jckarter writes:

Possible, but not implemented yet.

Secret Projects Diary #3: Decision to Make

Both my secret-project apps are very dynamic. (They both even have plugin architectures, so that people besides me can extend the apps in significant ways.) The designs call for heavy reliance on protocols — protocols all over the damn place.

Now I’m faced with a decision. Even though there are solutions to my earlier post regarding Swift and protocols, there are lots and lots of places that need solutions.

And those solutions require more code than I was expecting to write. More code means more code to maintain and more places for bugs to hide. And those solutions don’t have — I hate to say it — the elegance of Objective-C.

I was hoping that I could use protocols in Swift in much the same way I do in Objective-C. That is, just as Objective-C doesn’t care about the difference between id<Foo> and ConcreteFoo *, Swift wouldn’t care about the difference between Foo and ConcreteFoo. But that’s not the case.

* * *

I feel like a putz every time I write “I’m going to use Technology X!” and later write “No I’m not!”

People tease me about it (which is fine).

But I’m not a curmudgeon. I’d never call myself a graybeard (despite the actual gray that appears when I skip a day shaving). I like new things.

Here’s the thing: I have a responsibility to choose the best tool for the job, knowing that the best tool is the best tool for me, at this time, on this specific job.

And I like writing about these decisions, on the grounds that my actual circumstances will be different from yours but that it’s good exercise to follow along with other people’s engineering decisions. (Well, I certainly get a benefit from reading what other developers write, at least.)

* * *

Here’s one way to think about it: how much grief will I put myself through trying to get my protocol-heavy Swift code to work and make it good code and elegant?

* * *

Here’s another way to think about it. If Objective-C didn’t have the ability to treat id<Foo> and ConcreteFoo * in the same way, and suddenly Swift had that ability, would we be talking about how this feature is really, really cool? A game-changer? The best thing since type inference?

I think so.

So is it smart to cast aside Objective-C, which already has this (and other) wonderful features, just because it’s not the new thing?

Except that it’s not just that Objective-C isn’t the new thing. It’s also not the future thing. Switching isn’t a matter of if but of when.

And I want these apps to last a long time.

* * *

In the end, it took me writing it up to decide to stick with Swift.

Expect more blog posts.

Archive

Ads via The Deck