I initially started writing a blog post that discussed various alternatives to Key-Value-Observing (KVO) in Swift, however, I found myself writing more about the task of implementing events in Swift. So, I’ve decided to split the problem in two, in this post I’ll cover events, and I’ll follow-up with the KVO post later on.
I’m certainly not the first to blog about this topic, Mike Ash published a great article on Swift implementation of NSNotification just last week. However, in this post I’d like to approach the problem in a slightly different way, starting with a naïve implementation.
Let’s start at the beginning …
What are Events?
Cocoa has a number of techniques that allow classes to collaborate in a loosed-coupled fashion via some form of notification, including target-action, the delegate pattern, NSNotification and KVO. These are all forms of the classic Observer Pattern, yet all are different implementations, and each have their own failings. KVO is cumbersome, the delegate pattern only permits a single observer … However this post isn’t a rant about Cocoa, so enough of that ;-)
Events provide a generic mechanism for raising notifications that can be handled by multiple observers. C# developers have the luxury of first-class language support for events, however in most other mainstream languages this is something that is either provided at a library level or that you have to implement yourself.
A Naïve Event Implementation
Constructing a simple eventing mechanism is pretty straightforward in Swift, here’s my naïve implementation:
The Event
class has a generic parameter T
which defines the type of data that this event conveys and the EventHandler
typealias declares a function that accepts this type. The rest of this class is pretty straightforward, handlers are added to an array, with each being invoked when the event is raised.
The simple eventing code above supports multiple subscribers or handlers, as illustrated below:
In the above, both handlers are invoked when the event is raised. Notice the use of Void
for the event type, which allows us to invoke the raise
method without any arguments.
You can pass multiple parameters to event handlers via tuples:
And as Mike Ash highlighted (which was news to me), Swift treats a function with multiple parameters just the same as one with a single tuple parameter. As a result, you do not need to construct a tuple in order to raise an event with two string parameters:
(Thanks Mike!)
This eventing mechanism is strongly typed, supports generics, works well with closure expressions, what more could you want?
Actually, there are a couple of issues with the above code, firstly the use of closure expressions for event handlers is a risky business. Any closure that uses self
will cause a retain cycle and result in a memory leak. As Lammert Westerhoff points out, all you need is a single class instance and a closure with a captured reference to self to cause a memory leak.
Whilst this problem is easily remedied using a capture list [unowned self]
, it is all too easy to forget.
The second issue is much more fundamental, the above event implementation allows you to add handlers but doesn’t allow you to remove them. You might be tempted to add a removeHandler
implementation as follows:
However, this will not compile. Whilst closures are reference types, the Swift identity operators (===
and !==
) are defined for AnyObject
- and closures do not conform to this protocol!
As a result, there is no way to determine whether two closures are the same. Go ahead and try this out in a Playground:
It’s a real shame that this doesn’t work, I really liked the idea of adding a bit of C# style syntactic sugar for adding handlers:
Which would allow you to add handlers as follows:
Anyhow, this isn’t going to work :-(
A Complete Event Implementation
OK, so there are quite a few issues with the simple implementation, time to put things right.
Diving into the implementation of Event
, the updated version is shown below:
The concept is pretty similar to the earlier implementation, with the event class containing an array of handlers, however, this time they are instances of EventHandlerWrapper
that implement an Invocable
protocol.
The real magic happens in addHandler
, which makes use of the fact that instance methods are curried functions. For an excellent overview of what this means, I’d refer you to Ole Begemann’s blog.
Before looking at the implementation details, it’s worth taking a quick look at how you would use this event in practice:
This addresses the two issues outlined above, firstly the handler is supplied as a reference to a type (in this case self
) and a method defined on that type, you’ll see shortly that this results in a weak reference to self
, removing the retain cycle issues. Secondly, when a handler is added, it can later be disposed in order to remove the subscription.
So how are the two points above achieved?
The event’s addHandler
method constructs an instance of an EventHandlerWrapper
, which is shown below:
The above class maintains a weak reference to the target, avoiding potential retain cycles. The invoke
method invokes (partially applies) the curried type method to create the required instance method, then invokes the resultant method with the event data (again, if this sounds like gibberish, go read Ole’s blog post!).
Finally, the dispose
method simply removes itself from the array of EventHandlerWrapper
instances ensuring that the handler it wraps is no longer invoked.
Conclusions
And there you have it, a Swift event class that is strongly typed, generic, doesn’t create retain cycles, all good stuff!
In my next blog post I’ll look at how this class can be used to explore various KVO alternatives.
Regards, Colin E.