Skip to content

Commit b3ae158

Browse files
committed
Unit tests for batch fetching
1 parent 1f8001a commit b3ae158

11 files changed

Lines changed: 248 additions & 53 deletions

File tree

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@
145145
292C59A31A956527007E5DD6 /* ASRangeHandlerRender.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */; settings = {ATTRIBUTES = (Public, ); }; };
146146
292C59A41A956527007E5DD6 /* ASRangeHandlerRender.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */; };
147147
299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; };
148+
296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Public, ); }; };
149+
296A0A2F1A9516B2005ACEAA /* ASBatchFetching.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */; };
150+
296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */; };
148151
299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
149152
299DA1AA1A828D2900162D41 /* ASBatchContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.m */; };
150153
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
@@ -299,6 +302,10 @@
299302
292C599C1A956527007E5DD6 /* ASRangeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandler.h; sourceTree = "<group>"; };
300303
292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerRender.h; sourceTree = "<group>"; };
301304
292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeHandlerRender.mm; sourceTree = "<group>"; };
305+
296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchFetching.h; sourceTree = "<group>"; };
306+
296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetching.m; sourceTree = "<group>"; };
307+
296A0A311A951715005ACEAA /* ASScrollDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = AsyncDisplayKit/Details/ASScrollDirection.h; sourceTree = SOURCE_ROOT; };
308+
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetchingTests.m; sourceTree = "<group>"; };
302309
299DA1A71A828D2900162D41 /* ASBatchContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchContext.h; sourceTree = "<group>"; };
303310
299DA1A81A828D2900162D41 /* ASBatchContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchContext.m; sourceTree = "<group>"; };
304311
3C9C128419E616EF00E942A0 /* ASTableViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewTests.m; sourceTree = "<group>"; };
@@ -432,6 +439,7 @@
432439
058D09C5195D04C000B7D73C /* AsyncDisplayKitTests */ = {
433440
isa = PBXGroup;
434441
children = (
442+
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */,
435443
2911485B1A77147A005D0878 /* ASControlNodeTests.m */,
436444
058D0A2D195D057000B7D73C /* ASDisplayLayerTests.m */,
437445
058D0A2E195D057000B7D73C /* ASDisplayNodeAppearanceTests.m */,
@@ -472,6 +480,8 @@
472480
054963481A1EA066000F8E56 /* ASBasicImageDownloader.mm */,
473481
299DA1A71A828D2900162D41 /* ASBatchContext.h */,
474482
299DA1A81A828D2900162D41 /* ASBatchContext.m */,
483+
296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */,
484+
296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */,
475485
464052191A3F83C40061C0BA /* ASDataController.h */,
476486
4640521A1A3F83C40061C0BA /* ASDataController.mm */,
477487
05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */,
@@ -497,6 +507,7 @@
497507
292C599C1A956527007E5DD6 /* ASRangeHandler.h */,
498508
292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */,
499509
292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */,
510+
296A0A311A951715005ACEAA /* ASScrollDirection.h */,
500511
058D09EA195D050800B7D73C /* ASTextNodeCoreTextAdditions.h */,
501512
058D09EB195D050800B7D73C /* ASTextNodeCoreTextAdditions.m */,
502513
058D09EC195D050800B7D73C /* ASTextNodeRenderer.h */,
@@ -600,6 +611,7 @@
600611
058D0A4F195D05CB00B7D73C /* ASImageNode.h in Headers */,
601612
058D0A50195D05CB00B7D73C /* ASImageNode.mm in Headers */,
602613
058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */,
614+
296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */,
603615
058D0A52195D05CB00B7D73C /* ASTextNode.mm in Headers */,
604616
055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */,
605617
292C59A31A956527007E5DD6 /* ASRangeHandlerRender.h in Headers */,
@@ -832,6 +844,7 @@
832844
058D0A29195D050800B7D73C /* ASDisplayNode+DebugTiming.mm in Sources */,
833845
058D0A22195D050800B7D73C /* _ASAsyncTransaction.m in Sources */,
834846
055F1A3919ABD413004DAFF1 /* ASRangeController.mm in Sources */,
847+
296A0A2F1A9516B2005ACEAA /* ASBatchFetching.m in Sources */,
835848
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */,
836849
);
837850
runOnlyForDeploymentPostprocessing = 0;
@@ -841,6 +854,7 @@
841854
buildActionMask = 2147483647;
842855
files = (
843856
2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */,
857+
296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */,
844858
058D0A3E195D057000B7D73C /* ASTextNodeRendererTests.m in Sources */,
845859
058D0A3D195D057000B7D73C /* ASTextNodeCoreTextAdditionsTests.m in Sources */,
846860
058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */,

AsyncDisplayKit/ASCollectionView.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185
* If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching
186186
* should occur.
187187
*/
188-
- (BOOL)shouldBatchFetchForCollectionView:(UICollectionView *)collectionView;
188+
- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView;
189189

190190
/**
191191
* Receive a message that the collectionView is near the end of its data set and more data should be fetched if
@@ -200,7 +200,7 @@
200200
* UICollectionView currently only supports batch events for tail loads. If you require a head load, consider
201201
* implementing a UIRefreshControl.
202202
*/
203-
- (void)collectionView:(UICollectionView *)collectionView beginBatchFetchingWithContext:(ASBatchContext *)context;
203+
- (void)collectionView:(ASCollectionView *)collectionView beginBatchFetchingWithContext:(ASBatchContext *)context;
204204

205205
@end
206206

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import "ASRangeController.h"
1414
#import "ASDataController.h"
1515
#import "ASDisplayNodeInternal.h"
16+
#import "ASBatchFetching.h"
1617

1718
const static NSUInteger kASCollectionViewAnimationNone = 0;
1819

@@ -144,6 +145,8 @@ - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionVi
144145

145146
_batchContext = [[ASBatchContext alloc] init];
146147

148+
_leadingScreensForBatching = 1.0;
149+
147150
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
148151
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
149152

@@ -386,40 +389,20 @@ - (BOOL)shouldFetchBatch
386389
if ([self.asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]) {
387390
return [self.asyncDelegate shouldBatchFetchForCollectionView:self];
388391
} else {
389-
return YES;
392+
// if the delegate does not respond to this method, there is no point in starting to fetch
393+
return [self.asyncDelegate respondsToSelector:@selector(collectionView:beginBatchFetchingWithContext:)];
390394
}
391395
}
392396

393397
- (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset
394398
{
395399
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
396400

397-
// Bail if we are already fetching, the delegate doesn't care, or we're told not to fetch
398-
if ([_batchContext isFetching] ||
399-
![self.asyncDelegate respondsToSelector:@selector(collectionView:beginBatchFetchingWithContext:)] ||
400-
![self shouldFetchBatch]) {
401+
if (![self shouldFetchBatch]) {
401402
return;
402403
}
403404

404-
ASScrollDirection scrollDirection = [self scrollDirection];
405-
CGFloat viewSize, offset, contentSize;
406-
407-
if (scrollDirection == ASScrollDirectionUp) {
408-
viewSize = CGRectGetHeight(self.bounds);
409-
offset = targetOffset.y;
410-
contentSize = self.contentSize.height;
411-
} else { // horizontal
412-
viewSize = CGRectGetWidth(self.bounds);
413-
offset = targetOffset.x;
414-
contentSize = self.contentSize.width;
415-
}
416-
417-
CGFloat triggerDistance = viewSize * _leadingScreensForBatching;
418-
419-
// Determine if the offset that we are headed to is within the number of screens we have defined
420-
// ASCollectionView supports tail loading only currently, hence the check against Up and Left
421-
BOOL supportedBatchScrollDirection = scrollDirection == ASScrollDirectionUp || ASScrollDirectionLeft;
422-
if (supportedBatchScrollDirection && contentSize - (viewSize + offset) <= triggerDistance) {
405+
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) {
423406
[_batchContext beginBatchFetching];
424407
[self.asyncDelegate collectionView:self beginBatchFetchingWithContext:_batchContext];
425408
}

AsyncDisplayKit/ASTableView.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
* If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching
191191
* should occur.
192192
*/
193-
- (BOOL)shouldBatchFetchForTableView:(UITableView *)tableView;
193+
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView;
194194

195195
/**
196196
* Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
@@ -204,7 +204,7 @@
204204
* ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a
205205
* UIRefreshControl.
206206
*/
207-
- (void)tableView:(UITableView *)tableView beginBatchFetchingWithContext:(ASBatchContext *)context;
207+
- (void)tableView:(ASTableView *)tableView beginBatchFetchingWithContext:(ASBatchContext *)context;
208208

209209
@end
210210

AsyncDisplayKit/ASTableView.mm

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#import "ASLayoutController.h"
1515
#import "ASRangeController.h"
1616
#import "ASDisplayNodeInternal.h"
17-
17+
#import "ASBatchFetching.h"
1818

1919

2020
#pragma mark -
@@ -395,30 +395,20 @@ - (BOOL)shouldFetchBatch
395395
if ([self.asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]) {
396396
return [self.asyncDelegate shouldBatchFetchForTableView:self];
397397
} else {
398-
return YES;
398+
// if the delegate does not respond to this method, there is no point in starting to fetch
399+
return [self.asyncDelegate respondsToSelector:@selector(tableView:beginBatchFetchingWithContext:)];
399400
}
400401
}
401402

402403
- (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset
403404
{
404405
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
405406

406-
// Bail if we are already fetching, the delegate doesn't care, or we're told not to fetch
407-
if ([_batchContext isFetching] ||
408-
![self.asyncDelegate respondsToSelector:@selector(tableView:beginBatchFetchingWithContext:)] ||
409-
![self shouldFetchBatch]) {
407+
if (![self shouldFetchBatch]) {
410408
return;
411409
}
412410

413-
CGFloat viewHeight = CGRectGetHeight(self.bounds);
414-
CGFloat triggerDistance = viewHeight * _leadingScreensForBatching;
415-
CGFloat offset = targetOffset.y;
416-
CGFloat contentHeight = self.contentSize.height;
417-
418-
// Determine if the offset that we are headed to is within the number of screens we have defined
419-
// ASTableView supports tail loading only currently, hence the check against ASScrollDirectionUp
420-
if ([self scrollDirection] == ASScrollDirectionUp &&
421-
contentHeight - (viewHeight + offset) <= triggerDistance) {
411+
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) {
422412
[_batchContext beginBatchFetching];
423413
[self.asyncDelegate tableView:self beginBatchFetchingWithContext:_batchContext];
424414
}

AsyncDisplayKit/Details/ASBatchContext.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* of patent rights can be found in the PATENTS file in the same directory.
77
*/
88

9+
#import <Foundation/Foundation.h>
10+
911
/**
1012
* @abstract A context object to notify when batch fetches are finished or cancelled.
1113
*/
@@ -29,8 +31,6 @@
2931
*/
3032
- (void)completeBatchFetching:(BOOL)didComplete;
3133

32-
- (void)beginBatchFetching;
33-
3434
/**
3535
* Ask the context object if the batch fetching process was cancelled by the context owner.
3636
*
@@ -49,4 +49,12 @@
4949
*/
5050
- (void)cancelBatchFetching;
5151

52+
/**
53+
* Notify the context object that fetching has started.
54+
*
55+
* @discussion Call this method only when you are beginning a fetch process. This should really only be called by the
56+
* context object's owner. Calling this method should be complimented with -completeBatchFetching:.
57+
*/
58+
- (void)beginBatchFetching;
59+
5260
@end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import <UIKit/UIKit.h>
10+
11+
#import "ASBatchContext.h"
12+
#import "ASScrollDirection.h"
13+
#import "ASBaseDefines.h"
14+
15+
ASDISPLAYNODE_EXTERN_C_BEGIN
16+
17+
/**
18+
@abstract Determine if batch fetching should begin based on the state of the parameters.
19+
@param context The batch fetching context that contains knowledge about in-flight fetches.
20+
@param scrollDirection The current scrolling direction of the scroll view.
21+
@param bounds The bounds of the scrollview.
22+
@param contentSize The content size of the scrollview.
23+
@param targetOffset The offset that the scrollview will scroll to.
24+
@param leadingScreens How many screens in the remaining distance will trigger batch fetching.
25+
@return Whether or not the current state should proceed with batch fetching.
26+
@discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and
27+
* ASCollectionView batch fetching API.
28+
*/
29+
extern BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
30+
ASScrollDirection scrollDirection,
31+
CGRect bounds,
32+
CGSize contentSize,
33+
CGPoint targetOffset,
34+
CGFloat leadingScreens);
35+
36+
ASDISPLAYNODE_EXTERN_C_END
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import "ASBatchFetching.h"
10+
11+
BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
12+
ASScrollDirection scrollDirection,
13+
CGRect bounds,
14+
CGSize contentSize,
15+
CGPoint targetOffset,
16+
CGFloat leadingScreens) {
17+
// do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled
18+
if ([context isFetching]) {
19+
return NO;
20+
}
21+
22+
// no fetching for null states
23+
if (leadingScreens <= 0.0 ||
24+
CGPointEqualToPoint(targetOffset, CGPointZero) ||
25+
CGSizeEqualToSize(contentSize, CGSizeZero) ||
26+
CGRectEqualToRect(bounds, CGRectZero)) {
27+
return NO;
28+
}
29+
30+
// only Up and Left scrolls are currently supported (tail loading)
31+
if (scrollDirection != ASScrollDirectionUp && scrollDirection != ASScrollDirectionLeft) {
32+
return NO;
33+
}
34+
35+
CGFloat viewLength, offset, contentLength;
36+
37+
if (scrollDirection == ASScrollDirectionUp) {
38+
viewLength = bounds.size.height;
39+
offset = targetOffset.y;
40+
contentLength = contentSize.height;
41+
} else { // horizontal
42+
viewLength = bounds.size.width;
43+
offset = targetOffset.x;
44+
contentLength = contentSize.width;
45+
}
46+
47+
CGFloat triggerDistance = viewLength * leadingScreens;
48+
CGFloat remainingDistance = contentLength - viewLength - offset;
49+
50+
return remainingDistance <= triggerDistance;
51+
}

AsyncDisplayKit/Details/ASLayoutController.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,14 @@
1010

1111
#import <AsyncDisplayKit/ASBaseDefines.h>
1212
#import <AsyncDisplayKit/ASLayoutRangeType.h>
13+
#import "ASScrollDirection.h"
14+
1315

1416
typedef struct {
1517
CGFloat leadingBufferScreenfuls;
1618
CGFloat trailingBufferScreenfuls;
1719
} ASRangeTuningParameters;
1820

19-
typedef NS_ENUM(NSInteger, ASScrollDirection) {
20-
ASScrollDirectionNone,
21-
ASScrollDirectionRight,
22-
ASScrollDirectionLeft,
23-
ASScrollDirectionUp,
24-
ASScrollDirectionDown,
25-
};
26-
2721
@protocol ASLayoutController <NSObject>
2822

2923
/**
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import <Foundation/Foundation.h>
10+
11+
typedef NS_ENUM(NSInteger, ASScrollDirection) {
12+
ASScrollDirectionNone,
13+
ASScrollDirectionRight,
14+
ASScrollDirectionLeft,
15+
ASScrollDirectionUp,
16+
ASScrollDirectionDown,
17+
};

0 commit comments

Comments
 (0)