I’m just finishing up my first app store bound project that was written in Swift. It’s nothing hugely exciting, just a giant calculator sort of application. Why I chose Swift is that Swift’s static typing really made me think about the data layer, and how data flows through the application. What I missed terribly was KVO/KVC, and I’m not alone. Brent Simmons has also mentioned this, but as someone who’s used a lot of KVO and KVC over the years, I find that it’s helped me ship code a lot more quickly, and has been one of the most valuable features of the Mac frameworks. A lot of developers who are new to the platform aren’t aware of these constructs.
The idea is something like this: We’re done a really good job of optimizing the model layer of Model/View/Controller applications. And Swift has done an amazing job. Static typing provides huge advantages in reliability and coherency. But the Obj-C philosophy is really about re-usable components. In that philosophy, components written by one vendor need a way to seamlessly talk to another, and this is really where Swift and static typing fall flat. A view from one vendor or component needs a way to render data from a model from another component. We find this even in the system where a component like CoreData needs to be passed into a controller where it needs to be searched, or…
Hold on. I can hear the Swift developers already. “We have protocols and extensions for that! I can make a component from one source talk to a component from another source. All I need to do is define a protocol in an extension and I can have my static typing and everything!”
Ok. Let’s go down the rabbit hole.
The Swift Protocol Route
Let’s take a classic case that is actually a scenario that Apple shipped on the Mac in Mac OS 10.4. I want to have a controller, that given an input of an array of types, will filter the array based on a search term and output the result. The key here is my search controller doesn’t know the input type beforehand (maybe it came from a different vendor) and my input types don’t know about the search controller. I want to have a re-usable search controller, that I can use across all my projects, with minimal integration effort to save implementation time.
Using protocols, you might define a new protocol called “Searchable”. You extend or modify your existing model objects to follow the protocol. Under the “Searchable” protocol, objects would have to implement a function that receives a search term string input and return true or false based on if the object thinks it matches the search term. Easy.
But there are a few problems with this approach. The controller has become blind to how the search is actually performed, which isn’t what I wanted at all. The idea was that the controller would perform the search logic for me so I didn’t have to continuously rewrite it, and now I’m rewriting it for every searchable object in my project. If I need search to be customizable, where the user was selecting which fields they wanted to search, or selecting options like case sensitive or starts with/contains search, those options now need to be sent down into each Searchable object, and then logic written in each object to deal with that. Reusable components was supposed to make my code easier to write, and this sounds worse, not better.
Maybe I could try and flip this around. Instead of having extensions for my objects, I can have a search controller object that I subclass, and fill in with details about my objects. But I’d still have the same problem. I’m writing a lot of search logic all over again, when the point is I want to reuse my search logic between applications.
(If you’ve used NSPredicate, you probably know where this is going.)
Function Pointers
Alright, so clearly we were trying to implement this all in a naive way. We can do multiple searchable fields. When the search controller calls in to our Searchable object, we’ll provide it back an map of function pointers to searchable values, associated with a name for each field. This way all the logic stays in the controller. It just has to call the function pointers to get values, decide if the object meets the search criteria, and then either save or discard it. Easy. And we are getting closer to a workable solution, but now we have a few new problems.
Live search throws a wrench into this whole scheme. Not only do we need a way to know if an object meets the search criteria, but now we also need a way of knowing if an object’s properties have changed that could make it’s inclusion in our search change. This is especially important if I have multiple views. Maybe I have a form and a graph open for my model objects in different windows. If I change an entry in the form, I’d want the graph to possibly live update. And the form view and the graph view might have no knowledge of each other. So we need a way to callback to an interested observer of the object when a value changes. We could use a timer to check every second or so for changed values, but in some scenarios that could be a very expensive and needless operation. So while that would work, performance and battery life would significantly suffer. And it’s more code we don’t want to write.
There’s also the issue of nested values. Maybe what I’m searching are objects that represent employees, but now I also want to search on the name of the department employees belong to. In my object graph, it’s very likely that departments will be another model object type that will have a relationship with employee objects. So now I’m not just looking at returning function pointers to not just my employee objects, but department objects they belong to. And now I need to communicate changes not only in my object’s own values, but changes in it’s relationships to other objects.
Also there is the small issue of this approach not working with properties. As far as I know, you can’t create an function pointer to a property. So now I need to wrap all my properties with functions.
This is getting complicated again. Once again I’m writing a lot of code, and not saving any time at all. There has got to be a better way.
Key Value Coding
Well fortunately after years of going through this same mess in other languages, Apple came up with Key Value Coding as a solution.
Key Value Coding is extremely simple: It’s a protocol that allows any Obj-C object to be accessed like a dictionary. It’s properties (or getter and setter functions) can be referred to by using their names as keys. All NSObject subclasses have the following functions:
func valueForKey(_key
: String) -> AnyObject? func setValue(_value
: AnyObject?, forKeykey
: String)
(Reference to the entire protocol, which contains some other interesting functions, is here.)
Now my search controller is easy. I can simply tell the search controller all the possible searchable properties like so:
class Employee: NSObject { dynamic var lastName: String? dynamic var firstName: String? dynamic var title: String? dynamic var department: Department? } searchController.objects = SomeArrayOfEmployees searchController.searchKeys = ["firstName", "lastName", "title"]
Now I can have a generalized search controller, that I can share between projects or provide as a framework to other developers, that doesn’t have to know anything about the Employee object ahead of time. I can describe the shape of an object using it’s string key names. Underneath the hood, my search controller can call valueForKey passing the keys as arguments, and the object will dynamically return the values of it’s properties.
Another great example of the advantages of keys is NSPredicate. NSPredicate lets you write a SQL-like query against your objects, which is harder to do without being able to refer to your object’s fields by name.
There is a catch. If you’re a strong static typing proponent, you’ll notice that none of this is statically typed. I’m able to lie about what keys an object has, as there is no way to enforce the name of a key I’m giving as a string actually exists on the object before hand. I don’t even know what the return time will be. valueForKey returns AnyObject.
Quite simply, I don’t think static typing helps this use case. I think it hurts it. I don’t see a way to make this concept workable without dropping static typing, and I think that’s ok. Dynamic typing came about because of scenarios like this. It’s ok to use dynamic typing where it works better. And all isn’t lost. When our search controller ingests this data, if it’s a Swift object, it will have to transition these objects back into a type checked environment. So even though static typing can’t cover this whole use case, it improves the reliability of using Key Value Coding by validating that the values for keys are actually the type we assumed they would be.
Key Value Paths
There are a few problems KVC hasn’t solved yet. One is the object graph problem that was talked about above. What if we want to search the name of an employee’s department? Fortunately KVC solves this for us! Keys don’t just have to be one level deep, they can be entire paths!
The KVC protocol defines the following function:
func valueForKeyPath(_ keyPath
: String) -> AnyObject?
|
A key path of the form relationship.property (with one or more relationships); for example “department.name” or “department.manager.lastName”. |
Hey look, that’s uhhhh, exactly our demo scenario.
So now I can traverse several levels deep in my object. I can tell my search controller, after some modification, to use a key path of “department.name” on my employee object.
searchController.objects = SomeArrayOfEmployees searchController.searchKeyPaths = ["firstName", "lastName", "title", "department.name"]
Now internally, instead of calling valueForKey, my search controller just needs to call valueForKeyPath. I can use single level deep paths with valueForKeyPath with no issue, so my existing keys will work.
Notice that valueForKey and valueForKeyPath are functions that are called on your object. I’m not going to do a deep dive right now, but you could use these to implement fully dynamic values for your keys and key paths. Apple’s implementation of this function inspects your object and looks for a property or function that’s name matches the key, but there is no reason you can’t override the same function and do your own lookup on the fly. It’s useful for if your object is abstracting JSON or perhaps a SQL row.
It’s also important that this works on any NSObject. I can insert placeholder NSDictionary objects for temporary data right alongside my actual employee objects, and the same search logic will work across them. As long as the object has lastName, firstName, title, and department values, the object type no longer matters.
Key Value Observing
Well all that’s great, but we still have one more issue. We need to know when values change. Enter Key Value Observing. Key Value Observing is simple: Any time a property is called, or a setter function is called, a notification will automatically be dispatched to all interested objects. An object can signal interest in changes to a key’s value with the following function:
func addObserver(_anObserver
: NSObject, forKeyPathkeyPath
: String, optionsoptions
: NSKeyValueObservingOptions, contextcontext
: UnsafeMutablePointer<Void>)
(It’s worth checking out the other functions. They can give you finer control over sending change notifications. Also lookup the documentation for specifics on the change callback.)
Notice that the function takes a key path. An employee’s department name will not only change if their department’s name changes, but also if their department relationship changes. This covers both cases by observing any change to any object within the “department.name” path.
It’s also worth checking out the options. We can have the change callback provide both the new and old value, or even the inserted rows and removed rows of an array. Not only is this a great tool for observing changes in objects that our class doesn’t have deep knowledge of, but it’s just great in general. This sort of observing is really handy for controlling add/remove animations in collection views or table views.
In our search controller, we just need to observe all the keys we are searching in all the objects we are given, and then we can recalculate the search on an object by object basis. There are no timers running in the background, this change can fire directly from an object’s value being set.
So what’s the problem in Swift?
I’ve mentioned one problem already: Only classes that subclass NSObject can provide KVO/KVC support. Before Swift, that wasn’t a major problem. Now with Swift, we have non-NSObject subclasses, and non class types. Structs can’t support KVO/KVC in any fashion.
The properties/functions being observed also have to be dynamic. Again, not a problem in Obj-C where all functions and properties are dynamic. But not only are Swift functions not dynamic by default, some Swift types are not supported by dynamic functions. Want to observe a Swift enum type property? Can’t do that.
Even more worrisome, the open source Swift distribution could possibly not include any dynamic support, and KVO/KVC are defined as part of the Cocoa frameworks, which aren’t likely to be included with open source Swift. Any code that wants to target cross platform Swift might be forced to avoid KVO/KVC support. Ironically, just as we could be entering a golden age of framework availability with Swift, we might be discarding the technology which makes all those frameworks play cleanly with each other.
So what would I like to see from Swift?
- Include KVO/KVC functionality as part of core Swift: The current KVO/KVC are defined as part of Foundation. They don’t need to be moved, but Swift needs an equivalent that can bridge, and is cross platform.
- Have more dynamic functionality on by default: Another issue is that dynamic functionality is currently opt in. This is for a good reason: things like method swizzling won’t work with Swift’s static functions. But Apple could split the difference: Allow statically linked functions (and properties) to at least be looked up dynamically. This would allow functionality like KVO and KVC to work without giving up direct calling of functions or opening back up to method swizzling.
- Have the KVC/KVO replacement work with structs and Swift types: Simple. Enums in Swift are great. Now I just want to access them with KVC and observe them.