Key-Value Observing made easier with blocks.
This is an extension to Key-Value Observation mechanism and allows you to use blocks as observation handlers. Block Observing can be used and mixed with classic KVO without any problems.
You should be familiar with the concepts of Key-Value Observing and Key-Value Coding.
Library and example app in this project are for iOS, but you can use in OS X project too by importing the source files directly.
- Drag the project into your project (as a child or sibling).
- Add
Block Observing
to Target Dependencies in Build Phases. - Add
libBlockObserving.a
to Link Binary With Libraries in Build Phases. - Add
-ObjC
and-all_load
as Other Linker Flags in Build Settings. - Make sure you have Header Search Paths in Build Settings set up (e.g.
Libraries/**
). - Import
MTKObserving.h
to your files (usually inPrefix.pch
).
CocoaPods central repositary will no longer be updated. Use this:
pod 'Block-KVO', :git => 'https://github.com/Tricertops/Block-KVO.git'
Any object can observe its own key-path using block handler. Caller and receiver must be the same object and the key-path must be relative to the receiver.
[self observeProperty:@keypath(self.profile.username) withBlock:
^(__weak typeof(self) self, NSString *oldUsername, NSString *newUsername) {
self.usernameLabel.text = newUsername;
}];
Block arguments has no specific type (so they are id
). You are supposed to specifiy the type by yourself as you want. Primitive values are wrapped by NSNumber
or NSValue
instances
The above code example using provided macro:
MTKObservePropertySelf(profile.username, NSString *, {
self.usernameLabel.text = new;
});
IMPORTANT: This is different from the standard KVO.
Once the value of observed property changes, but the values are equal (using -isEqual:
method) the observation blocks are not invoked. For example self.title = self.title;
will not trigger observation.
All observation blocks have first argument the receive/caller with name self
. It overrides method argument self
, but contains the same object. The only difference is __weak
attribute. In the example code above, you can use self
and will not cause retain cycle.
If you want to get out of the current scope, you can just provide selector instead of block.
[self observeProperty:@keypath(self.profile.username) withSelector:@selector(didChangeUsernameFrom:to:)];
There are methods that take an array of key-paths and one block (or selector).
Map property to another property. Once the source key-path changes, destination is updated with the new value. Transform the value as you wish.
[self map:@keypath(self.profile.isLogged) to:@keypath(self.isLoggedLabel.text) transform:
^NSString *(NSNumber *isLogged) {
return (isLogged.boolValue ? @"Logged In" : @"Not Logged In");
}];
Also, there is convenience method for specifying replacement for null value.
[self map:@keypath(self.profile.username) to:@(self.usernameLabel.text) null:@"Unknown"];
Two-way binding can be achieved by using two one-way bindings. Don't worry about recursion, because observation is supressed if the values are equal.
[self map:@keypath(self.task.isDone) to:@keypath(self.doneButton.selected) null:nil];
[self map:@keypath(self.doneButton.selected) to:@keypath(self.task.isDone) null:nil];
Improved observation of notifications using blocks. NSNotificationCenter
provides some support for this, but here you don't need to worry about removing those blocks or retain cycles.
Removing is now automatic.
The MIT License
Copyright © 2012 – 2016 Martin Kiss