Observable Events Revisited

Back in May of last year I wrote about my ObservableEvent library, which is a simple, efficient observer pattern written in Swift. You can see the original post here. Since that time there have been a couple of things that I wanted to change and improve. Its time for version 2.0.

Disposables

The first thing I wanted to improve was how an observer is removed. In the original design, when creating an observer it would return a closure that could be called later to remove the observer. It worked like this:

This works great, but can become awkward to manage if a piece of code is observing multiple observable events. For example, in a solitaire card game I am currently working on I created a quest system. The player completes quests to get rewards. I implemented several different quest classes to represent the different types of quests that are possible. Each quest class has observers to different game events so that it can update its state as the games plays. As an example, a particular quest class may observe the card played event and the new round started event as follows:

It would be nice if there was a way to remove all the observes with one call and not have to declare variables to hold each remove observer closure.

To improve this I decided to borrow an idea from reactive frameworks such as RxSwift. There they use the notion of a Disposable and a DisposeBag to manage the lifetime of the observer.

Now when adding an observer it returns an instance of a Disposable instead of returning a closure. To remove the observer, call the dispose() method on the Disposable object.

This alone does not really help with the multiple observer scenario. But using a DisposeBag does make a difference. A DisposeBag is a collection of Disposable objects. The DisposeBag will dispose all of its contents when the bag itself is disposed, or when the DisposeBag is deallocated. Returning to our quest class example:

Now when each observer is setup the resulting Disposable object is added to a single DisposeBag by calling the disposeWith method on the Disposable. The bag will be deallocated when the quest class instance is deallocated. And when the bag is deallocated it will automatically call dispose on each item it contains, thus removing all the observers. We no longer need to maintain a separate variable for each observer and we no longer need to explicitly remove the observers in deinit. They are all automatically removed by the bag.

Properties

The second improvement I wanted to make was to address a common pattern that happens frequently when using observable events. Observable events are often used to provide change notifications for when a specific variable changes. It would typically look something like:

The new Property class provides a convenient way to implement this pattern by combining the variable instance and the observable event into a single class.

To create a property, create an instance of the Property class, passing the property’s initial value to the initializer. The above score example can be implementing using a Property as follows:

To access the value of the Property use its value property:

You set the property’s value by assigning to its value property. Whenever a property’s value is set, it will automatically notify all observers.

To add an observer to a property call its observe method. It works identical to the observe method on ObservableEvent, passing in a closure and returning a Disposable:

An important note about properties… the observer closure will be called with the current value of the property as soon as the observe method is called. This means that property observers are called with the current value of the property and any future values.

Summary

Version 2.0 of the ObservableEvent framework is now available on github. It includes the additions of Disposable, DisposeBag, and Property. If you need a simple, efficient Swift implementation of the observer pattern, but don’t need a full featured reactive framework such as RxSwift, then I hope you find ObservableEvents useful. As always feel free to leave questions or comments below.

This entry was posted in Game Programming and tagged , .

Leave a Reply

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