New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better modeling for KVO collection changes #1032
Conversation
@robrix This addresses some of the things we were discussing the other day, I think. The collection mutation protocols are functors, so it should be easy to "remap" changes to a different type of object (like updating your VM to match changes in your model). |
This was the first thing that came to mind when reading through. 💡 |
/// | ||
/// NSMutableArray *VMs = [self mutableArrayValueForKey:@keypath(self.viewModels)]; | ||
/// [viewModelsMutation mutateCollection:VMs]; | ||
/// }]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern reminds me of RAC(…) = RACObserve(…)
. What are your thoughts on similar macros?
(macro names for illustration)
RACMutate(self, viewModels) = [RACObserveMutations(self, models)
reduceEach:^(id _, id<RACOrderedCollectionMutation> modelsMutation) {
return [modelsMutation map:^(Model *model) {
return [[ViewModel alloc] initWithModel:model];
}];
}];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collection updates are comparatively uncommon, and we already get flak for the common macros. I'd rather not bloat the framework further with more.
Damn, I hate this when you work on something a whole month, and then there is a pull request in ReactiveCocoa, doing exactly the same thing ;(( I feel bad ;( But the cool part is the fact that this idea is originated in someone else head ) |
Totally stoked for this! Question about the
|
@indragiek You should take into consideration that table view reflects sectioned collection. Not a simple array. |
This is for NSTableView, which doesn't have section support like UITableView does. Indragie Karunaratne On Fri, Dec 27, 2013 at 2:28 AM, Denis Mikhaylov notifications@github.com
|
@indragiek my bad ) |
@indragiek Not sure yet. I'm just gonna start implementing it and see what makes sense. Part of that will be determining whether animation support is important at all. |
Conflicts: ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj
This is a nice approach! It appears not to handle moves at the moment, but that seems like a relatively straightforward extension. I would suggest that a notion of applying some ordering is a good mutation to capture, since it’s not uncommon to mutate elements of a set which gets sorted into an array for display. This gives mutations from unordered to ordered (sorting, changing an element such that it is sorted at a different index), and from ordered to ordered (resorting, moving, etc). |
@robrix I agree with you about the need for a move mutation, especially when considering animations you don't want to implement it as a remove then add. I'm not sure sorting (and filtering) fit this mutation model as well. They may be better handled as a separate operators or collection transforms. Specifically you want The hard part of this problem is something you already alluded to
Solving this well requires a way to efficiently aggregate item level changes into the collection's mutation stream. Based on the current code this might look like @interface RACItemMutation : NSObject <RACCollectionMutation>
@property id item;
@property NSString* keyPath;
@property NSDictionary* change;
// or you could extract old and new values and expose them directly instead of the KVO dictionary
@end
@interface RACOrderedItemMutation : RACItemMutation <RACOrderedCollectionMutation>
@end The naive approach to generating these is subscribing property keys that affect sort and/or filter for all items. There could be a default implementation for said approach that would work well enough on small collections. More complicated collections would likely require a custom RACCollection implementation with inherent knowledge of the contained items and their change patterns. |
This is awesome. I can certainly see that I would use this in a good amount of my code and can't wait to use it. Could this be even more abstract when applied to Signals of collections? I'm thinking of an operator on
I can think of one non-UI area would be (necessary) side-effecting when a remote resource changes, where the remote resource is represented by a Signal of collections. When there additions/deletions to the collection, side-effects occur. With the current implementation, this could be done by binding to-and-from a property. However, if the side-effect belongs further up the Signal chain, the Signal operator would be preferable. |
I have no intention of supporting anything like this. You basically need a full diffing algorithm to calculate deltas reliably. |
I see, this is something that |
@lawrencelomax Unfortunately, that's not automatic either. Those change kinds require the use of |
This allows us to define ordered mutations that use 0 or > 1 sets of indexes.
With If people think that makes sense, I can implement a similar pattern for @robrix @neilpa Sorting is a sufficiently thorny problem that I'm gonna punt on it for now. I'd suppose that the mutations already captured should cover 80% of the use cases for automatic bindings. |
Also decided to punt on/avoid |
@jspahrsummers There's plenty o' good stuff here so punting on sorting makes sense. Given time this weekend I'll experiment with this as a replacement for some custom query/collection code I have in my current project. Requires back-porting to RAC 2.1 first though :( |
@jspahrsummers agreed on punting that—it’s worth thinking about, but not worth delaying this (already useful!) stuff for. |
🤘 |
And only the mutable collections conform to the <RACCollection> protocols.
💻 |
/// should be animated. | ||
/// | ||
/// Returns a disposable which can be used to cancel the binding. | ||
- (RACDisposable *)rac_animateOrderedMutations:(RACSignal *)orderedMutations withInsertionAnimation:(NSTableViewAnimationOptions)insertionOptions removalAnimation:(NSTableViewAnimationOptions)removalOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be nice if the animations parameters (here and elsewhere) were signals since it seems likely you won't always want animations. But I'm happy to merge this without it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I designed these APIs to be super simple. It's easy enough to implement this by hand that it'd be totally appropriate to reinvent for special behaviors w.r.t. animations, or updating multiple parts of a UI{Table,Collection}View
at once.
🍬 Also needs some merge action. |
Conflicts: ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.pbxproj ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACPropertySubscribing.m ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingSpec.m
🆒 |
Better modeling for KVO collection changes
This abstracts over Cocoa's built-in collections with
<RACCollection>
and<RACOrderedCollection>
protocols, and then models changes to them with<RACCollectionMutation>
and<RACOrderedCollectionMutation>
. There are ordered and unordered versions of KVO change kinds.Although this might seem like over-abstraction, it means that new collections and new mutation types can be added in the future (or by consumers) without affecting existing code.
I chose to focus on mutation instead of immutable transformation for two reasons:
Plus, “immutable” transformation can always be emulated by creating a mutable copy of the input.
This API will eventually be useful for granular table/collection view updates.
@dnalot @iMartinKiss @indragiek
To do:
NSTableView
sUITableView
bindingsUICollectionView
bindings