Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6571fc8

Browse files
author
tangjiapeng
committed
完善VIPER骨架逻辑,扩充自定义方法
1 parent a59c628 commit 6571fc8

File tree

58 files changed

+3123
-191
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3123
-191
lines changed

‎iOS-Network-Stack-Dive/ArchitectureExtensions/VIPER-Integration/CustomTableViewDemo/TJPViperBaseTableView/View/TJPBaseTableView.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ NS_ASSUME_NONNULL_BEGIN
6262
- (void)configurePullUpRefreshControlWithTarget:(id)target pullUpAction:(SEL)pullUpAction;
6363
/// 结束刷新
6464
- (void)endRefreshing;
65+
/// 没有更多数据
66+
- (void)noMoreData;
67+
6568

6669

6770
/// 空白样式 允许重写

‎iOS-Network-Stack-Dive/ArchitectureExtensions/VIPER-Integration/CustomTableViewDemo/TJPViperBaseTableView/View/TJPBaseTableView.m‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ - (void)endRefreshing {
289289
[self.mj_footer endRefreshing];
290290
}
291291

292+
- (void)noMoreData {
293+
[self.mj_footer endRefreshingWithNoMoreData];
294+
}
295+
292296
//**************************************************
293297

294298

‎iOS-Network-Stack-Dive/ArchitectureExtensions/VIPER-Integration/VIPER-Architecture/Interactor/TJPViperBaseInteractorImpl.h‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
NS_ASSUME_NONNULL_BEGIN
1616

17+
@class TJPPaginationInfo;
18+
1719
@interface TJPViperBaseInteractorImpl : NSObject <TJPViperBaseInteractorProtocol>
1820

1921
// 基础控件
@@ -27,25 +29,45 @@ NS_ASSUME_NONNULL_BEGIN
2729
// 状态管理
2830
@property (nonatomic, assign, readonly) BOOL isInitialized;
2931

32+
// 分页管理
33+
@property (nonatomic, strong, readonly) TJPPaginationInfo *currentPagination;
34+
@property (nonatomic, assign, readonly) NSInteger currentPage;
35+
@property (nonatomic, assign, readonly) BOOL hasMoreData;
36+
@property (nonatomic, assign) NSInteger defaultPageSize;
3037

3138
// 快速定义错误
3239
- (NSError *)createErrorWithCode:(TJPViperError)errorCode description:(NSString *)description;
3340

34-
// 子类需要实现的抽象方法
35-
- (void)performDataRequestForPage:(NSInteger)page
36-
completion:(void (^)(NSArray * _Nullable data, NSInteger totalPage, NSError * _Nullable error))completion;
41+
/// 子类需要实现的抽象方法
42+
- (void)performDataRequestForPage:(NSInteger)page completion:(void (^)(NSArray * _Nullable data, NSInteger totalPage, NSError * _Nullable error))completion;
43+
44+
/// 子类实现带分页信息的抽象方法(推荐)
45+
- (void)performDataRequestForPage:(NSInteger)page withPagination:(void (^)(NSArray * _Nullable data, TJPPaginationInfo * _Nullable pagination, NSError * _Nullable error))completion;
46+
3747

3848
// 子类可重写的方法
3949
- (NSString *)baseURLString;
4050
- (NSDictionary *)commonParameters;
4151
- (NSDictionary *)parametersForPage:(NSInteger)page;
4252
- (NSArray *)processRawResponseData:(id)rawData;
4353
- (NSError * _Nullable)validateResponseData:(id)rawData;
54+
- (TJPPaginationInfo * _Nullable)extractPaginationFromResponse:(id)rawData;
55+
56+
// 分页相关方法
57+
- (void)resetPagination;
58+
- (void)updatePaginationInfo:(TJPPaginationInfo *)paginationInfo;
59+
- (BOOL)canLoadNextPage;
60+
- (NSInteger)getNextPageNumber;
4461

4562
// 工具方法
4663
- (NSString *)cacheKeyForPage:(NSInteger)page;
4764
- (BOOL)shouldCacheDataForPage:(NSInteger)page;
4865

66+
// 分页缓存相关
67+
- (NSString *)paginationCacheKeyForPage:(NSInteger)page;
68+
- (void)cachePaginationInfo:(TJPPaginationInfo *)pagination forPage:(NSInteger)page;
69+
- (TJPPaginationInfo * _Nullable)loadCachedPaginationForPage:(NSInteger)page;
70+
4971
// 子类可重写的扩展方法
5072
- (void)setupInteractor;
5173
- (void)teardownInteractor;

‎iOS-Network-Stack-Dive/ArchitectureExtensions/VIPER-Integration/VIPER-Architecture/Interactor/TJPViperBaseInteractorImpl.m‎

Lines changed: 172 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import "TJPNetworkDefine.h"
1010
#import "TJPViperDefaultErrorHandler.h"
1111
#import "TJPMemoryCache.h"
12+
#import "TJPPaginationInfo.h"
1213

1314
@interface TJPViperBaseInteractorImpl ()
1415

@@ -19,6 +20,11 @@ @interface TJPViperBaseInteractorImpl ()
1920
@property (nonatomic, strong) NSMutableSet<NSString *> *subscribedTopics;
2021

2122

23+
// 分页相关属性
24+
@property (nonatomic, strong) TJPPaginationInfo *currentPagination;
25+
@property (nonatomic, assign) NSInteger currentPage;
26+
@property (nonatomic, assign) BOOL hasMoreData;
27+
2228
@end
2329

2430

@@ -42,6 +48,12 @@ - (instancetype)init {
4248
_uploadProgressMap = [NSMutableDictionary dictionary];
4349
_subscribedTopics = [NSMutableSet set];
4450

51+
// 分页相关初始化
52+
_defaultPageSize = 10;
53+
_currentPage = 0;
54+
_hasMoreData = YES;
55+
_currentPagination = nil;
56+
4557
[self setupInteractor];
4658
_isInitialized = YES;
4759

@@ -74,8 +86,20 @@ - (RACSubject *)navigateToPageSubject {
7486

7587
#pragma mark - Load Data
7688
- (void)fetchDataForPageWithCompletion:(NSInteger)page success:(void (^)(NSArray * _Nullable, NSInteger))success failure:(void (^)(NSError * _Nullable))failure {
77-
//提供标准接口 子类需要重写此方法并实现具体的业务逻辑
78-
TJPLOG_INFO(@"[%@] BaseInteractor 提供了一个标准接口 - fetchDataForPageWithCompletion", NSStringFromClass([self class]));
89+
// 调用新方法并适配旧接口
90+
[self fetchDataForPageWithPagination:page success:^(NSArray * _Nullable data, TJPPaginationInfo * _Nullable pagination) {
91+
if (success) {
92+
NSInteger totalPage = pagination.paginationType == TJPPaginationTypePageBased ? pagination.totalPages : 0;
93+
success(data, totalPage);
94+
}
95+
} failure:failure];
96+
}
97+
98+
- (void)fetchDataForPageWithPagination:(NSInteger)page
99+
success:(void (^)(NSArray * _Nullable, TJPPaginationInfo * _Nullable))success
100+
failure:(void (^)(NSError * _Nullable))failure {
101+
102+
TJPLOG_INFO(@"[%@] 开始请求第 %ld 页数据(带分页信息)", NSStringFromClass([self class]), (long)page);
79103

80104
// 参数验证
81105
if (page <= 0) {
@@ -86,34 +110,60 @@ - (void)fetchDataForPageWithCompletion:(NSInteger)page success:(void (^)(NSArray
86110
return;
87111
}
88112

113+
// 检查是否可以加载下一页
114+
if (page > 1 && _currentPagination && !_currentPagination.canLoadNextPage) {
115+
NSError *error = [NSError errorWithDomain:TJPViperErrorDomain
116+
code:TJPViperErrorBusinessLogicFailed
117+
userInfo:@{NSLocalizedDescriptionKey: @"没有更多数据"}];
118+
if (failure) failure(error);
119+
return;
120+
}
121+
122+
// 检查缓存
123+
NSString *cacheKey = [self cacheKeyForPage:page];
124+
NSString *paginationCacheKey = [self paginationCacheKeyForPage:page];
89125

126+
NSArray *cachedData = [self.cacheManager loadCacheForKey:cacheKey];
127+
TJPPaginationInfo *cachedPagination = [self loadCachedPaginationForPage:page];
90128

91-
// 如果是基类被直接调用,抛出标准的TJPNetworkError
129+
if (cachedData && cachedPagination) {
130+
TJPLOG_INFO(@"[%@] 返回第 %ld 页缓存数据", NSStringFromClass([self class]), (long)page);
131+
[self updatePaginationInfo:cachedPagination];
132+
if (success) success(cachedData, cachedPagination);
133+
return;
134+
}
135+
136+
// 如果是基类被直接调用,抛出错误
92137
if ([self isMemberOfClass:[TJPViperBaseInteractorImpl class]]) {
93138
NSError *error = [NSError errorWithDomain:TJPViperErrorDomain
94139
code:TJPViperErrorBusinessLogicFailed
95140
userInfo:@{NSLocalizedDescriptionKey: @"子类必须重写此方法"}];
96141
if (failure) failure(error);
97142
return;
98143
}
99-
100-
TJPLOG_INFO(@"[%@] 正在请求第 %ld 页的数据", NSStringFromClass([self class]), (long)page);
101144

102145
// 执行具体的数据请求(由子类实现)
103-
[self performDataRequestForPage:page completion:^(NSArray *data, NSInteger totalPage, NSError *error) {
146+
[self performDataRequestForPage:page withPagination:^(NSArray *data, TJPPaginationInfo *pagination, NSError *error) {
104147
if (error) {
105148
TJPLOG_ERROR(@"数据请求失败: %@", error.localizedDescription);
106149
if (failure) failure(error);
107150
} else {
108151
TJPLOG_INFO(@"数据请求成功: %lu 条数据", (unsigned long)data.count);
109152

110-
// 缓存数据
153+
// 更新分页信息
154+
if (pagination) {
155+
[self updatePaginationInfo:pagination];
156+
}
157+
158+
// 缓存数据和分页信息
111159
if ([self shouldCacheDataForPage:page] && data.count > 0) {
112-
NSString *cacheKey = [self cacheKeyForPage:page];
113160
[self.cacheManager saveCacheWithData:data forKey:cacheKey expireTime:TJPCacheExpireTimeMedium];
161+
if (pagination) {
162+
[self cachePaginationInfo:pagination forPage:page];
163+
}
114164
}
115165

116-
if (success) success(data, totalPage);
166+
if (success) success(data, pagination);
117167
}
118168
}];
119169
}
@@ -211,16 +261,59 @@ - (void)searchDataWithKeyword:(NSString *)keyword filters:(NSDictionary *)filter
211261
});
212262
}
213263

264+
- (TJPPaginationInfo * _Nullable)extractPaginationFromResponse:(id)rawData {
265+
// 基类提供默认实现,子类可重写
266+
if ([rawData isKindOfClass:[NSDictionary class]]) {
267+
NSDictionary *dict = (NSDictionary *)rawData;
268+
NSDictionary *paginationDict = dict[@"pagination"] ?: dict[@"page_info"] ?: dict[@"paging"];
269+
270+
if (paginationDict) {
271+
return [TJPPaginationInfo paginationWithDict:paginationDict];
272+
}
273+
}
274+
return nil;
275+
}
276+
214277
#pragma mark - Manage Cache
278+
- (NSString *)paginationCacheKeyForPage:(NSInteger)page {
279+
return [NSString stringWithFormat:@"%@_pagination_page_%ld", NSStringFromClass([self class]), (long)page];
280+
}
281+
282+
- (void)cachePaginationInfo:(TJPPaginationInfo *)pagination forPage:(NSInteger)page {
283+
NSString *cacheKey = [self paginationCacheKeyForPage:page];
284+
NSDictionary *paginationDict = [pagination toDictionary];
285+
[self.cacheManager saveCacheWithData:paginationDict forKey:cacheKey expireTime:TJPCacheExpireTimeMedium];
286+
287+
TJPLOG_INFO(@"[%@] 缓存第 %ld 页分页信息", NSStringFromClass([self class]), (long)page);
288+
}
289+
290+
- (TJPPaginationInfo * _Nullable)loadCachedPaginationForPage:(NSInteger)page {
291+
NSString *cacheKey = [self paginationCacheKeyForPage:page];
292+
NSDictionary *cachedDict = [self.cacheManager loadCacheForKey:cacheKey];
293+
294+
if (cachedDict && [cachedDict isKindOfClass:[NSDictionary class]]) {
295+
TJPLOG_INFO(@"[%@] 加载第 %ld 页缓存分页信息", NSStringFromClass([self class]), (long)page);
296+
return [TJPPaginationInfo paginationWithDict:cachedDict];
297+
}
298+
299+
return nil;
300+
}
215301

216302
- (void)clearCache:(NSString *)cacheKey {
217303
TJPLOG_INFO(@"[%@] 清理缓存,缓存键: %@", NSStringFromClass([self class]), cacheKey);
218304
[self.cacheManager removeCacheForKey:cacheKey];
305+
306+
// 同时清理对应的分页缓存
307+
if ([cacheKey containsString:@"_page_"]) {
308+
NSString *paginationCacheKey = [cacheKey stringByReplacingOccurrencesOfString:@"_page_" withString:@"_pagination_page_"];
309+
[self.cacheManager removeCacheForKey:paginationCacheKey];
310+
}
219311
}
220312

221313
- (void)clearAllCache {
222314
TJPLOG_INFO(@"[%@] 清理所有缓存", NSStringFromClass([self class]));
223315
[self.cacheManager clearAllCache];
316+
[self resetPagination]; // 同时重置分页信息
224317
}
225318

226319
- (NSUInteger)getCacheSize {
@@ -354,11 +447,22 @@ - (NSError * _Nullable)validateBusinessRules:(NSDictionary *)data {
354447
#pragma mark - Abstract Methods
355448
- (void)performDataRequestForPage:(NSInteger)page
356449
completion:(void (^)(NSArray * _Nullable, NSInteger, NSError * _Nullable))completion {
357-
// 这是抽象方法,子类必须实现
450+
// 调用新的带分页信息的方法
451+
[self performDataRequestForPage:page withPagination:^(NSArray * _Nullable data, TJPPaginationInfo * _Nullable pagination, NSError * _Nullable error) {
452+
if (completion) {
453+
NSInteger totalPage = pagination && pagination.paginationType == TJPPaginationTypePageBased ? pagination.totalPages : 0;
454+
completion(data, totalPage, error);
455+
}
456+
}];
457+
}
458+
459+
- (void)performDataRequestForPage:(NSInteger)page
460+
withPagination:(void (^)(NSArray * _Nullable, TJPPaginationInfo * _Nullable, NSError * _Nullable))completion {
461+
// 这是新的抽象方法,子类必须实现
358462
NSError *error = [NSError errorWithDomain:TJPViperErrorDomain
359463
code:TJPViperErrorBusinessLogicFailed
360-
userInfo:@{NSLocalizedDescriptionKey: @"子类必须实现performDataRequestForPage:completion:方法"}];
361-
if (completion) completion(nil, 0, error);
464+
userInfo:@{NSLocalizedDescriptionKey: @"子类必须实现performDataRequestForPage:withPagination:方法"}];
465+
if (completion) completion(nil, nil, error);
362466
}
363467

364468
#pragma mark - Methods for Subclass Override
@@ -421,9 +525,65 @@ - (void)teardownInteractor {
421525
// 清理上传进度
422526
[self.uploadProgressMap removeAllObjects];
423527

528+
// 清理分页信息
529+
[self resetPagination];
530+
531+
424532
// 子类可重写此方法进行清理工作
425533
}
426534

535+
#pragma mark - Pagination Management
536+
- (void)resetPagination {
537+
_currentPage = 0;
538+
_hasMoreData = YES;
539+
_currentPagination = nil;
540+
541+
TJPLOG_INFO(@"[%@] 分页信息已重置", NSStringFromClass([self class]));
542+
}
543+
544+
- (void)updatePaginationInfo:(TJPPaginationInfo *)paginationInfo {
545+
NSLog(@"[DEBUG] updatePaginationInfo 接收到的分页信息:");
546+
NSLog(@" - currentPage: %ld", (long)paginationInfo.currentPage);
547+
NSLog(@" - hasMore: %@", paginationInfo.hasMore ? @"YES" : @"NO");
548+
NSLog(@" - getNextPageNumber: %ld", (long)paginationInfo.getNextPageNumber);
549+
NSLog(@" - paginationType: %ld", (long)paginationInfo.paginationType);
550+
551+
_currentPagination = [paginationInfo copy];
552+
553+
if (paginationInfo.paginationType == TJPPaginationTypePageBased) {
554+
_currentPage = paginationInfo.currentPage;
555+
_hasMoreData = paginationInfo.hasMore;
556+
} else {
557+
// 游标分页时,currentPage 用于记录已加载的页面数
558+
_currentPage = _currentPage > 0 ? _currentPage : 1;
559+
_hasMoreData = paginationInfo.hasMore;
560+
}
561+
562+
TJPLOG_INFO(@"[%@] 分页信息已更新: %@", NSStringFromClass([self class]), paginationInfo.debugDescription);
563+
}
564+
565+
- (BOOL)canLoadNextPage {
566+
NSLog(@"[DEBUG] canLoadNextPage: hasMoreData=%@, currentPage=%ld", _hasMoreData ? @"YES" : @"NO", (long)self.currentPage);
567+
return _hasMoreData && _currentPagination.canLoadNextPage;
568+
}
569+
570+
- (NSInteger)getNextPageNumber {
571+
NSInteger nextPage;
572+
if (!_currentPagination) {
573+
nextPage = 1;
574+
} else if (_currentPagination.paginationType == TJPPaginationTypePageBased) {
575+
nextPage = _currentPagination.getNextPageNumber;
576+
} else {
577+
nextPage = _currentPage + 1;
578+
}
579+
580+
NSLog(@"[DEBUG] getNextPageNumber: currentPage=%ld, nextPage=%ld", (long)_currentPage, (long)nextPage);
581+
NSLog(@"[DEBUG] 调用栈: %@", [NSThread callStackSymbols]);
582+
583+
return nextPage;
584+
}
585+
586+
427587
#pragma mark - Private Methods
428588

429589
- (void)simulateRealTimeDataForTopic:(NSString *)topic {

‎iOS-Network-Stack-Dive/ArchitectureExtensions/VIPER-Integration/VIPER-Architecture/Interactor/TJPViperBaseInteractorProtocol.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
NS_ASSUME_NONNULL_BEGIN
1313

14+
@class TJPPaginationInfo;
15+
16+
1417
@protocol TJPViperBaseInteractorProtocol <NSObject>
1518

1619
#pragma mark - 响应式信号
@@ -31,6 +34,9 @@ NS_ASSUME_NONNULL_BEGIN
3134
*/
3235
- (void)fetchDataForPageWithCompletion:(NSInteger)page success:(void (^)(NSArray * _Nullable data, NSInteger totalPage))success failure:(void (^)(NSError * _Nullable error))failure;
3336

37+
// 带分页信息的数据加载(推荐使用)
38+
- (void)fetchDataForPageWithPagination:(NSInteger)page success:(void (^)(NSArray * _Nullable data, TJPPaginationInfo * _Nullable pagination))success failure:(void (^)(NSError * _Nullable error))failure;
39+
3440

3541
#pragma mark - 数据操作
3642

@@ -134,6 +140,11 @@ NS_ASSUME_NONNULL_BEGIN
134140
- (NSError * _Nullable)validateBusinessRules:(NSDictionary *)data;
135141

136142

143+
#pragma mark - 分页管理
144+
- (void)resetPagination;
145+
- (BOOL)canLoadNextPage;
146+
- (NSInteger)getNextPageNumber;
147+
137148
@optional
138149

139150
@end

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /