TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2014 |
Maintained by Martin Kiss.
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.
__weak
)Block Observing
to Target Dependencies in Build Phases.libBlockObserving.a
to Link Binary With Libraries in Build Phases.-ObjC
and -all_load
as Other Linker Flags in Build Settings.Libraries/**
).MTKObserving.h
to your files (usually in Prefix.pch
).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 observe:@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 = newUsername;
});
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 observe:@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. With this you can map user.name
to textField.text
, so it will always display the name and then map textField.text
to user.name
for the name to be updated once the user make changes.
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.
MIT License, Copyright (c) 2012—2013 Martin Kiss
THE SOFTWARE IS PROVIDED "AS IS", and so on …