Skip to content

Commit

Permalink
Implements array binding with fine grained change support
Browse files Browse the repository at this point in the history
Previous implementation supported only one kind of KVO change, this led to incorrect behaviour in case of binding array properties if one of them generated fine grained KVO notifications (insertions, removals, replacements).
  • Loading branch information
tarbayev committed Mar 13, 2017
1 parent 7ff314e commit 2fd8969
Show file tree
Hide file tree
Showing 2 changed files with 786 additions and 56 deletions.
102 changes: 58 additions & 44 deletions Sources/MFBBinding.m
Original file line number Diff line number Diff line change
Expand Up @@ -300,66 +300,80 @@ - (void)unbind
}
}


#pragma mark - KVO Methods

- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *, id> *)change
context:(void *)context
- (void)updateObject:(id)object
forKeyPath:(NSString *)keyPath
change:(NSDictionary<NSString *, id> *)change
transformer:(id(^)(NSValueTransformer *, id))transformer
{
if (_flags.updating) {
return;
}

if (context == FirstToSecondKey) {

if (_flags.updating) {
return;
}
id newValue = change[NSKeyValueChangeNewKey];

id newValue = change[NSKeyValueChangeNewKey];
if (newValue == [NSNull null]) {
newValue = nil;
}

if (newValue == [NSNull null]) {
newValue = nil;
}
NSValueTransformer *valueTransformer = self.valueTransformer;

NSValueTransformer *valueTransformer = self.valueTransformer;
if (valueTransformer) {
newValue = transformer(valueTransformer, newValue);
}

if (valueTransformer) {
newValue = [valueTransformer transformedValue:newValue];
_flags.updating = YES;

switch ([change[NSKeyValueChangeKindKey] unsignedIntegerValue]) {
case NSKeyValueChangeSetting:
[object setValue:newValue forKeyPath:keyPath];
break;
case NSKeyValueChangeInsertion: {
NSMutableArray *proxyArray = [object mutableArrayValueForKeyPath:keyPath];
NSIndexSet *indexes = change[NSKeyValueChangeIndexesKey];
[proxyArray insertObjects:newValue atIndexes:indexes];
break;
}
case NSKeyValueChangeRemoval: {
NSMutableArray *proxyArray = [object mutableArrayValueForKeyPath:keyPath];
NSIndexSet *indexes = change[NSKeyValueChangeIndexesKey];
[proxyArray removeObjectsAtIndexes:indexes];
break;
}
case NSKeyValueChangeReplacement: {
NSMutableArray *proxyArray = [object mutableArrayValueForKeyPath:keyPath];
NSIndexSet *indexes = change[NSKeyValueChangeIndexesKey];
[proxyArray replaceObjectsAtIndexes:indexes withObjects:newValue];
break;
}

_flags.updating = YES;

[_secondObject setValue:newValue forKeyPath:_secondKeyPath];

_flags.updating = NO;

return;
}

if (context == SecondToFirstKey) {
_flags.updating = NO;
}

if (_flags.updating) {
return;
}
static __auto_type ForwardTransformer = ^(NSValueTransformer *valueTransformer, id value) {
return [valueTransformer transformedValue:value];
};

id newValue = change[NSKeyValueChangeNewKey];
static __auto_type ReverseTransformer = ^(NSValueTransformer *valueTransformer, id value) {
return [valueTransformer reverseTransformedValue:value];
};

if (newValue == [NSNull null]) {
newValue = nil;
}

NSValueTransformer *valueTransformer = self.valueTransformer;
#pragma mark - KVO Methods

if (valueTransformer) {
newValue = [valueTransformer reverseTransformedValue:newValue];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *, id> *)change
context:(void *)context
{

_flags.updating = YES;
if (context == FirstToSecondKey) {
[self updateObject:_secondObject forKeyPath:_secondKeyPath change:change transformer:ForwardTransformer];
return;
}

[_firstObject setValue:newValue forKeyPath:_firstKeyPath];

_flags.updating = NO;

if (context == SecondToFirstKey) {
[self updateObject:_firstObject forKeyPath:_firstKeyPath change:change transformer:ReverseTransformer];
return;
}

Expand Down
Loading

0 comments on commit 2fd8969

Please sign in to comment.