Skip to content

Commit

Permalink
increase coalesce window when collection-view is not visible
Browse files Browse the repository at this point in the history
Summary: In case the collection-view is not visible, lets try batching more updates together to minimize the amount of off-screen work we do.

Differential Revision: D60691861

fbshipit-source-id: 154164fcd370e016c12bace60269c946a153c284
  • Loading branch information
Maxime Ollivier authored and facebook-github-bot committed Aug 6, 2024
1 parent f429ec5 commit 420fa27
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Source/IGListDiffKit/IGListExperiments.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ typedef struct IGListAdaptiveCoalescingExperimentConfig {
NSTimeInterval intervalIncrement;
/// This is the maximum coalesce interval, so the slowest and update can wait.
NSTimeInterval maxInterval;
/// Coalece using `maxInterval` if view is not visible according to `IGListViewVisibilityTracker`
BOOL useMaxIntervalWhenViewNotVisible;
} IGListAdaptiveCoalescingExperimentConfig;

/**
Expand Down
2 changes: 1 addition & 1 deletion Source/IGListKit/IGListAdapterUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ - (void)_queueUpdateIfNeeded {
}

// Will call `-performUpdateWithCoalescer`
[self.coalescer queueUpdate];
[self.coalescer queueUpdateForView:self.transactionBuilder.collectionView];
}

- (void)performUpdateWithCoalescer:(IGListUpdateCoalescer *)coalescer {
Expand Down
10 changes: 7 additions & 3 deletions Source/IGListKit/Internal/IGListUpdateCoalescer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#if __has_include(<IGListDiffKit/IGListDiffKit.h>)
#import <IGListDiffKit/IGListExperiments.h>
Expand Down Expand Up @@ -34,8 +34,12 @@ NS_SWIFT_NAME(ListUpdateCoalescer)

@property (nonatomic, weak) id<IGListUpdateCoalescerDelegate> delegate;

/// Start coalescing updates, which will eventually call `-performUpdateWithCoalescer`
- (void)queueUpdate;
/**
Start coalescing updates, which will eventually call `-performUpdateWithCoalescer`
@params view View used to track visibility (if enabled in config)
*/
- (void)queueUpdateForView:(nullable UIView *)view;

@end

Expand Down
47 changes: 32 additions & 15 deletions Source/IGListKit/Internal/IGListUpdateCoalescer.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#import "IGListUpdateCoalescer.h"

#import "IGListViewVisibilityTracker.h"

@implementation IGListUpdateCoalescer {
BOOL _hasQueuedUpdate;

Expand All @@ -15,7 +17,7 @@ @implementation IGListUpdateCoalescer {
NSTimeInterval _coalescenceInterval;
}

- (void)queueUpdate {
- (void)queueUpdateForView:(nullable UIView *)view {
if (_hasQueuedUpdate) {
return;
}
Expand All @@ -26,7 +28,7 @@ - (void)queueUpdate {
// details on how coalescence is done.

if (self.adaptiveCoalescingExperimentConfig.enabled) {
[self _adaptiveDispatchUpdate];
[self _adaptiveDispatchUpdateForView:view];
} else {
[self _regularDispatchUpdate];
}
Expand All @@ -40,26 +42,41 @@ - (void)_regularDispatchUpdate {
});
}

- (void)_adaptiveDispatchUpdate {
static BOOL _isViewVisible(UIView *_Nullable view, IGListAdaptiveCoalescingExperimentConfig config) {
if (config.useMaxIntervalWhenViewNotVisible) {
IGListViewVisibilityTracker *const tracker = IGListViewVisibilityTrackerAttachedOnView((UIView *)view);
if (tracker && tracker.state == IGListViewVisibilityStateNotVisible) {
return NO;
}
}

return YES;
}

- (void)_adaptiveDispatchUpdateForView:(nullable UIView *)view {
const IGListAdaptiveCoalescingExperimentConfig config = _adaptiveCoalescingExperimentConfig;

const NSTimeInterval timeSinceLastUpdate = -[_lastUpdateStartDate timeIntervalSinceNow];
if (!_lastUpdateStartDate || timeSinceLastUpdate > _coalescenceInterval) {
// It's been long enough, so lets reset interval and perform update right away
_coalescenceInterval = config.minInterval;
[self _performUpdate];
return;
const BOOL isViewVisible = _isViewVisible(view, config);

if (isViewVisible) {
if (!_lastUpdateStartDate || timeSinceLastUpdate > _coalescenceInterval) {
// It's been long enough, so lets reset interval and perform update right away
_coalescenceInterval = config.minInterval;
[self _performUpdate];
return;
} else {
// If we keep hitting the delay, lets increase it.
_coalescenceInterval = MIN(_coalescenceInterval + config.intervalIncrement, config.maxInterval);
}
}

// If we keep hitting the delay, lets increase it.
_coalescenceInterval = MIN(_coalescenceInterval + config.intervalIncrement, config.maxInterval);


// Delay by the time remaining in the interval
const NSTimeInterval remainingTime = MAX(_coalescenceInterval - timeSinceLastUpdate, 0);
const NSTimeInterval remainingTime = isViewVisible ? (_coalescenceInterval - timeSinceLastUpdate) : config.maxInterval;
const NSTimeInterval remainingTimeCapped = MAX(remainingTime, 0);

_hasQueuedUpdate = YES;
__weak __typeof__(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(remainingTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(remainingTimeCapped * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf _performUpdate];
});
}
Expand Down
2 changes: 2 additions & 0 deletions Source/IGListKit/Internal/IGListUpdateTransactionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Change the `UICollectionView` dataSource

- (BOOL)hasChanges;

- (nullable UICollectionView *)collectionView;

@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions Source/IGListKit/Internal/IGListUpdateTransactionBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,8 @@ - (BOOL)hasChanges {
|| self.sectionDataBlock != nil;
}

- (nullable UICollectionView *)collectionView {
return self.collectionViewBlock ? self.collectionViewBlock() : nil;
}

@end

0 comments on commit 420fa27

Please sign in to comment.