Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Conversation

@nguyenhuy
Copy link
Contributor

@nguyenhuy nguyenhuy commented Feb 11, 2017

Author: Thanh Huy Nguyen

This PR has 2 main goals:

Remaining tasks:

This PR is still a WIP and contains many TODOs. However, I'd appreciate any early feedbacks and help for testing. Thanks!

Ticket: TextureGroup/Texture#186

@nguyenhuy nguyenhuy force-pushed the HNRefactorDataControllerTake3 branch 2 times, most recently from a07b4a9 to 0d62525 Compare February 11, 2017 08:26
Copy link
Contributor

@Adlai-Holler Adlai-Holler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an absolutely massive improvement in data controller! I love it, and we're gonna be able to move much much faster from here on out. Left some comments but hopefully we can land ASAP.

*/
@property (nonatomic, assign) BOOL neverShowPlaceholders;

//TODO shouldn't this be a generic kind (e.g includes ASDataControllerRowNodeKind)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ASDataControllerRowNodeKind is private, we should expose that as nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we have a common ground that this property should be called kind and exposed as nil for row kind?

On the other hand, can ASDataControllerRowNodeKind possibly be exposed/public?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm down for kind or elementKind 👍

{
[self endUpdatesCompletion:nil];
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on! Removing...

}
//TODO Reimplement _automaticallyAdjustsContentOffset
// if (_automaticallyAdjustsContentOffset) {
// [self beginAdjustingContentOffset];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag has a very tumultuous history, but we should really strive to support it in every version unless we make it unavailable. If it's absolutely unbearable to implement – even in a rough way – we should be sure to put in the release notes that this flag is not functional in this version.

if (!_performingBatchUpdates) {
[_rangeController updateIfNeeded];
}
// TODO Should reloadData triggers batch fetching?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should. If I reload data, and I don't have e.g. 2 screenfuls of data ahead of me, I should attempt to batch fetch.

});

//TODO is _automaticallyAdjustsContentOffset needed for reloadData?
// if (_automaticallyAdjustsContentOffset) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm good question. I think it should not apply to reloadData because we have to assume that every single item was replaced by something completely different.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!


@end

NS_ASSUME_NONNULL_END
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WOW I'm glad to see this file gone ⚡️ 🎉 🌴

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super great!!! Thanks @nguyenhuy!

*/
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray<NSArray<ASCellNode *> *> *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)dataController:(ASDataController *)dataController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Very happy to see this argument removed. Let's phrase it didInsertSections:(NSIndexSet *)sectionIndexes since it's clear that we're passing an index set.

_completedNodeContexts = loadingNodeContexts;

// Step 3: Now that _completedNodeContexts is ready, call UIKit to update using the original change set.
[_delegate dataControllerDidReloadData:self];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Formal reloadData!

_completedNodeContexts = loadingNodeContexts;

// Step 3: Now that _completedNodeContexts is ready, call UIKit to update using the original change set.
[self _forwardChangeSetToDelegate:changeSet animated:animated];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How hard would it be to fuse reloadData into updateWithChangeSet: by adding a flag like _ASHierarchyChangeSet.includesReloadData? Much of the internal processing of _ASHierarchyChangeSet would be skipped when that flag is set, and we use it to determine e.g. how to report to the delegate.

That way there's only one implementation of this core update method.

ASMoveElementInTwoDimensionalArray(_externalCompletedNodes, indexPath, newIndexPath);
ASMoveElementInTwoDimensionalArray(_completedNodes[ASDataControllerRowNodeKind], indexPath, newIndexPath);
NSMutableArray<ASNodeContextTwoDimensionalArray *> *deepCopy = [NSMutableArray arrayWithCapacity:originalDict.count];
for (ASNodeContextTwoDimensionalArray *sections in originalDict.allValues) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than copying the array with allValues, let's use objectEnumerator here and below.

Check optional methods in ASDataControllerSource
- No more new/inserted contexts flag
- Encapsulate code shared between reloadData and updateWithChangeSet
- Remove dataControllerWillDeleteAllData delegate method
- Hierarchy changes no longer needs to conform to NSCopying
- Reword TODOs
… index space

- This helps to avoid immature node allocation, especially when node virtualization is a thing
- However, this means that -reloadDataInitiallyIfNeeded in ASCollectionNode and ASTableNode must wait until all updates are finished.
…h the assumption that clients absolutely need it.

- Revisit this when node virtualization is implemented.
- It can't be used in conjuntion with other updates.
- Calling it inside a batch update during the initial load can cause data inconsistency thrown by UICollectionView/UITableView
- Replace delegate methods in these protocols with -willUpdateWithChangeSet and -didUpdateWithChangeSet.
- ASRangeController, ASCollectionView and ASTableView are simplified because of this.
…ctionView's reloadData

- Since UICollectionView's reloadData doesn't requery data source but defers until the next layout pass,  we need to wait until then to update range controller and do batch fetching.
- `-[ASCollectionView waitUntilAllUpdatesAreCommited]` needs to force a layout pass to make sure
everything is ready after it returns.
…e nodes. Other nodes will be re-measured later.
@nguyenhuy nguyenhuy force-pushed the HNRefactorDataControllerTake3 branch from d3a8169 to 0983866 Compare February 21, 2017 14:05
@nguyenhuy
Copy link
Contributor Author

nguyenhuy commented Feb 21, 2017

@Adlai-Holler @appleguy This PR is finally ready for review! As it is getting huge, I'd like to merge this ASAP and work on follow up tasks. Thank you!

Copy link
Contributor

@Adlai-Holler Adlai-Holler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely fantastic! I am so so happy to have these changes in. Mostly non-blocking feedback. We can build quite a bit from here! ❤️ @nguyenhuy

//#define LOG(...) NSLog(__VA_ARGS__)
#define LOG(...)
#define LOG(...) NSLog(__VA_ARGS__)
//#define LOG(...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn logging back off

* @param changeSet The change set that includes all updates
*/
- (void)rangeController:(ASRangeController *)rangeController didDeleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)rangeController:(ASRangeController * )rangeController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet;;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Remove the space

* @param changeSet The change set that includes all updates
*/
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray<NSArray<ASCellNode *> *> *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)dataController:(ASDataController * )dataController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet;;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove stray space

// Dictionary with each entry is a pair of "kind" key and two dimensional array of node contexts
#define ASNodeContextTwoDimensionalDictionary NSDictionary<NSString *, ASNodeContextTwoDimensionalArray *>
// Mutable dictionary with each entry is a pair of "kind" key and two dimensional array of node contexts
#define ASNodeContextTwoDimensionalMutableDictionary NSMutableDictionary<NSString *, ASNodeContextTwoDimensionalMutableArray *>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

#pragma mark - Cell Layout

- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts batchSize:(NSUInteger)batchSize batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: When adding new integer variables, let's standardize on NSInteger because it's simpler and the Swift import is way easier.

}

ASDisplayNodeAssert(NO, @"Unknown constrained size for node of kind %@ by data source %@", kind, _dataSource);
return ASSizeRangeMake(CGSizeZero, CGSizeZero);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ASSizeRangeZero

* @param changeSet The change set that includes all updates
*/
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
- (void)rangeController:(ASRangeController * )rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet;;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the space


@property (nonatomic, readonly) BOOL completed;
/// Whether or not changes should be animated.
@property (nonatomic, readwrite) BOOL animated;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, putting this flag here. My reckoning here is, if any update in the stack is non-animated, the whole update should be non-animated. Would be fine as a follow-up task or discussion, or implement it here.

if (0 < (_originalDeleteSectionChanges.count + _originalDeleteItemChanges.count
+_originalInsertSectionChanges.count + _originalInsertItemChanges.count
+ _reloadSectionChanges.count + _reloadItemChanges.count)) {
ASFailUpdateValidation(@"Attempt to reload data in conjuntion with other updates.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can probably remove this and be lenient on them. Right?

- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
[result addObject:@{ @"_includesReloadData" : @(_includesReloadData) }];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: string should just be "includesReloadData"

@nguyenhuy nguyenhuy changed the title [WIP][ASDataController] Refactor ASDataController [ASDataController] Refactor ASDataController Feb 21, 2017
@nguyenhuy nguyenhuy force-pushed the HNRefactorDataControllerTake3 branch from b4698a4 to 483f6eb Compare February 22, 2017 12:46
@Adlai-Holler
Copy link
Contributor

Here, we, go!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants