Observable Events in Swift

When writing code for video games there are certain design patterns that always seem to come up. The observer pattern is definitely one of those patterns. Take for example the iOS game I am currently working on, where each level has one or more goals. To successfully complete a level the player must complete all of the level’s goals. The game is a word-based game so the goals for a particular level might be:

  1. Score 10,000 points
  2. Spell 3 words with 5 or more characters

Each goal instance needs to observe different events in the system so that it can track the player’s progress towards completing the goal. In the example above the point total goal needs to observe the game score, and the word length goal needs to know whenever the player spells a word so it can check the length of the word.

Using NotificationCenter for observers

My first thought was to implement these observers using NotificationCenter. Using NotificationCenter to post a score changed notification and pass the new updated score would look like:

To register as an observer of our ScoreChanged notification would be:

And finally to remove the observer of the notification:

This solution works well, but due to NotificationCenter’s objective-c heritage it can be a little clunky to use in Swift. Even with the recent improvements in Swift 3.0 it is still awkward to use. In particular the userInfo dictionary is rather awkward. There is the ugly unboxing code that needs to be done by the observer to get to the data.

I think we can do better by implementing our own observer pattern in Swift. My goal is to create a simple, easy to use, observer pattern that takes advantage of Swift’s type system.

Observers in Swift

Lets start by first looking at the design of NotificationCenter. It behaves like a mediator between the event publisher and observer. Both the publisher and the observer interact with NotificationCenter instead of each other. I would like to remove the middle man and let publishers create their own set of observable events. Publisher entities can expose their observable events as properties. This makes it clearer what events a publisher is publishing. For example my game’s ScoreManager class would create a score changed event property and would notify observers every time the score changes. Other game entities could then access this event property to setup their observers.

When notifying observers of our event I want to be able to send data with the notification. But instead of using a dictionary of AnyObjects, I want to be able to send the data directly, with no unboxing code needed. Using generics should work well for this, allowing us to create event instances that work with any kind of data. No more ugly unboxing code and everything is statically type checked at compile time. It also means we are no longer restricted to types that conform to AnyType. We can now pass any Swift type.

We can define our observable event data type as a generic type as follows:

Next we add a notifyObservers method that supports passing notification data to the observers:

This simple definition is enough to allow us to create different types of events, passing data when notifying the event observers.  For example:

Next we need to add support for allowing observers to listen to the event. We want to be able to pass in a closure that gets called whenever notifyObservers is called, so we define an addObserver method that takes a closure as parameter:

Here is an example of using addObserver on scoreChangeEvent:

or using a method instead of closure:

The only thing left is a way to remove observers from the event. To be able to do that we need a way of identifying which observer we are removing.  One approach would be for the addObserver method to return a token or identifier similar to what NotificationCenter does. We would also need to have a removeObserver method that takes the token as a parameter. I chose a different approach. Instead of returning a token I chose to have addObserver return a closure. To remove the observer just call the returned closure. Here is the updated class definition with the remove observer supported added:

So now an observer adds and removes itself as follows:

We now have a complete definition of the observer pattern.  We just need to add in the details for managing the observers.  Here is the completed class:

You may be wondering about the ids. In Swift closures are not comparable. So if we stored only the closures in the observers array, there would be no way to remove a specific observer from the array. To solve this I store a tuple of a unique id and the closure in the observers array. Then to remove the observer just filter out the item with the matching id. The unique ids are generated by a simple increasing integer value. The addition of the ids makes the implementation not as clean as I would like, but it works well.

Leveraging Swift types

The benefit of defining ObservableEvent as a generic type is that we can define events for any Swift type, including tuples, structs, class instances, or enums. This allows us to do some interesting stuff.  For example, using tuples we can pass multiple values with the event:

It is even possible to create untyped ObservableEvents that do not pass data when notifying observers. This can easily be done by using Void as the generic type when creating the ObservableEvent.

ObservableEvents in action

Here is an example of how I use ObservableEvent in my game. I have a ScoreManager class that keeps track of the current score and also manages high scores, etc. The relevant part of it looks something like this:

So now whenever the ScoreManager‘s score property is changed it will notify all the observers of the scoreChangedEvent sending them the new updated score.

The goal class that observers the scoreChangedEvent is setup as follows:

Memory management

A quick word about memory management and closures. Consider what would have happened if I had called addObserver as follows in the above example:

While this is more straight forward, it also creates a strong reference to GoalScore that is owned by the scoreChangedEvent class. Because of this our GoalScore class would never get deallocated and deinit would not get called.  To solve this we could call removeScoreObserver() somewhere else other than deinit.  When removeScoreObserver() is called it would remove the strong reference and the GoalScore class would get deallocated normally. The other way to solve it is as I have shown above, to use a closure with weak self to call scoreChanged. This prevents the strong reference and our class deallocates normally and deinit will get called to remove our observer.

Conclusion

Using NotificationCenter to implement observers works well but can be awkward to use in Swift. But by taking advantage of Swifts generic types we are easily able to create a much easier to use implementation of observers that works with any type of Swift data type.

The completed code and license information for ObservableEvent can be found in gitHub at https://github.com/tkier/ObservableEvent

UPDATE: The ObservableEvent framework is now available as a pod.  Add the following line to your pod file and run “pod install”

Hopefully you find the class useful for your projects. If you have any questions or comments please use the comment section below.

UPDATE 2: Version 2.0 of the ObservableEvent framework is now available. There are several changes and improvements. Please see this blog post for more information.

This entry was posted in Game Programming and tagged , .

Leave a Reply

Your email address will not be published. Required fields are marked *