Observing Many, Notifying One

(This is a repost from my old blog at blogger.com.)

I’ve made an Objective C framework for observing (a combination of) many properties, while triggering only one notification callback, whenever the multi-observation criteria is fulfilled.

Sounds fancy, but the need is quiet simple. The canonical example is a login form with a name and a password, plus a Login button which is only enabled when the name and password are not empty.

An alternative example is a checklist with an action, that should only be triggered when all items on the list are checked.

The framework has an elegant way to express these cases. Here is how the first use case would be expressed, by multi-observing the AND combination of the relevant properties:

PIMultiObserver *multiObserver = [PIMultiObserver new];
[multiObserver observeAnd:@[
    [PIObserver observerOf:self keyPath:@"name.length"],
    [PIObserver observerOf:self keyPath@"password.length"]]
    block:^(BOOL combinedValue) {
        self.loginButton.hidden = !combinedValue;
    }];

And here is the checklist use case, using the observeAllYes method, which only triggers the notification block when all observed properties evaluate true:

[multiObserver observeAllYes:@[
    [PIObserver observerOf:self keyPath:@"booster"],
    [PIObserver observerOf:self keyPath@"retro"],
    [PIObserver observerOf:self keyPath@"fido"],
    [PIObserver observerOf:self keyPath@"guidance"],
    [PIObserver observerOf:self keyPath@"surgeon"]]
    block:^(BOOL combinedValue) {
        NSLog(@"All systems go!");
    }];

Individual observers participating in the multi-observation can also map their observed properties to a BOOL by defining a mapper function.

The code is freely available on Github: https://github.com/pipacs/multiobserver.

Logging in Swift

Printing meaningful logs from a Swift program is tricky, mainly because there is no way to get the current function’s “pretty” names, e.g. there is no real alternative to Objective-C’s magic constant __PRETTY_FUNCTION__.

Luckily other magic constants do exist in Swift, like __FILE__ and __FUNCTION__ — see https://developer.apple.com/swift/blog/?id=15. Concatenating __FILE__ and __FUNCTION__ is an okay approximation of the full function signature in most cases. The logs would be overly verbose though as  __FILE__ contains the full path name and extension as well. We can get rid of the noise with some string manipulations:

func Log(message: String = "",
         _ path: String = __FILE__,
         _ function: String = __FUNCTION__) {
    let module = path.componentsSeparatedByString("/").
        last!.componentsSeparatedByString(".").first!
    NSLog("\(module).\(function): \(message)")
}

A finishing touch could be to disable logging in release builds, by wrapping the function body in #if DEBUG/#endif.