A Definition of Dynamic Programming in the Cocoa World

When I talk about dynamic programming on iOS and Mac, I mean this:

  1. An app can learn about its structure at runtime.

  2. An app can do things based on what it knows about its structure.

  3. An app can make changes to its structure.

The first is things like knowing what methods an instance implements, or getting a reference to a protocol, class, or method from a string. (As in NSSelectorFromString and so on.)

The second is things like being able to instantiate a class where the name wasn’t known at compile time, or to call a method or reference a property that wasn’t known at compile time. (As with performSelector:, KVC, the responder chain, and xib and storyboard loading.)

The third is things like adding methods at runtime (a la Core Data) or adding classes — for instance, by loading compiled code from disk (plugins).

(Note — because people sometimes misread what I write — this definition is not advocacy for a particular style of programming, nor is it a judgment of Swift, which, I repeat, I love, and is my preferred language. It’s to help us know what we’re talking about when we talk about dynamic programming.)

Oldie Complains About the Old Old Ways

I saw a thing on Twitter that said I’m just an old guy complaining about the new ways. Then the tweet was deleted, to the credit of its author. But let me take up the point.

It’s not the new ways that bother me — it’s the old old ways. That is, how I wrote apps before I started using AppKit.

In those days it was common to use C or C++ — always C for me, thankfully — and you may or may not have used an app framework (MacApp, PowerPlant, MFC, etc.). The app framework might generate code for you, which was a source of fragility and bugs. But, even if not, you had to do things like this:

Menu item with unique ID is chosen
App’s central event handler is called
App looks at its context and dispatches event to the right function

There were lots of switch statements. To add, for instance, a copy (or whatever) implementation to a particular view, you’d have to edit your event dispatcher to know about that particular view and its copy function. Making changes required making changes in various places.

Sure, there were things you could to make this a bit easier. It didn’t have to be total spaghetti. But, even at best, it was bad code, and there was nothing you could do about it.

Contrast that with the first time I used AppKit about 15 years ago. I wrote an action method in a view, wired it to a menu item in Interface Builder, and it worked. No switch statements, no touching a half-dozen locations just to add a command.

I’m sure, that first time, that I didn’t even wire that menu item up to First Responder. It was wired directly. But, even still, AppKit used the runtime’s dynamic features to be able to find and call the right object and the right method. And that’s still true today. (Even in UIKit. Even if it’s a button. In other words, if you don’t think you’re taking advantage of Objective-C’s dynamic features, you totally are.)

It seemed like magic, then. I later came to understand how it worked, and then it just seemed like brilliance. (Brilliance is better than magic, because you get to learn it.)

* * *

So when people like me write about these problems, it’s not because we fear the future and new ways of doing things. We love learning new ways of doing things — particularly when they’re better solutions to the problems we have.

We’re not afraid of the future — we’re afraid of the past.

We remember how these problems were solved by the static languages of the day, and we don’t want to go back. In the words of Guy English:

If you see a switch statement or dispatch table they blew it.

So, again, I’m documenting the problems currently solved by Objective-C’s dynamism, and suggesting that Swift, as it evolves, needs to take these problems into account. The foundation should be built with some idea of what the upper floors will look like.

The answer doesn’t have to be that Swift is dynamic in the way Objective-C is, or even dynamic at all. But the eventual Swift app frameworks need to solve these problems as well as — hopefully better than — UIKit and AppKit do right now. And those solutions start with the language.

PS I think I’ll write about plugins next.

WA Primary vs. Caucus

While 230,000+ people participated in Washington state’s Democratic caucus, 661,403 (and counting) participated in the non-binding Democratic primary.

The results were even different — Sanders won the caucus by a large margin, while Clinton is winning the primary. Those results could be explained by several different factors, though, and that’s not my point.

My point is that the caucus system decreases participation, and is therefore less democratic than primaries.

In my state we have mail-in voting, which is more fair and accessible than caucuses or even going to the polls on election day.

Anyone who works or cares for children or adults — or who has other responsibilities that keep them from participating in a caucus — can still vote via mail.

And anyone for whom it’s physically or psychically difficult or impossible to attend a caucus has a much better chance of voting via mail.

As a Democrat, it’s disheartening to see my own party effectively locking out many thousands of people from participating. This goes against my values — and it goes against the values of the Democratic party. (Or so I thought.)

I wrote a note to the Washington State Democrats and argued the above. You can too.

* * *

The other thing people argue about is whether or not primaries should be open — that is, do you have to be a registered Democrat or Republican to be able to vote?

I like Washington’s system: you have to check a box on your primary ballot that you consider yourself a Democrat or Republican. That’s it.

I think it’s absolutely fair that a party would want members of its party to choose their nominee. You don’t want other people to choose your nominee for you.

But at the same time it shouldn’t be onerous to declare that you’re a Democrat or Republican. If you’re not willing to do that much — well, then, that’s a valid choice, but you don’t get to vote.

Paul, Daniel, Dave

Paul Kim, On Dynamism:

My problems aren’t performance. They aren’t type-safety (maybe it’s just me, but I rarely have issues with it and when I do, the time it takes to fix it is far less than the time specifying everything to a tee everywhere else). They aren’t being able to write clever VDLs. For me, it’s writing apps that solve my users’ problems and getting them out in a timely fashion. As it stands now, Swift (at least pure-Swift, or even current Swift as a non-ABI-stable moving target) does not do that for me.

Now maybe these same problems can be solved in a static way but what I’m not seeing from the static-camp are (decent) solutions. What I’m seeing are either hand-waving or the same crufty code-generation, write tons of repetitive boilerplate type of solutions that I had hoped we had left behind in the 90s.

What I do see makes me worry that it’s not the experienced app-writers that are being heard.

* * *

Daniel Jalkut, Not Perfected Here:

Swift is a fascinating, beautiful language. How will it evolve to prove superior, in the long-run to Objective-C? By providing a suite of impedance-matched frameworks that fulfill all the needs of current iOS and Mac developers.

* * *

Dave DeLong posted 12 tweets on the subject, starting here, and including:

5/ but one of the things I love about @SwiftLang is that, since the dynamism isn’t there, I have to think about problems in new ways

…and…

8/ I know that the people working on @SwiftLang are some of the most brilliant people in the world, and they all also love Objective-C

…and…

10/ so maybe we’ll end up dynamism. Maybe we won’t. What we WILL end up with is one of the most kick-ass languages in the world

Though I could ask about the frameworks (and I did), I can’t argue with Dave’s conclusion. I’ve said before, and will say again, that I’m writing all my new code in Swift and I’m enjoying it a ton, and I get bugged when I have to write Objective-C.

In other words — if you think that the various people writing about Swift and dynamism are anti-Swift, you’ve got it wrong.

Dynamic Swift Comments on Hacker News

I don’t generally point to (or even read) comments on Hacker News — because in the past I’ve found that they’re like other comments on the web.

However, I think the Hacker News comments on Michael Tsai’s Dynamic Swift post are worth reading.

Ash and Marcel

Ash Furrow, Adulterated Objective-C:

…maybe it’s not important to re-invent Objective-C’s dynamic runtime in Swift. The language is far more static than Objective-C. And besides, a dynamic runtime is only a set of tools used to solve problems. In a different context, like Swift, different tools might be better suited.

Problems currently solved with dynamic programming should have solutions just as good (if not better) in Swift and future Swift-only app frameworks. And “better” can’t mean, “Yes, it’s a big pain now and inelegant, but the compiler’s happy.”

I’m skeptical that there is a solution short of adopting the same kinds of dynamic features Objective-C has, but I’m only skeptical. I don’t say it’s impossible. I want to be surprised and amazed.

* * *

Marcel Weiher, What's Missing in the Discussion about Dynamic Swift:

The truly amazing thing about KVC, CoreData, bindings, HOM, NSUndoManager and so on is that none of them were known when Objective-C was designed, and none of them needed specific language/compiler support to implement.

Instead, the language was and is sufficiently malleable that its users can think up these things and then go to implement them. So instead of being an afterthought, a legacy concern or a feature grudgingly and minimally implemented, the metasystem should be the primary focus of new language development. (And unsurprisingly, that's the case in Objective-Smalltalk).

Updating Local Objects with Server Objects

I’ve spent much of my career writing apps that store objects locally that come from the web — and that may change on the web, and then need to be updated locally.

This kind of code can be tedious to write. Given a local object and an objectified version of some JSON or XML or whatever, I’d write something like this (translated to Swift for fun):

if localObject.foo != serverObject.foo {
  localObject.foo = serverObject.foo
  changeDictionary[fooKey] = serverObject.foo
}
if localObject.bar != serverObject.bar {
  localObject.foo = serverObject.bar
  changeDictionary[barKey] = serverObject.bar
}
// multiply above a dozen times and for several different classes
return changeDictionary

Maybe you spotted the bug in the above, and maybe you didn’t — and that’s part of my point. I’d write this code using copy-and-paste, and then go over it visually, line-by-line, to make sure it’s right, and sometimes I’d miss something anyway.

If foo and bar are both strings, for instance, the above would compile and all would be well. Except that I’ve written a bug where localObject.foo = serverObject.bar.

Eventually I found a better way — some code that I can write once, and that can’t have the bug I wrote above. See this gist.

* * *

The solution is pretty simple. You have two objects — which may or may not be the same class but where the properties have the same names — and then loop through an array of mergeable property names. (The two objects don’t have to have the exact same list of property names. They just have to have the mergeable property names and types in common, since those are the ones we care about.)

Then there’s a method that compares the local and server objects. For each property name in the list, it gets the localValue and serverValue using valueForKey:, and then compares them (via isEqual:).

When they’re not equal, then it uses setValue:forKey: on localObject to give it the serverValue. It also adds the property name and new value to a change dictionary. (That dictionary could be useful for efficient database updating, sending notifications, etc.)

There are places this could go wrong, and where the compiler wouldn’t complain. In the gist there’s this line:

static let mergeablePropertyNames = ["dog", "cat", "zebra", "numberOfAnimals", "fedTheTigers", "attendingDoctors"]

Obviously that line has to be maintained, and it has to be true that both local and server objects have those properties and those properties have to be of the same type. Yes.

However, you’re going to notice any error at runtime pretty quickly, since you’ll get an exception — while you may not notice the localObject.foo = serverObject.bar bug from the manual version any time soon.

Best thing: you can reuse that updateLocalObject​WithServerObject method for all the various local/server object updating you need to do in all your apps.

* * *

Even though we’re writing in Swift here, the solution uses the Objective-C runtime and KVC.

KVC is both awesome and to be used sparingly. Most of the time you want to write standard code that the compiler can better check — you want to write foo.bar = true. Absolutely.

But there are those occasional critical cases where KVC creates more reliable code that’s easier to maintain, debug, and reuse. This is one of those.

Layers Conference

In case you haven’t seen it mentioned already — Layers is a design conference in San Francisco taking place the same week as WWDC. It’s “like a party, but for learning,” and promises snacks, and people I respect tell me it’s wonderful.

Be sure to scroll down and look at the list of presenters. It’s amazing.

Gus and Craig

Gus Mueller:

Being able to write dynamic, responder chain-like code in Swift or Objective-C (or whatever) is extremely important to me. And if I had to give one reason why it would be this: Dynamic code is less code, and less code means fewer bugs and maintenance.

Craig Hockenberry:

The community around Swift’s evolution is amazing. The language is improving quickly and dramatically thanks to talented developers inside and outside of Apple. It’s a remarkable open source project.

My concern is that there isn’t a corresponding discussion about the things we’re going to build with this new language. As you’ve just seen, frameworks are important, yet there is no uikit-evolution mailing list. There is an imbalance between the tool and the craft.

What I’m Doing With These Articles

In case it’s not clear: with recent and future articles I’m documenting problems that Mac and iOS developers solve using the dynamic features of the Objective-C runtime.

My point isn’t to argue that Swift itself should have similar features — I think it should, but that’s not the point.

The point is that these problems will need solving in a possible future world without the Objective-C runtime. These kinds of problems should be considered as that world is being designed. The answers don’t have to be the same answers as Objective-C — but they need to be good answers, better than (for instance) writing a script to generate some code.

(Swift is just one part of that future world. The part I care about more, far more — as much as I love Swift — is any app frameworks built on Swift. But the design of Swift has an impact on the design of those frameworks.)

There’s a second point: many people writing in Swift right now seem not to realize the extent of their reliance on Objective-C’s dynamism. Even if you don’t write a line of Objective-C, you’re running a ton of code that is Objective-C, and that dynamic runtime, and the app frameworks built on top, is what makes your apps actually work. This is kind of a “duh” thing, I realize, but it’s worth the reminder.

Next up (probably Thursday or Friday) will be a thing on easily updating local model objects with data downloaded from a server. And then more to come.

Archive

Ads via The Deck