Multi-Level KVO
Jakub Hladík
Jakub Hladík
jakub@hippotaps.com
@ku33ing, @hippotaps
iOS app developer
hippotaps co-founder
iOS teacher at FIT CTU Prague
Key-Value Observing
What?
• Key-value observing is a mechanism that
allows objects to be notified of changes to
specified properties of other objects.
• alternatives you should already know:
• delegate/protocol
• NSNotification
• callback using @selector or block
9
Key-Value Coding First
• mechanism that allows accessing object
properties indirectly by a name/key (string)
• NSKeyValueCoding protocol
• NSObject provides default implementation
(getters/setters, …)
10
KVC How?
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row
{
ChildObject *child = [childrenArray objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
return [child name];
}
if ([[column identifier] isEqualToString:@"age"]) {
return [child age];
}
if ([[column identifier] isEqualToString:@"favoriteColor"]) {
return [child favoriteColor];
}
}
11
KVC How?
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row
{
ChildObject *child = [childrenArray objectAtIndex:row];
return [child valueForKey:[column identifier]];
}
12
KVC Terminology
• properties:
• attributes (scalar, string, NSNumber, …)
• to-one relationships (self.otherObject, self.superView, …)
• to-many relationships (collection of objects – NSArray,
NSSet, …)
• key (@”age”) – identifies object property
• path (@”superView.frame”, @”address.street”)
– dot.separated keys specifying a sequence of objects to
traverse
13
Getting/Setting
Attributes
– valueForKey:
– valueForKeyPath:
– dictionaryWithValuesForKeys:
– valueForUndefinedKey:
…
– setValue:forKeyPath:
– setValuesForKeysWithDictionary:
– setNilValueForKey:
– setValue:forKey:
– setValue:forUndefinedKey:
…
14
Getting/Setting
Attributes
@interface MyClass
@property NSString *stringProperty;
@property NSInteger integerProperty;
@property MyClass *linkedInstance;
@end
MyClass *myInstance = [[MyClass alloc] init];
NSString *string = [myInstance valueForKey:@"stringProperty"];
[myInstance setValue:@2 forKey:@"integerProperty"];
MyClass *anotherInstance = [[MyClass alloc] init];
myInstance.linkedInstance = anotherInstance;
[myInstance setValue:@2 forKeyPath:@"linkedInstance.integerProperty"];
15
Finally KVO
– observeValueForKeyPath:ofObject:change:context:
– addObserver:forKeyPath:options:context:
– removeObserver:forKeyPath:
– removeObserver:forKeyPath:context:
…
– willChangeValueForKey:
– didChangeValueForKey:
– willChange:valuesAtIndexes:forKey:
– didChange:valuesAtIndexes:forKey:
…
16
KVO
17
KVO
18
Simple Observing
[self setValue:[[self randomPhoneArray] mutableCopy]
forKey:@"dataArray"];
// self.dataArray = [[self randomPhoneArray] mutableCopy];
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:@"dataArray"]) {
self.dataArray = change[NSKeyValueChangeNewKey];
[self.tableView reloadData];
}
}
19
Demo
Simple Observing
HPT-Multi-Level-KVO
branch stage1
20
NSMutableArray
Observing
- (void)insertDataObject:(id)object
atIndex:(NSUInteger)index
{
[self willChange:NSKeyValueChangeInsertion
valuesAtIndexes:[NSIndexSet indexSetWithIndex:index]
forKey:@"dataArray"];
[self.dataArray insertObject:object
atIndex:index];
[self didChange:NSKeyValueChangeInsertion
valuesAtIndexes:[NSIndexSet indexSetWithIndex:index]
forKey:@"dataArray"];
}
21
NSMutableArray
Observing
- (void)removeDataObjectAtIndex:(NSUInteger)index
{
[self willChange:NSKeyValueChangeRemoval
valuesAtIndexes:[NSIndexSet indexSetWithIndex:index]
forKey:@"dataArray"];
[self.dataArray removeObjectAtIndex:index];
[self didChange:NSKeyValueChangeRemoval
valuesAtIndexes:[NSIndexSet indexSetWithIndex:index]
forKey:@"dataArray"];
}
22
NSMutableArray
Observing
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:@"dataArray"]) {
NSIndexSet *set = change[NSKeyValueChangeIndexesKey];
NSKeyValueChange valueChange = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
NSArray *new = change[NSKeyValueChangeNewKey];
switch (valueChange) {
case NSKeyValueChangeInsertion:
[self addObject:new.lastObject atIndex:set.lastIndex];
break;
case NSKeyValueChangeRemoval:
[self removeObjectAtIndex:set.lastIndex];
break;
case NSKeyValueChangeSetting:
self.dataArray = [new mutableCopy];
[self.tableView reloadData];
break;
default:
break;
}
}
}
23
Demo
NSMutableArray Change
Observing
HPT-Multi-Level-KVO
branch stage2
24
Array Observing
Simpler
- (void)insertDataObject:(id)object
atIndex:(NSUInteger)index
{
NSMutableArray *array = [self
mutableArrayValueForKey:@"dataArray"];
[array insertObject:object atIndex:index];
}
- (void)removeDataObjectAtIndex:(NSUInteger)index
{
NSMutableArray *array = [self
mutableArrayValueForKey:@"dataArray"];
[array removeObjectAtIndex:index];
}
25
Demo
NSMutableArray Change
Observing Done Better
HPT-Multi-Level-KVO
branch stage3
26
Nested Object
Observing
• NSMutableArray in NSMutableArray ):
• Not possible without ugly hacks…
• NSNotifications… (:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"objectInsertedAtIndexPath"
object:self
userInfo:@{ @"object" : object,
@"indexPath" : indexPath
}];
27
Multi-Level
Demo
Nested NSMutableArray Change
Observing using NSNotification
HPT-Multi-Level-KVO
branch stage4
28
Is That It?
29
No Way!
30
- (id)valueForUndefinedKey:(NSString *)key
{
NSUInteger i = [key integerValue];
return self.dataArray[i];
}
Model Change
- (id)valueForUndefinedKey:(NSString *)key
{
NSUInteger i = [key integerValue];
return self.dataArray[i];
}
- (void)insertDataObject:(id)object atIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == self.dataArray.count) {
[self insertDataObject:[NSMutableArray array] atIndex:indexPath.section];
}
[self willChange:NSKeyValueChangeInsertion
valuesAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row]
forKey:[@(indexPath.section) description]];
NSMutableArray *array = self.dataArray[indexPath.section];
[array insertObject:object atIndex:indexPath.row];
[self didChange:NSKeyValueChangeInsertion
valuesAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row]
forKey:[@(indexPath.section) description]];
}
31
Observer Change 1
- (void)addObserverForKey:(NSString *)key
{
[[HPTDataService sharedService] addObserver:self
forKeyPath:key
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)removeObserverForLastKey
{
[[HPTDataService sharedService] removeObserver:self
forKeyPath:[@(self.dataArray.count-1) description]
context:NULL];
}
32
Observer Change 2
- (void)addObject:(id)object atIndex:(NSUInteger)index
{
[self.dataArray insertObject:object atIndex:index];
[self addObserverForKey:[@(self.dataArray.count-1) description]];
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:index]
withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
}
33
Final Working
Multi-Level
KVO Demo
YEAH!
HPT-Multi-Level-KVO
branch stage5
34
The End
• Key-Value Observing Programming Guide
• Key-Value Coding Programming Guide
• https://github.com/kubbing/HPT-Multi-Level-KVO
• http://www.slideshare.net/JakubHladk/for-mobile-513
35

For mobile 5/13'