Merge pull request #115 from TouchInstinct/feature/data_loading_and_pagination_wrapper

Feature/data loading and pagination wrapper
This commit is contained in:
Ivan Smolin 2018-03-22 15:09:29 +03:00 committed by GitHub
commit cd00fb2f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1580 additions and 652 deletions

View File

@ -40,17 +40,17 @@ Pod::Spec.new do |s|
ss.source_files = "Sources/**/*.swift"
ss.watchos.exclude_files = [
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Classes/Views/XibView/*",
"Sources/Classes/Views/SpinnerView/*",
"Sources/Extensions/CABasicAnimation/*",
"Sources/Extensions/CGFloat/CGFloat+Pixels.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/NetworkService/NetworkService+RxLoadImage.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/Support/UIScrollView+Support.swift",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift",
@ -59,36 +59,40 @@ Pod::Spec.new do |s|
"Sources/Extensions/UICollectionView/*",
"Sources/Extensions/UIDevice/*",
"Sources/Extensions/UIImage/*",
"Sources/Extensions/UITableView/*",
"Sources/Extensions/UITableView/*",
"Sources/Extensions/UIView/*",
"Sources/Extensions/UIViewController/*",
"Sources/Extensions/UIWindow/*",
"Sources/Protocols/LoadingIndicator.swift",
"Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift",
"Sources/Structures/Views/AnyLoadingIndicator.swift",
"Sources/Structures/DrawingOperations/CALayerDrawingOperation.swift",
"Sources/Structures/DrawingOperations/RoundDrawingOperation.swift",
"Sources/Structures/DrawingOperations/BorderDrawingOperation.swift",
"Sources/Structures/DataLoading/PaginationDataLoading/*"
]
ss.tvos.exclude_files = [
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Structures/Drawing/CALayerDrawingOperation.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/Support/UIScrollView+Support.swift",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift"
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift",
"Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift",
"Sources/Structures/DataLoading/PaginationDataLoading/*"
]
ss.dependency "CocoaLumberjack/Swift", '~> 3.3.0'
ss.dependency "RxSwift", '4.0.0'
ss.dependency "RxCocoa", '4.0.0'
ss.dependency "RxAlamofire", '4.0.0'
ss.dependency "ObjectMapper", '~> 3.0.0'
ss.dependency "CocoaLumberjack/Swift", '~> 3.4'
ss.dependency "RxSwift", '~> 4.1'
ss.dependency "RxCocoa", '~> 4.1'
ss.dependency "RxAlamofire", '~> 4.1'
ss.dependency "ObjectMapper", '~> 3.0'
ss.ios.dependency "TableKit", '~> 2.5.0'
ss.ios.dependency "TableKit", '~> 2.6'
ss.ios.dependency "UIScrollView-InfiniteScroll", '~> 1.0.0'
end
@ -101,18 +105,18 @@ Pod::Spec.new do |s|
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift"
]
ss.dependency "CocoaLumberjack/Swift", '~> 3.3.0'
ss.dependency "RxSwift", '4.0.0'
ss.dependency "RxCocoa", '4.0.0'
ss.dependency "RxAlamofire", '4.0.0'
ss.dependency "ObjectMapper", '~> 3.0.0'
ss.dependency "CocoaLumberjack/Swift", '~> 3.4'
ss.dependency "RxSwift", '~> 4.1'
ss.dependency "RxCocoa", '~> 4.1'
ss.dependency "RxAlamofire", '~> 4.1'
ss.dependency "ObjectMapper", '~> 3.0'
end
s.default_subspec = 'Core'

View File

@ -41,11 +41,6 @@
6714625D1EB3396E00EAB194 /* LogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461CE1EB3396E00EAB194 /* LogFormatter.swift */; };
6714625E1EB3396E00EAB194 /* LogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461CE1EB3396E00EAB194 /* LogFormatter.swift */; };
6714625F1EB3396E00EAB194 /* LogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461CE1EB3396E00EAB194 /* LogFormatter.swift */; };
671462601EB3396E00EAB194 /* PaginationTableViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D01EB3396E00EAB194 /* PaginationTableViewWrapper.swift */; };
671462641EB3396E00EAB194 /* PaginationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D11EB3396E00EAB194 /* PaginationViewModel.swift */; };
671462651EB3396E00EAB194 /* PaginationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D11EB3396E00EAB194 /* PaginationViewModel.swift */; };
671462661EB3396E00EAB194 /* PaginationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D11EB3396E00EAB194 /* PaginationViewModel.swift */; };
671462671EB3396E00EAB194 /* PaginationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D11EB3396E00EAB194 /* PaginationViewModel.swift */; };
671462681EB3396E00EAB194 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D31EB3396E00EAB194 /* NetworkService.swift */; };
671462691EB3396E00EAB194 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D31EB3396E00EAB194 /* NetworkService.swift */; };
6714626A1EB3396E00EAB194 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461D31EB3396E00EAB194 /* NetworkService.swift */; };
@ -108,7 +103,6 @@
671462AD1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F11EB3396E00EAB194 /* Observable+DeferredJust.swift */; };
671462AE1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F11EB3396E00EAB194 /* Observable+DeferredJust.swift */; };
671462AF1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F11EB3396E00EAB194 /* Observable+DeferredJust.swift */; };
671462B41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift */; };
671462B81EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F61EB3396E00EAB194 /* Sequence+ConcurrentMap.swift */; };
671462B91EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F61EB3396E00EAB194 /* Sequence+ConcurrentMap.swift */; };
671462BA1EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461F61EB3396E00EAB194 /* Sequence+ConcurrentMap.swift */; };
@ -304,6 +298,57 @@
6771DFEB1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771DFE91EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift */; };
6771DFEC1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771DFE91EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift */; };
6771DFED1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771DFE91EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift */; };
67745268206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745267206249360024EEEF /* UITableView+PaginationWrappable.swift */; };
67745269206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745267206249360024EEEF /* UITableView+PaginationWrappable.swift */; };
6774526A206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745267206249360024EEEF /* UITableView+PaginationWrappable.swift */; };
6774526C206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774526B206249E30024EEEF /* UICollectionView+PaginationWrappable.swift */; };
6774526D206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774526B206249E30024EEEF /* UICollectionView+PaginationWrappable.swift */; };
6774526E206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774526B206249E30024EEEF /* UICollectionView+PaginationWrappable.swift */; };
6774527020624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774526F20624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift */; };
6774527120624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774526F20624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift */; };
6774527420624E820024EEEF /* DataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527320624E820024EEEF /* DataLoadingModel.swift */; };
6774527520624E820024EEEF /* DataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527320624E820024EEEF /* DataLoadingModel.swift */; };
6774527620624E820024EEEF /* DataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527320624E820024EEEF /* DataLoadingModel.swift */; };
6774527720624E820024EEEF /* DataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527320624E820024EEEF /* DataLoadingModel.swift */; };
67745279206252020024EEEF /* DataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745278206252020024EEEF /* DataLoadingState.swift */; };
6774527A206252020024EEEF /* DataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745278206252020024EEEF /* DataLoadingState.swift */; };
6774527B206252020024EEEF /* DataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745278206252020024EEEF /* DataLoadingState.swift */; };
6774527C206252020024EEEF /* DataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745278206252020024EEEF /* DataLoadingState.swift */; };
67745280206256A20024EEEF /* RxDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527F206256A20024EEEF /* RxDataLoadingModel.swift */; };
67745281206256A20024EEEF /* RxDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527F206256A20024EEEF /* RxDataLoadingModel.swift */; };
67745282206256A20024EEEF /* RxDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527F206256A20024EEEF /* RxDataLoadingModel.swift */; };
67745283206256A20024EEEF /* RxDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774527F206256A20024EEEF /* RxDataLoadingModel.swift */; };
67745286206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */; };
67745287206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */; };
67745288206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */; };
67745289206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */; };
6774528D20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */; };
6774528E20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */; };
6774528F20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */; };
6774529020625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */; };
6774529220625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */; };
6774529320625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */; };
6774529420625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */; };
6774529520625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */; };
6774529A20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */; };
6774529B20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */; };
6774529C20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */; };
6774529D20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */; };
6774529F20625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */; };
677452A020625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */; };
677452A120625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */; };
677452A220625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */; };
677452A420625FA90024EEEF /* RxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A320625FA90024EEEF /* RxDataSource.swift */; };
677452A520625FA90024EEEF /* RxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A320625FA90024EEEF /* RxDataSource.swift */; };
677452A620625FA90024EEEF /* RxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A320625FA90024EEEF /* RxDataSource.swift */; };
677452A720625FA90024EEEF /* RxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A320625FA90024EEEF /* RxDataSource.swift */; };
677452A9206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */; };
677452AA206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */; };
677452AB206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */; };
677452AC206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */; };
677452AE206274630024EEEF /* PaginationWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677452AD206274630024EEEF /* PaginationWrapper.swift */; };
677452B720627FE00024EEEF /* PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF7206175F700BDD9FB /* PaginationWrappable.swift */; };
677452B820627FE00024EEEF /* PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF7206175F700BDD9FB /* PaginationWrappable.swift */; };
6782BBA91EB31D5A0086E0B8 /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6782BBA01EB31D590086E0B8 /* LeadKit.framework */; };
67952C3C1EB3266100B3BA1A /* LeadKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 67186B201EB247A200CFAFFB /* LeadKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
67952C3D1EB3266200B3BA1A /* LeadKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 67186B201EB247A200CFAFFB /* LeadKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -336,14 +381,14 @@
67EB7FD520615D1700BDD9FB /* ResettableCursorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD320615D1700BDD9FB /* ResettableCursorType.swift */; };
67EB7FD620615D1700BDD9FB /* ResettableCursorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD320615D1700BDD9FB /* ResettableCursorType.swift */; };
67EB7FD720615D1700BDD9FB /* ResettableCursorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD320615D1700BDD9FB /* ResettableCursorType.swift */; };
67EB7FDA20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */; };
67EB7FDB20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */; };
67EB7FDC20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */; };
67EB7FDD20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */; };
67EB7FE420615DE000BDD9FB /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */; };
67EB7FE520615DE000BDD9FB /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */; };
67EB7FE620615DE000BDD9FB /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */; };
67EB7FE720615DE000BDD9FB /* DataSourceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */; };
67EB7FDA20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */; };
67EB7FDB20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */; };
67EB7FDC20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */; };
67EB7FDD20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */; };
67EB7FE420615DE000BDD9FB /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSource.swift */; };
67EB7FE520615DE000BDD9FB /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSource.swift */; };
67EB7FE620615DE000BDD9FB /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSource.swift */; };
67EB7FE720615DE000BDD9FB /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FE320615DE000BDD9FB /* DataSource.swift */; };
67EB7FEB2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FEA2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift */; };
67EB7FEC2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FEA2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift */; };
67EB7FED2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FEA2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift */; };
@ -352,6 +397,13 @@
67EB7FF22061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF02061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift */; };
67EB7FF32061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF02061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift */; };
67EB7FF42061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF02061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift */; };
67EB7FF8206175F700BDD9FB /* PaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FF7206175F700BDD9FB /* PaginationWrappable.swift */; };
67EB7FFD206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FFC206176C900BDD9FB /* AnyPaginationWrappable.swift */; };
67EB7FFE206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FFC206176C900BDD9FB /* AnyPaginationWrappable.swift */; };
67EB7FFF206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB7FFC206176C900BDD9FB /* AnyPaginationWrappable.swift */; };
67EB8001206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB8000206177D600BDD9FB /* PaginationWrapperDelegate.swift */; };
67EB8002206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB8000206177D600BDD9FB /* PaginationWrapperDelegate.swift */; };
67EB8003206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB8000206177D600BDD9FB /* PaginationWrapperDelegate.swift */; };
67FDC25F1FA310EA00C76A77 /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FDC25E1FA310EA00C76A77 /* RequestError.swift */; };
67FDC2601FA310EA00C76A77 /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FDC25E1FA310EA00C76A77 /* RequestError.swift */; };
67FDC2611FA310EA00C76A77 /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FDC25E1FA310EA00C76A77 /* RequestError.swift */; };
@ -456,8 +508,6 @@
671461CC1EB3396E00EAB194 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
671461CD1EB3396E00EAB194 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
671461CE1EB3396E00EAB194 /* LogFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogFormatter.swift; sourceTree = "<group>"; };
671461D01EB3396E00EAB194 /* PaginationTableViewWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationTableViewWrapper.swift; sourceTree = "<group>"; };
671461D11EB3396E00EAB194 /* PaginationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationViewModel.swift; sourceTree = "<group>"; };
671461D31EB3396E00EAB194 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
671461D51EB3396E00EAB194 /* XibView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XibView.swift; sourceTree = "<group>"; };
671461D71EB3396E00EAB194 /* CursorError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CursorError.swift; sourceTree = "<group>"; };
@ -474,7 +524,6 @@
671461E91EB3396E00EAB194 /* CursorType+Slice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CursorType+Slice.swift"; sourceTree = "<group>"; };
671461EB1EB3396E00EAB194 /* Double+Rounding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Rounding.swift"; sourceTree = "<group>"; };
671461F11EB3396E00EAB194 /* Observable+DeferredJust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+DeferredJust.swift"; sourceTree = "<group>"; };
671461F41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PaginationTableViewWrapperDelegate+DefaultImplementation.swift"; sourceTree = "<group>"; };
671461F61EB3396E00EAB194 /* Sequence+ConcurrentMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sequence+ConcurrentMap.swift"; sourceTree = "<group>"; };
671461FC1EB3396E00EAB194 /* String+Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; };
671461FD1EB3396E00EAB194 /* String+SizeCalculation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SizeCalculation.swift"; sourceTree = "<group>"; };
@ -535,6 +584,20 @@
6771DFDD1EE99F6F002DCDAE /* DateFormattingArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingArguments.swift; sourceTree = "<group>"; };
6771DFE31EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormattingArguments+DateFormatter.swift"; sourceTree = "<group>"; };
6771DFE91EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormattingService+MappingTransform.swift"; sourceTree = "<group>"; };
67745267206249360024EEEF /* UITableView+PaginationWrappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+PaginationWrappable.swift"; sourceTree = "<group>"; };
6774526B206249E30024EEEF /* UICollectionView+PaginationWrappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+PaginationWrappable.swift"; sourceTree = "<group>"; };
6774526F20624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaginationWrapperDelegate+DefaultImplementation.swift"; sourceTree = "<group>"; };
6774527320624E820024EEEF /* DataLoadingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoadingModel.swift; sourceTree = "<group>"; };
67745278206252020024EEEF /* DataLoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataLoadingState.swift; sourceTree = "<group>"; };
6774527F206256A20024EEEF /* RxDataLoadingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxDataLoadingModel.swift; sourceTree = "<group>"; };
67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Rx+RxDataSourceProtocol.swift"; sourceTree = "<group>"; };
6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingState.swift; sourceTree = "<group>"; };
6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingModel.swift; sourceTree = "<group>"; };
6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataLoadingState.swift; sourceTree = "<group>"; };
6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataLoadingModel.swift; sourceTree = "<group>"; };
677452A320625FA90024EEEF /* RxDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxDataSource.swift; sourceTree = "<group>"; };
677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CursorType+RxDataSourceDefaultImplementation.swift"; sourceTree = "<group>"; };
677452AD206274630024EEEF /* PaginationWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationWrapper.swift; sourceTree = "<group>"; };
6782BB911EB31CFE0086E0B8 /* LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6782BBA01EB31D590086E0B8 /* LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6782BBA81EB31D5A0086E0B8 /* LeadKit tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LeadKit tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -555,10 +618,13 @@
67EB7FC6206148D000BDD9FB /* TotalCountCursorListingResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalCountCursorListingResult.swift; sourceTree = "<group>"; };
67EB7FCE20615B8900BDD9FB /* TotalCountCursorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalCountCursorConfiguration.swift; sourceTree = "<group>"; };
67EB7FD320615D1700BDD9FB /* ResettableCursorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResettableCursorType.swift; sourceTree = "<group>"; };
67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResettableCursorDataSource.swift; sourceTree = "<group>"; };
67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceProtocol.swift; sourceTree = "<group>"; };
67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResettableRxCursorDataSource.swift; sourceTree = "<group>"; };
67EB7FE320615DE000BDD9FB /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = "<group>"; };
67EB7FEA2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTotalCountCursorListingResult.swift; sourceTree = "<group>"; };
67EB7FF02061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift"; sourceTree = "<group>"; };
67EB7FF7206175F700BDD9FB /* PaginationWrappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationWrappable.swift; sourceTree = "<group>"; };
67EB7FFC206176C900BDD9FB /* AnyPaginationWrappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyPaginationWrappable.swift; sourceTree = "<group>"; };
67EB8000206177D600BDD9FB /* PaginationWrapperDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationWrapperDelegate.swift; sourceTree = "<group>"; };
67FDC25E1FA310EA00C76A77 /* RequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = "<group>"; };
78405D3B3D3C3E17456877FF /* Pods_LeadKit_iOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit_iOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Single+DeferredJust.swift"; sourceTree = "<group>"; };
@ -666,9 +732,8 @@
671461C41EB3396E00EAB194 /* Classes */ = {
isa = PBXGroup;
children = (
671461C71EB3396E00EAB194 /* Cursors */,
6774527E2062566D0024EEEF /* DataLoading */,
671461CB1EB3396E00EAB194 /* Logging */,
671461CF1EB3396E00EAB194 /* Pagination */,
671461D21EB3396E00EAB194 /* Services */,
671461D41EB3396E00EAB194 /* Views */,
);
@ -697,13 +762,13 @@
path = Logging;
sourceTree = "<group>";
};
671461CF1EB3396E00EAB194 /* Pagination */ = {
671461CF1EB3396E00EAB194 /* PaginationDataLoading */ = {
isa = PBXGroup;
children = (
671461D01EB3396E00EAB194 /* PaginationTableViewWrapper.swift */,
671461D11EB3396E00EAB194 /* PaginationViewModel.swift */,
6774529E20625EEE0024EEEF /* PaginationDataLoadingModel.swift */,
677452AD206274630024EEEF /* PaginationWrapper.swift */,
);
path = Pagination;
path = PaginationDataLoading;
sourceTree = "<group>";
};
671461D21EB3396E00EAB194 /* Services */ = {
@ -730,7 +795,7 @@
671461D61EB3396E00EAB194 /* Enums */ = {
isa = PBXGroup;
children = (
671461D71EB3396E00EAB194 /* CursorError.swift */,
6774528A20625C860024EEEF /* DataLoading */,
671461D81EB3396E00EAB194 /* LeadKitError.swift */,
671461D91EB3396E00EAB194 /* ResizeMode.swift */,
67FDC25E1FA310EA00C76A77 /* RequestError.swift */,
@ -749,14 +814,12 @@
671461E21EB3396E00EAB194 /* CGImage */,
671461E51EB3396E00EAB194 /* CGSize */,
A6D10EA91F8A9269003E69DD /* Comparable */,
67EB7FEF2061681A00BDD9FB /* Cursors */,
671461E81EB3396E00EAB194 /* CursorType */,
67745284206259C20024EEEF /* DataLoading */,
6771DFE21EE99FF3002DCDAE /* DateFormattingArguments */,
6771DFE81EEA7C8F002DCDAE /* DateFormattingService */,
671461EA1EB3396E00EAB194 /* Double */,
6714639C1EB33AC200EAB194 /* NetworkService */,
671461F01EB3396E00EAB194 /* Observable */,
671461F31EB3396E00EAB194 /* PaginationTableViewWrapperDelegate */,
671461F51EB3396E00EAB194 /* Sequence */,
82F8BB161F5DDED100C1061B /* Single */,
671461FA1EB3396E00EAB194 /* String */,
@ -822,6 +885,7 @@
isa = PBXGroup;
children = (
671461E91EB3396E00EAB194 /* CursorType+Slice.swift */,
677452A8206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift */,
);
path = CursorType;
sourceTree = "<group>";
@ -842,14 +906,6 @@
path = Observable;
sourceTree = "<group>";
};
671461F31EB3396E00EAB194 /* PaginationTableViewWrapperDelegate */ = {
isa = PBXGroup;
children = (
671461F41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift */,
);
path = PaginationTableViewWrapperDelegate;
sourceTree = "<group>";
};
671461F51EB3396E00EAB194 /* Sequence */ = {
isa = PBXGroup;
children = (
@ -955,15 +1011,14 @@
671462221EB3396E00EAB194 /* Protocols */ = {
isa = PBXGroup;
children = (
67EB7FE820615E5000BDD9FB /* Cursors */,
67EB7FC5206148C400BDD9FB /* Loading */,
671462331EB3396E00EAB194 /* ConfigurableView.swift */,
67EB7FC5206148C400BDD9FB /* DataLoading */,
67EB7FF5206175CD00BDD9FB /* Views */,
679C77D41F98F78E0094BE10 /* AlertRepresentable.swift */,
671463A11EB33FF600EAB194 /* Animatable.swift */,
40F118461F8FEF97004AADAF /* AppearanceConfigurable.swift */,
671462231EB3396E00EAB194 /* BaseViewModel.swift */,
671462241EB3396E00EAB194 /* ConfigurableController.swift */,
671462251EB3396E00EAB194 /* CursorType.swift */,
671462331EB3396E00EAB194 /* ConfigurableView.swift */,
671462261EB3396E00EAB194 /* DrawingOperation.swift */,
671462281EB3396E00EAB194 /* LoadingIndicator.swift */,
671462291EB3396E00EAB194 /* ModuleConfigurator.swift */,
@ -981,7 +1036,7 @@
671462351EB3396E00EAB194 /* Structures */ = {
isa = PBXGroup;
children = (
67EB7FE92061666900BDD9FB /* Cursors */,
677452B620627F9E0024EEEF /* DataLoading */,
671462361EB3396E00EAB194 /* Api */,
6771DFDC1EE99F52002DCDAE /* DateFormatting */,
671462381EB3396E00EAB194 /* DrawingOperations */,
@ -1121,6 +1176,90 @@
path = DateFormattingService;
sourceTree = "<group>";
};
67745266206248F00024EEEF /* PaginationDataLoading */ = {
isa = PBXGroup;
children = (
6774526F20624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift */,
6774526B206249E30024EEEF /* UICollectionView+PaginationWrappable.swift */,
67745267206249360024EEEF /* UITableView+PaginationWrappable.swift */,
);
path = PaginationDataLoading;
sourceTree = "<group>";
};
6774527E2062566D0024EEEF /* DataLoading */ = {
isa = PBXGroup;
children = (
671461C71EB3396E00EAB194 /* Cursors */,
6774529620625DA80024EEEF /* GeneralDataLoading */,
671461CF1EB3396E00EAB194 /* PaginationDataLoading */,
6774527F206256A20024EEEF /* RxDataLoadingModel.swift */,
);
path = DataLoading;
sourceTree = "<group>";
};
67745284206259C20024EEEF /* DataLoading */ = {
isa = PBXGroup;
children = (
67EB7FEF2061681A00BDD9FB /* Cursors */,
671461E81EB3396E00EAB194 /* CursorType */,
67745266206248F00024EEEF /* PaginationDataLoading */,
67745285206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift */,
);
path = DataLoading;
sourceTree = "<group>";
};
6774528A20625C860024EEEF /* DataLoading */ = {
isa = PBXGroup;
children = (
6774528B20625C8D0024EEEF /* GeneralDataLoading */,
6774529820625E290024EEEF /* PaginationDataLoading */,
671461D71EB3396E00EAB194 /* CursorError.swift */,
);
path = DataLoading;
sourceTree = "<group>";
};
6774528B20625C8D0024EEEF /* GeneralDataLoading */ = {
isa = PBXGroup;
children = (
6774528C20625C9E0024EEEF /* GeneralDataLoadingState.swift */,
);
path = GeneralDataLoading;
sourceTree = "<group>";
};
6774529620625DA80024EEEF /* GeneralDataLoading */ = {
isa = PBXGroup;
children = (
6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */,
);
path = GeneralDataLoading;
sourceTree = "<group>";
};
6774529820625E290024EEEF /* PaginationDataLoading */ = {
isa = PBXGroup;
children = (
6774529920625E5B0024EEEF /* PaginationDataLoadingState.swift */,
);
path = PaginationDataLoading;
sourceTree = "<group>";
};
677452B620627F9E0024EEEF /* DataLoading */ = {
isa = PBXGroup;
children = (
67EB7FE92061666900BDD9FB /* Cursors */,
677452BA2062812C0024EEEF /* PaginationDataLoading */,
);
path = DataLoading;
sourceTree = "<group>";
};
677452BA2062812C0024EEEF /* PaginationDataLoading */ = {
isa = PBXGroup;
children = (
67EB7FFC206176C900BDD9FB /* AnyPaginationWrappable.swift */,
67EB8000206177D600BDD9FB /* PaginationWrapperDelegate.swift */,
);
path = PaginationDataLoading;
sourceTree = "<group>";
};
679C77D51F98F7A60094BE10 /* UIAlertController */ = {
isa = PBXGroup;
children = (
@ -1137,29 +1276,35 @@
path = CABasicAnimation;
sourceTree = "<group>";
};
67EB7FC5206148C400BDD9FB /* Loading */ = {
67EB7FC5206148C400BDD9FB /* DataLoading */ = {
isa = PBXGroup;
children = (
67EB7FD820615D3100BDD9FB /* Pagination */,
67EB7FE320615DE000BDD9FB /* DataSourceProtocol.swift */,
67EB7FE820615E5000BDD9FB /* Cursors */,
67EB7FD820615D3100BDD9FB /* PaginationDataLoading */,
67EB7FE320615DE000BDD9FB /* DataSource.swift */,
6774527320624E820024EEEF /* DataLoadingModel.swift */,
67745278206252020024EEEF /* DataLoadingState.swift */,
677452A320625FA90024EEEF /* RxDataSource.swift */,
);
path = Loading;
path = DataLoading;
sourceTree = "<group>";
};
67EB7FD820615D3100BDD9FB /* Pagination */ = {
67EB7FD820615D3100BDD9FB /* PaginationDataLoading */ = {
isa = PBXGroup;
children = (
67EB7FF7206175F700BDD9FB /* PaginationWrappable.swift */,
67EB7FCE20615B8900BDD9FB /* TotalCountCursorConfiguration.swift */,
67EB7FC6206148D000BDD9FB /* TotalCountCursorListingResult.swift */,
);
path = Pagination;
path = PaginationDataLoading;
sourceTree = "<group>";
};
67EB7FE820615E5000BDD9FB /* Cursors */ = {
isa = PBXGroup;
children = (
67EB7FD920615D5B00BDD9FB /* ResettableCursorDataSource.swift */,
671462251EB3396E00EAB194 /* CursorType.swift */,
67EB7FD320615D1700BDD9FB /* ResettableCursorType.swift */,
67EB7FD920615D5B00BDD9FB /* ResettableRxCursorDataSource.swift */,
);
path = Cursors;
sourceTree = "<group>";
@ -1180,6 +1325,13 @@
path = Cursors;
sourceTree = "<group>";
};
67EB7FF5206175CD00BDD9FB /* Views */ = {
isa = PBXGroup;
children = (
);
path = Views;
sourceTree = "<group>";
};
78CFEE201C5C456B00F50370 = {
isa = PBXGroup;
children = (
@ -2082,8 +2234,11 @@
EFBE57D01EC35EF20040E00A /* Array+Extensions.swift in Sources */,
671462E41EB3396E00EAB194 /* UIColor+Hex.swift in Sources */,
67EB7FF12061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */,
67EB8001206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */,
671462CC1EB3396E00EAB194 /* String+SizeCalculation.swift in Sources */,
6774529A20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
671462801EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
67EB7FF8206175F700BDD9FB /* PaginationWrappable.swift in Sources */,
671463541EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
671463601EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
671462841EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
@ -2097,7 +2252,6 @@
671463101EB3396E00EAB194 /* UIViewController+DefaultXibName.swift in Sources */,
674AF55C1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift in Sources */,
671463401EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462641EB3396E00EAB194 /* PaginationViewModel.swift in Sources */,
67A1FF8F1EBCA09B00D6C89F /* UIImage+Spinner.swift in Sources */,
671462901EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
671462FC1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */,
@ -2105,6 +2259,7 @@
36DAAF512007CC920090BE0D /* UITableView+Extensions.swift in Sources */,
671463841EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
6771DFD81EE99EBA002DCDAE /* DateFormattingService.swift in Sources */,
6774528D20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */,
671462D01EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */,
671463901EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */,
A658E54D1F8CD7790093527A /* TableRow+SeparatorsExtensions.swift in Sources */,
@ -2115,13 +2270,13 @@
82F8BB181F5DDED100C1061B /* Single+DeferredJust.swift in Sources */,
671463301EB3396E00EAB194 /* CursorType.swift in Sources */,
67FDC25F1FA310EA00C76A77 /* RequestError.swift in Sources */,
67745268206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */,
6714624C1EB3396E00EAB194 /* MapCursor.swift in Sources */,
A6C9A4FA1F8BBCF2009311CC /* EmptyCell.swift in Sources */,
671463241EB3396E00EAB194 /* Any+TypeName.swift in Sources */,
671463881EB3396E00EAB194 /* RoundDrawingOperation.swift in Sources */,
A6D10EAB1F8A9278003E69DD /* Comparable+Extensions.swift in Sources */,
671463801EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */,
671462601EB3396E00EAB194 /* PaginationTableViewWrapper.swift in Sources */,
671463281EB3396E00EAB194 /* BaseViewModel.swift in Sources */,
A6E0DDDF1F8A696F002CA74E /* SeparatorCell.swift in Sources */,
671462AC1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */,
@ -2130,16 +2285,18 @@
6714627C1EB3396E00EAB194 /* AlamofireManager+Extensions.swift in Sources */,
671462D41EB3396E00EAB194 /* TableDirector+Extensions.swift in Sources */,
671462581EB3396E00EAB194 /* Log.swift in Sources */,
671462B41EB3396E00EAB194 /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift in Sources */,
671462781EB3396E00EAB194 /* ResizeMode.swift in Sources */,
A676AE551F98112E001F9214 /* ObservableMappable.swift in Sources */,
A6E0DDE11F8A696F002CA74E /* SeparatorRowBox.swift in Sources */,
A6E0DDDE1F8A696F002CA74E /* EmptyCellRow.swift in Sources */,
67EB7FFD206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */,
671463041EB3396E00EAB194 /* UIView+LoadingIndicator.swift in Sources */,
6774527420624E820024EEEF /* DataLoadingModel.swift in Sources */,
40F118491F8FF223004AADAF /* TableRow+AppearanceExtension.swift in Sources */,
671463701EB3396E00EAB194 /* ApiRequestParameters.swift in Sources */,
A658E5501F8CD9350093527A /* Array+SeparatorRowBoxExtensions.swift in Sources */,
671462EC1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */,
677452AE206274630024EEEF /* PaginationWrapper.swift in Sources */,
A6E0DDF11F8A6C80002CA74E /* SeparatorConfiguration.swift in Sources */,
67EB7FEB2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */,
6714636C1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */,
@ -2148,17 +2305,21 @@
6714625C1EB3396E00EAB194 /* LogFormatter.swift in Sources */,
671463081EB3396E00EAB194 /* UIView+Rotation.swift in Sources */,
6714626C1EB3396E00EAB194 /* XibView.swift in Sources */,
6774529220625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
6714637C1EB3396E00EAB194 /* ImageDrawingOperation.swift in Sources */,
671463341EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
671462701EB3396E00EAB194 /* CursorError.swift in Sources */,
671463981EB3396E00EAB194 /* AnyLoadingIndicator.swift in Sources */,
671463A71EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */,
671463141EB3396E00EAB194 /* UIViewController+TopVisibleViewController.swift in Sources */,
6774527020624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */,
67745280206256A20024EEEF /* RxDataLoadingModel.swift in Sources */,
A6F32C081F6EBDAA00AC08EE /* String+LocalizedComponent.swift in Sources */,
671462881EB3396E00EAB194 /* CGFloat+Pixels.swift in Sources */,
671462941EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */,
67745279206252020024EEEF /* DataLoadingState.swift in Sources */,
671463641EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
67EB7FDA20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */,
67EB7FDA20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
671462481EB3396E00EAB194 /* FixedPageCursor.swift in Sources */,
671462C81EB3396E00EAB194 /* String+Localization.swift in Sources */,
671462B81EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */,
@ -2173,14 +2334,19 @@
6714632C1EB3396E00EAB194 /* ConfigurableController.swift in Sources */,
6714628C1EB3396E00EAB194 /* CGImage+Alpha.swift in Sources */,
671462741EB3396E00EAB194 /* LeadKitError.swift in Sources */,
677452A420625FA90024EEEF /* RxDataSource.swift in Sources */,
671462D81EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */,
6714638C1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */,
6774526C206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */,
671463A21EB33FF600EAB194 /* Animatable.swift in Sources */,
677452A9206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
671462501EB3396E00EAB194 /* StaticCursor.swift in Sources */,
67EB7FC7206148D000BDD9FB /* TotalCountCursorListingResult.swift in Sources */,
67EB7FE420615DE000BDD9FB /* DataSourceProtocol.swift in Sources */,
67745286206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */,
67EB7FE420615DE000BDD9FB /* DataSource.swift in Sources */,
67EB7FD420615D1700BDD9FB /* ResettableCursorType.swift in Sources */,
6714629C1EB3396E00EAB194 /* CursorType+Slice.swift in Sources */,
6774529F20625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
671463681EB3396E00EAB194 /* ConfigurableView.swift in Sources */,
6771DFE41EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift in Sources */,
);
@ -2211,7 +2377,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67EB7FE620615DE000BDD9FB /* DataSourceProtocol.swift in Sources */,
67EB7FE620615DE000BDD9FB /* DataSource.swift in Sources */,
6714634A1EB3396E00EAB194 /* ResettableType.swift in Sources */,
671462E61EB3396E00EAB194 /* UIColor+Hex.swift in Sources */,
671462CE1EB3396E00EAB194 /* String+SizeCalculation.swift in Sources */,
@ -2219,31 +2385,36 @@
EFBE57D21EC35EF20040E00A /* Array+Extensions.swift in Sources */,
67EB7FC2206140E600BDD9FB /* TotalCountCursor.swift in Sources */,
671462821EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
67745288206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */,
67E6C2371EBB32F5007842A6 /* SingleLoadCursor.swift in Sources */,
671463561EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
6771DFE01EE99F6F002DCDAE /* DateFormattingArguments.swift in Sources */,
671463621EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
6771DFEC1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */,
671462861EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
6774527B206252020024EEEF /* DataLoadingState.swift in Sources */,
6714634E1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
6714626A1EB3396E00EAB194 /* NetworkService.swift in Sources */,
671463421EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462661EB3396E00EAB194 /* PaginationViewModel.swift in Sources */,
671462921EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
671463861EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
671463921EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */,
67745282206256A20024EEEF /* RxDataLoadingModel.swift in Sources */,
6714629A1EB3396E00EAB194 /* CGSize+Resize.swift in Sources */,
671463321EB3396E00EAB194 /* CursorType.swift in Sources */,
6714624E1EB3396E00EAB194 /* MapCursor.swift in Sources */,
A676AE571F981130001F9214 /* ObservableMappable.swift in Sources */,
6774528F20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */,
671463261EB3396E00EAB194 /* Any+TypeName.swift in Sources */,
6714638A1EB3396E00EAB194 /* RoundDrawingOperation.swift in Sources */,
6774529C20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
671463821EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */,
6714632A1EB3396E00EAB194 /* BaseViewModel.swift in Sources */,
671462AE1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */,
67EB7FDC20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */,
67EB7FDC20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
A6F32C0B1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */,
6714627E1EB3396E00EAB194 /* AlamofireManager+Extensions.swift in Sources */,
677452AB206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
6771DFDA1EE99EBA002DCDAE /* DateFormattingService.swift in Sources */,
67EB7FD620615D1700BDD9FB /* ResettableCursorType.swift in Sources */,
6714625A1EB3396E00EAB194 /* Log.swift in Sources */,
@ -2264,17 +2435,21 @@
671463661EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
67EB7FD120615B8900BDD9FB /* TotalCountCursorConfiguration.swift in Sources */,
67EB7FC9206148D000BDD9FB /* TotalCountCursorListingResult.swift in Sources */,
6774527620624E820024EEEF /* DataLoadingModel.swift in Sources */,
6771DFE61EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift in Sources */,
6714624A1EB3396E00EAB194 /* FixedPageCursor.swift in Sources */,
671462CA1EB3396E00EAB194 /* String+Localization.swift in Sources */,
677452A620625FA90024EEEF /* RxDataSource.swift in Sources */,
671462BA1EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */,
671463761EB3396E00EAB194 /* BorderDrawingOperation.swift in Sources */,
677452A120625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
671462561EB3396E00EAB194 /* App.swift in Sources */,
675C1FB51F97CA33007D5249 /* AppearanceConfigurable.swift in Sources */,
6714628E1EB3396E00EAB194 /* CGImage+Alpha.swift in Sources */,
671462761EB3396E00EAB194 /* LeadKitError.swift in Sources */,
671462DA1EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */,
6714638E1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */,
6774529420625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
67FDC2611FA310EA00C76A77 /* RequestError.swift in Sources */,
671462521EB3396E00EAB194 /* StaticCursor.swift in Sources */,
6714629E1EB3396E00EAB194 /* CursorType+Slice.swift in Sources */,
@ -2290,6 +2465,9 @@
671462E71EB3396E00EAB194 /* UIColor+Hex.swift in Sources */,
671462CF1EB3396E00EAB194 /* String+SizeCalculation.swift in Sources */,
671462831EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
677452B820627FE00024EEEF /* PaginationWrappable.swift in Sources */,
67745283206256A20024EEEF /* RxDataLoadingModel.swift in Sources */,
67EB8003206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */,
671463571EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
671463631EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
671462871EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
@ -2299,9 +2477,8 @@
6714626B1EB3396E00EAB194 /* NetworkService.swift in Sources */,
671463131EB3396E00EAB194 /* UIViewController+DefaultXibName.swift in Sources */,
671463431EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462671EB3396E00EAB194 /* PaginationViewModel.swift in Sources */,
671462931EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
67EB7FE720615DE000BDD9FB /* DataSourceProtocol.swift in Sources */,
67EB7FE720615DE000BDD9FB /* DataSource.swift in Sources */,
671462FF1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */,
67FDC2621FA310EA00C76A77 /* RequestError.swift in Sources */,
671463871EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
@ -2315,9 +2492,13 @@
67EB7FD220615B8900BDD9FB /* TotalCountCursorConfiguration.swift in Sources */,
EFBE57D31EC35EF20040E00A /* Array+Extensions.swift in Sources */,
6714638B1EB3396E00EAB194 /* RoundDrawingOperation.swift in Sources */,
67EB7FFF206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */,
A676AE4A1F97D28A001F9214 /* String+Extensions.swift in Sources */,
671463831EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */,
6774527C206252020024EEEF /* DataLoadingState.swift in Sources */,
677452AC206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
6714632B1EB3396E00EAB194 /* BaseViewModel.swift in Sources */,
677452A220625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
A6F32C0C1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */,
671462AF1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */,
A676AE4F1F9810C1001F9214 /* Any+Cast.swift in Sources */,
@ -2326,12 +2507,15 @@
6771DFDB1EE99EBA002DCDAE /* DateFormattingService.swift in Sources */,
6714625B1EB3396E00EAB194 /* Log.swift in Sources */,
6714627B1EB3396E00EAB194 /* ResizeMode.swift in Sources */,
67745289206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */,
671463071EB3396E00EAB194 /* UIView+LoadingIndicator.swift in Sources */,
6774526E206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */,
6771DFED1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */,
671463A91EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */,
A676AE581F981131001F9214 /* ObservableMappable.swift in Sources */,
671463731EB3396E00EAB194 /* ApiRequestParameters.swift in Sources */,
671462EF1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */,
6774526A206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */,
6714636F1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */,
EFBE57DE1EC361620040E00A /* UIView+Layout.swift in Sources */,
67A1FF971EBCA65E00D6C89F /* CABasicAnimation+Rotation.swift in Sources */,
@ -2343,7 +2527,9 @@
67EB7FD720615D1700BDD9FB /* ResettableCursorType.swift in Sources */,
671463371EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
671462731EB3396E00EAB194 /* CursorError.swift in Sources */,
67EB7FDD20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */,
67EB7FDD20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
677452A720625FA90024EEEF /* RxDataSource.swift in Sources */,
6774527720624E820024EEEF /* DataLoadingModel.swift in Sources */,
6714639B1EB3396E00EAB194 /* AnyLoadingIndicator.swift in Sources */,
6771DFE11EE99F6F002DCDAE /* DateFormattingArguments.swift in Sources */,
671463171EB3396E00EAB194 /* UIViewController+TopVisibleViewController.swift in Sources */,
@ -2363,12 +2549,15 @@
6714631B1EB3396E00EAB194 /* UIWindow+Extensions.swift in Sources */,
671462571EB3396E00EAB194 /* App.swift in Sources */,
6714637B1EB3396E00EAB194 /* CALayerDrawingOperation.swift in Sources */,
6774529520625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
6774529D20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
6714632F1EB3396E00EAB194 /* ConfigurableController.swift in Sources */,
67EB7FF42061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */,
6714628F1EB3396E00EAB194 /* CGImage+Alpha.swift in Sources */,
67051ADD1EBC7C36008EADC0 /* SpinnerView.swift in Sources */,
671462771EB3396E00EAB194 /* LeadKitError.swift in Sources */,
671462DB1EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */,
6774529020625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */,
67EB7FC3206140E600BDD9FB /* TotalCountCursor.swift in Sources */,
6714638F1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */,
67E6C2381EBB32F5007842A6 /* SingleLoadCursor.swift in Sources */,
@ -2396,11 +2585,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67EB7FE520615DE000BDD9FB /* DataSourceProtocol.swift in Sources */,
67EB7FE520615DE000BDD9FB /* DataSource.swift in Sources */,
671463491EB3396E00EAB194 /* ResettableType.swift in Sources */,
A676AE491F97D28A001F9214 /* String+Extensions.swift in Sources */,
671462E51EB3396E00EAB194 /* UIColor+Hex.swift in Sources */,
67EB7FFE206176C900BDD9FB /* AnyPaginationWrappable.swift in Sources */,
671462CD1EB3396E00EAB194 /* String+SizeCalculation.swift in Sources */,
677452B720627FE00024EEEF /* PaginationWrappable.swift in Sources */,
67EB7FC1206140E600BDD9FB /* TotalCountCursor.swift in Sources */,
6771DFDF1EE99F6F002DCDAE /* DateFormattingArguments.swift in Sources */,
6771DFEB1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */,
@ -2411,16 +2602,17 @@
671463611EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
671462851EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
6714634D1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
67745281206256A20024EEEF /* RxDataLoadingModel.swift in Sources */,
671462F11EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */,
671462691EB3396E00EAB194 /* NetworkService.swift in Sources */,
671463111EB3396E00EAB194 /* UIViewController+DefaultXibName.swift in Sources */,
671463411EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462651EB3396E00EAB194 /* PaginationViewModel.swift in Sources */,
671462911EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
67051ADC1EBC7C36008EADC0 /* SpinnerView.swift in Sources */,
671462FD1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */,
671463851EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
671462D11EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */,
6774528E20625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */,
671463911EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */,
67EB7FEC2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */,
671462991EB3396E00EAB194 /* CGSize+Resize.swift in Sources */,
@ -2431,13 +2623,17 @@
671463891EB3396E00EAB194 /* RoundDrawingOperation.swift in Sources */,
675C1FB61F97CA33007D5249 /* AppearanceConfigurable.swift in Sources */,
671463811EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */,
67EB7FDB20615D5B00BDD9FB /* ResettableCursorDataSource.swift in Sources */,
67EB7FDB20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
A6F32C0A1F6EBE5B00AC08EE /* String+LocalizedComponent.swift in Sources */,
671463291EB3396E00EAB194 /* BaseViewModel.swift in Sources */,
671462AD1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */,
671463011EB3396E00EAB194 /* UIView+LoadFromNib.swift in Sources */,
6774526D206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */,
6714627D1EB3396E00EAB194 /* AlamofireManager+Extensions.swift in Sources */,
671462591EB3396E00EAB194 /* Log.swift in Sources */,
6774527120624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */,
67EB8002206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */,
677452AA206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
671462791EB3396E00EAB194 /* ResizeMode.swift in Sources */,
671463051EB3396E00EAB194 /* UIView+LoadingIndicator.swift in Sources */,
671463711EB3396E00EAB194 /* ApiRequestParameters.swift in Sources */,
@ -2449,7 +2645,9 @@
6714626D1EB3396E00EAB194 /* XibView.swift in Sources */,
6714637D1EB3396E00EAB194 /* ImageDrawingOperation.swift in Sources */,
671463351EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
677452A020625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
67FDC2601FA310EA00C76A77 /* RequestError.swift in Sources */,
6774529320625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
671462711EB3396E00EAB194 /* CursorError.swift in Sources */,
671463991EB3396E00EAB194 /* AnyLoadingIndicator.swift in Sources */,
671463A81EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */,
@ -2461,7 +2659,9 @@
671462951EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */,
67EB7FF22061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */,
671463651EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
6774527520624E820024EEEF /* DataLoadingModel.swift in Sources */,
A6C9A5101F8BC79D009311CC /* Comparable+Extensions.swift in Sources */,
677452A520625FA90024EEEF /* RxDataSource.swift in Sources */,
671462491EB3396E00EAB194 /* FixedPageCursor.swift in Sources */,
671462C91EB3396E00EAB194 /* String+Localization.swift in Sources */,
671462B91EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */,
@ -2471,10 +2671,14 @@
EFBE57DC1EC361620040E00A /* UIView+Layout.swift in Sources */,
671462551EB3396E00EAB194 /* App.swift in Sources */,
67A1FF901EBCA09B00D6C89F /* UIImage+Spinner.swift in Sources */,
6774529B20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
671463791EB3396E00EAB194 /* CALayerDrawingOperation.swift in Sources */,
6714632D1EB3396E00EAB194 /* ConfigurableController.swift in Sources */,
67745287206259CF0024EEEF /* Rx+RxDataSourceProtocol.swift in Sources */,
A676AE561F98112F001F9214 /* ObservableMappable.swift in Sources */,
6774527A206252020024EEEF /* DataLoadingState.swift in Sources */,
6714628D1EB3396E00EAB194 /* CGImage+Alpha.swift in Sources */,
67745269206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */,
67EB7FC8206148D000BDD9FB /* TotalCountCursorListingResult.swift in Sources */,
671462751EB3396E00EAB194 /* LeadKitError.swift in Sources */,
EFBE57D11EC35EF20040E00A /* Array+Extensions.swift in Sources */,

View File

@ -23,7 +23,9 @@
import RxSwift
/// Paging cursor implementation with enclosed cursor for fetching results
public class FixedPageCursor<Cursor: CursorType>: CursorType {
public class FixedPageCursor<Cursor: CursorType>: CursorType, RxDataSource {
public typealias ResultType = [Element]
fileprivate let cursor: Cursor
@ -76,6 +78,8 @@ public class FixedPageCursor<Cursor: CursorType>: CursorType {
/// FixedPageCursor subclass with implementation of ResettableType
public class ResettableFixedPageCursor<Cursor: ResettableCursorType>: FixedPageCursor<Cursor>, ResettableType {
public typealias ResultType = [Element]
public override init(cursor: Cursor, pageSize: Int) {
super.init(cursor: cursor, pageSize: pageSize)
}

View File

@ -45,7 +45,9 @@ public extension CursorType {
}
/// Map cursor implementation with enclosed cursor for fetching results
public class MapCursor<Cursor: CursorType, T>: CursorType {
public class MapCursor<Cursor: CursorType, T>: CursorType, RxDataSource {
public typealias ResultType = [Element]
public typealias Transform = (Cursor.Element) -> T?
@ -91,6 +93,8 @@ public class MapCursor<Cursor: CursorType, T>: CursorType {
/// MapCursor subclass with implementation of ResettableType
public class ResettableMapCursor<Cursor: ResettableCursorType, T>: MapCursor<Cursor, T>, ResettableType {
public typealias ResultType = [Cursor.Element]
public override init(cursor: Cursor, transform: @escaping Transform) {
super.init(cursor: cursor, transform: transform)
}

View File

@ -23,7 +23,9 @@
import RxSwift
/// Stub cursor implementation for array content type
public class StaticCursor<Element>: ResettableCursorType {
public class StaticCursor<Element>: ResettableRxDataSourceCursor {
public typealias ResultType = [Element]
private let content: [Element]

View File

@ -23,19 +23,17 @@
import RxSwift
import RxCocoa
public final class TotalCountCursor<CC: TotalCountCursorConfiguration>: ResettableCursorDataSource {
public final class TotalCountCursor<CursorConfiguration: TotalCountCursorConfiguration>: ResettableRxDataSourceCursor {
public typealias Element = CC.ListingType.ElementType
public typealias ResultType = [CC.ListingType.ElementType]
public typealias Element = CursorConfiguration.ResultType.ElementType
public typealias ResultType = [Element]
private let configuration: CC
private let configuration: CursorConfiguration
private var elements: [Element] = []
public private(set) var totalCount: Int = .max
private let disposeBag = DisposeBag()
public var exhausted: Bool {
return count >= totalCount
}
@ -48,16 +46,16 @@ public final class TotalCountCursor<CC: TotalCountCursorConfiguration>: Resettab
return elements[index]
}
public init(configuration: CC) {
public init(configuration: CursorConfiguration) {
self.configuration = configuration
}
public required init(resetFrom other: TotalCountCursor) {
self.configuration = other.configuration.reset()
configuration = other.configuration.reset()
}
public func loadNextBatch() -> Single<[Element]> {
return configuration.nextBatchObservable()
return configuration.resultSingle()
.do(onSuccess: { [weak self] listingResult in
self?.totalCount = listingResult.totalCount
self?.elements = (self?.elements ?? []) + listingResult.results

View File

@ -0,0 +1,26 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
public final class GeneralDataLoadingModel<T>: RxDataLoadingModel<GeneralDataLoadingState<Single<T>>> {
}

View File

@ -0,0 +1,105 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
public final class PaginationDataLoadingModel<Cursor: ResettableRxDataSourceCursor>:
RxDataLoadingModel<PaginationDataLoadingState<Cursor>> {
private enum LoadType {
case reload
case retry
case next
}
// Additional init with closure typealias fixes swift
// runtime crash inside emptyResultChecker.
public typealias PaginationEmptyResultChecker = (Cursor.ResultType) -> Bool
public override init(dataSource: Cursor, emptyResultChecker: @escaping PaginationEmptyResultChecker) {
super.init(dataSource: dataSource, emptyResultChecker: emptyResultChecker)
}
override open func reload() {
load(.reload)
}
override open func retry() {
load(.retry)
}
public func loadMore() {
load(.next)
}
private func load(_ loadType: LoadType) {
switch loadType {
case .reload, .retry:
dataSource = dataSource.reset()
if loadType == .retry {
state = .initial
}
state = .initialLoading(after: state)
case .next:
if case .exhausted = state {
fatalError("You shouldn't call load(.next) after got .exhausted state!")
}
state = .loadingMore(after: state)
}
requestResult(from: dataSource)
}
override func onGot(error: Error) {
if case .exhausted? = error as? CursorError, case .initialLoading(let after) = state {
switch after {
case .initial, .empty: // cursor exhausted after creation
state = .empty
default:
super.onGot(error: error)
}
} else {
super.onGot(error: error)
}
}
override func updateStateAfterNonEmptyResult(from dataSource: DataSourceType) {
if dataSource.exhausted {
state = .exhausted
}
}
}
extension PaginationDataLoadingModel {
convenience init(cursor: Cursor) {
self.init(dataSource: cursor) { $0.isEmpty }
}
}

View File

@ -0,0 +1,371 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
import UIScrollView_InfiniteScroll
/// Class that connects PaginationDataLoadingModel with UIScrollView. It handles all non-visual and visual states.
final public class PaginationWrapper<Cursor: ResettableRxDataSourceCursor, Delegate: PaginationWrapperDelegate>
// "Segmentation fault: 11" in Xcode 9.2 without redundant same-type constraint :(
where Cursor == Delegate.DataSourceType, Cursor.ResultType == [Cursor.Element] {
private typealias DataLoadingModel = PaginationDataLoadingModel<Cursor>
private typealias LoadingState = DataLoadingModel.LoadingStateType
private var wrappedView: AnyPaginationWrappable
private let paginationViewModel: DataLoadingModel
private weak var delegate: Delegate?
/// Sets the offset between the real end of the scroll view content and the scroll position,
/// so the handler can be triggered before reaching end. Defaults to 0.0;
public var infiniteScrollTriggerOffset: CGFloat {
get {
return wrappedView.scrollView.infiniteScrollTriggerOffset
}
set {
wrappedView.scrollView.infiniteScrollTriggerOffset = newValue
}
}
public var pullToRefreshEnabled: Bool = true {
didSet {
if pullToRefreshEnabled {
createRefreshControl()
} else {
removeRefreshControl()
}
}
}
private let disposeBag = DisposeBag()
private var currentPlaceholderView: UIView?
private var currentPlaceholderViewTopConstraint: NSLayoutConstraint?
private let applicationCurrentyActive = Variable<Bool>(true)
/// Initializer with table view, placeholders container view, cusor and delegate parameters.
///
/// - Parameters:
/// - wrappedView: UIScrollView instance to work with.
/// - cursor: Cursor object that acts as data source.
/// - delegate: Delegate object for data loading events handling and UI customization.
public init(wrappedView: AnyPaginationWrappable, cursor: Cursor, delegate: Delegate) {
self.wrappedView = wrappedView
self.delegate = delegate
self.paginationViewModel = PaginationDataLoadingModel(cursor: cursor)
bindViewModelStates()
createRefreshControl()
bindAppStateNotifications()
}
/// Method that reload all data in internal view model.
public func reload() {
paginationViewModel.reload()
}
/// Method acts like reload, but shows initial loading view after being invoked.
public func retry() {
paginationViewModel.retry()
}
/// Method that enables placeholders animation due pull-to-refresh interaction.
///
/// - Parameter scrollObservable: Observable that emits content offset as CGPoint.
public func setScrollObservable(_ scrollObservable: Observable<CGPoint>) {
scrollObservable
.asDriver(onErrorJustReturn: .zero)
.drive(scrollOffsetChanged)
.disposed(by: disposeBag)
}
// MARK: - States handling
private func onInitialState() {
//
}
private func onLoadingState(afterState: LoadingState) {
if case .initial = afterState {
wrappedView.scrollView.isUserInteractionEnabled = false
removeCurrentPlaceholderView()
guard let loadingIndicator = delegate?.initialLoadingIndicator() else {
return
}
let loadingIndicatorView = loadingIndicator.view
loadingIndicatorView.translatesAutoresizingMaskIntoConstraints = true
wrappedView.backgroundView = loadingIndicatorView
loadingIndicator.startAnimating()
currentPlaceholderView = loadingIndicatorView
} else {
removeInfiniteScroll()
wrappedView.footerView = nil
}
}
private func onLoadingMoreState(afterState: LoadingState) {
if case .error = afterState { // user tap retry button in table footer
delegate?.footerRetryButtonWillDisappear()
wrappedView.footerView = nil
addInfiniteScroll(withHandler: false)
wrappedView.scrollView.beginInfiniteScroll(true)
}
}
private func onResultsState(newItems: DataLoadingModel.ResultType,
from cursor: Cursor,
afterState: LoadingState) {
wrappedView.scrollView.isUserInteractionEnabled = true
if case .initialLoading = afterState {
delegate?.paginationWrapper(didReload: newItems, using: cursor)
removeCurrentPlaceholderView()
wrappedView.scrollView.support.refreshControl?.endRefreshing()
addInfiniteScroll(withHandler: true)
} else if case .loadingMore = afterState {
delegate?.paginationWrapper(didLoad: newItems, using: cursor)
readdInfiniteScrollWithHandler()
}
}
private func onErrorState(error: Error, afterState: LoadingState) {
if case .initialLoading = afterState {
defer {
wrappedView.scrollView.support.refreshControl?.endRefreshing()
}
let customErrorHandling = delegate?.customInitialLoadingErrorHandling(for: error) ?? false
guard !customErrorHandling, let errorView = delegate?.errorPlaceholder(for: error) else {
return
}
replacePlaceholderViewIfNeeded(with: errorView)
delegate?.clearView()
} else if case .loadingMore = afterState {
removeInfiniteScroll()
guard let retryButton = delegate?.footerRetryButton(),
let retryButtonHeigth = delegate?.footerRetryButtonHeight() else {
return
}
retryButton.frame = CGRect(x: 0, y: 0, width: wrappedView.scrollView.bounds.width, height: retryButtonHeigth)
retryButton.rx
.controlEvent(.touchUpInside)
.asDriver()
.drive(reloadEvent)
.disposed(by: disposeBag)
delegate?.footerRetryButtonWillAppear()
wrappedView.footerView = retryButton
}
}
private func onEmptyState() {
defer {
wrappedView.scrollView.support.refreshControl?.endRefreshing()
}
delegate?.clearView()
guard let emptyView = delegate?.emptyPlaceholder() else {
return
}
replacePlaceholderViewIfNeeded(with: emptyView)
}
private func replacePlaceholderViewIfNeeded(with placeholderView: UIView) {
wrappedView.scrollView.isUserInteractionEnabled = true
removeCurrentPlaceholderView()
placeholderView.translatesAutoresizingMaskIntoConstraints = false
placeholderView.isHidden = false
// I was unable to add pull-to-refresh placeholder scroll behaviour without this trick
let placeholderWrapperView = UIView()
placeholderWrapperView.addSubview(placeholderView)
let leadingConstraint = placeholderView.leadingAnchor.constraint(equalTo: placeholderWrapperView.leadingAnchor)
let trailingConstraint = placeholderView.trailingAnchor.constraint(equalTo: placeholderWrapperView.trailingAnchor)
let topConstraint = placeholderView.topAnchor.constraint(equalTo: placeholderWrapperView.topAnchor)
let bottomConstraint = placeholderView.bottomAnchor.constraint(equalTo: placeholderWrapperView.bottomAnchor)
NSLayoutConstraint.activate([leadingConstraint,
trailingConstraint,
topConstraint,
bottomConstraint])
currentPlaceholderViewTopConstraint = topConstraint
wrappedView.backgroundView = placeholderWrapperView
currentPlaceholderView = placeholderView
}
// MARK: - private stuff
private func onExhaustedState() {
removeInfiniteScroll()
}
private func readdInfiniteScrollWithHandler() {
removeInfiniteScroll()
addInfiniteScroll(withHandler: true)
}
private func addInfiniteScroll(withHandler: Bool) {
if withHandler {
wrappedView.scrollView.addInfiniteScroll { [weak paginationViewModel] _ in
paginationViewModel?.loadMore()
}
} else {
wrappedView.scrollView.addInfiniteScroll { _ in }
}
wrappedView.scrollView.infiniteScrollIndicatorView = delegate?.loadingMoreIndicator().view
}
private func removeInfiniteScroll() {
wrappedView.scrollView.finishInfiniteScroll()
wrappedView.scrollView.removeInfiniteScroll()
}
private func createRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.rx
.controlEvent(.valueChanged)
.asDriver()
.drive(reloadEvent)
.disposed(by: disposeBag)
wrappedView.scrollView.support.setRefreshControl(refreshControl)
}
private func removeRefreshControl() {
wrappedView.scrollView.support.setRefreshControl(nil)
}
private func bindViewModelStates() {
paginationViewModel.stateDriver
.flatMap { [applicationCurrentyActive] state -> Driver<LoadingState> in
if applicationCurrentyActive.value {
return .just(state)
} else {
return applicationCurrentyActive
.asObservable()
.filter { $0 }
.delay(0.5, scheduler: MainScheduler.instance)
.asDriver(onErrorJustReturn: true)
.map { _ in state }
}
}
.drive(stateChanged)
.disposed(by: disposeBag)
}
private func removeCurrentPlaceholderView() {
wrappedView.backgroundView = nil
}
private func bindAppStateNotifications() {
let notificationCenter = NotificationCenter.default.rx
notificationCenter.notification(.UIApplicationWillResignActive)
.map { _ in false }
.asDriver(onErrorJustReturn: false)
.drive(applicationCurrentyActive)
.disposed(by: disposeBag)
notificationCenter.notification(.UIApplicationDidBecomeActive)
.map { _ in true }
.asDriver(onErrorJustReturn: true)
.drive(applicationCurrentyActive)
.disposed(by: disposeBag)
}
}
private extension PaginationWrapper {
private var stateChanged: Binder<LoadingState> {
return Binder(self) { base, value in
switch value {
case .initial:
base.onInitialState()
case .initialLoading(let after):
base.onLoadingState(afterState: after)
case .loadingMore(let after):
base.onLoadingMoreState(afterState: after)
case .results(let newItems, let from, let after):
base.onResultsState(newItems: newItems, from: from, afterState: after)
case .error(let error, let after):
base.onErrorState(error: error, afterState: after)
case .empty:
base.onEmptyState()
case .exhausted:
base.onExhaustedState()
}
}
}
var retryEvent: Binder<()> {
return Binder(self) { base, _ in
base.paginationViewModel.loadMore()
}
}
var reloadEvent: Binder<()> {
return Binder(self) { base, _ in
base.reload()
}
}
var scrollOffsetChanged: Binder<CGPoint> {
return Binder(self) { base, value in
base.currentPlaceholderViewTopConstraint?.constant = -value.y
}
}
}

View File

@ -0,0 +1,108 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
open class RxDataLoadingModel<LoadingStateType: DataLoadingState>: DataLoadingModel
where LoadingStateType.DataSourceType: RxDataSource {
public typealias DataSourceType = LoadingStateType.DataSourceType
public typealias ResultType = DataSourceType.ResultType
public typealias EmptyResultChecker = (ResultType) -> Bool
private let stateVariable = Variable<LoadingStateType>(.initialState)
var currentRequestDisposable: Disposable?
var dataSource: DataSourceType
let emptyResultChecker: EmptyResultChecker
open var stateDriver: Driver<LoadingStateType> {
return stateVariable.asDriver()
}
public init(dataSource: DataSourceType, emptyResultChecker: @escaping EmptyResultChecker) {
self.dataSource = dataSource
self.emptyResultChecker = emptyResultChecker
}
open func reload() {
load(isRetry: false)
}
open func retry() {
load(isRetry: true)
}
private func load(isRetry: Bool) {
currentRequestDisposable?.dispose()
if isRetry {
state = .initialState
}
state = .initialLoadingState(after: state)
requestResult(from: dataSource)
}
func onGot(error: Error) {
state = .errorState(error: error, after: state)
}
private func onGot(result: ResultType, from dataSource: DataSourceType) {
if emptyResultChecker(result) {
state = .emptyState
} else {
state = .resultState(result: result,
from: dataSource,
after: state)
updateStateAfterNonEmptyResult(from: dataSource)
}
}
func requestResult(from dataSource: DataSourceType) {
currentRequestDisposable = dataSource
.resultSingle()
.subscribe(onSuccess: { [weak self] result in
self?.onGot(result: result, from: dataSource)
}, onError: { [weak self] error in
self?.onGot(error: error)
})
}
func updateStateAfterNonEmptyResult(from dataSource: DataSourceType) {
// override in subcass if needed
}
var state: LoadingStateType {
get {
return stateVariable.value
}
set {
stateVariable.value = newValue
}
}
}

View File

@ -1,378 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
import RxSwift
import RxCocoa
import UIScrollView_InfiniteScroll
/// PaginationTableViewWrapper delegate used for pagination results handling and
/// customization of bound states (loading, empty, error, etc.).
public protocol PaginationTableViewWrapperDelegate: class {
associatedtype Cursor: ResettableCursorType
/// Delegate method that handles loading new chunk of data.
///
/// - Parameters:
/// - wrapper: Wrapper object that loaded new items.
/// - newItems: New items.
/// - cursor: Cursor used to load items
func paginationWrapper(wrapper: PaginationTableViewWrapper<Cursor, Self>,
didLoad newItems: [Cursor.Element],
usingCursor cursor: Cursor)
/// Delegate method that handles reloading or initial loading of data.
///
/// - Parameters:
/// - wrapper: Wrapper object that reload items.
/// - allItems: New items.
/// - cursor: Cursor used to load items
func paginationWrapper(wrapper: PaginationTableViewWrapper<Cursor, Self>,
didReload allItems: [Cursor.Element],
usingCursor cursor: Cursor)
/// Delegate method that returns placeholder view for empty state.
///
/// - Parameter wrapper: Wrapper object that requests empty placeholder view.
/// - Returns: Configured instace of UIView.
func emptyPlaceholder(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> UIView
/// Delegate method that returns placeholder view for error state.
///
/// - Parameters:
/// - wrapper: Wrapper object that requests error placeholder view.
/// - error: Error that occured due data loading.
/// - Returns: Configured instace of UIView.
func errorPlaceholder(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>,
forError error: Error) -> UIView
/// Delegate method that returns loading idicator for initial loading state.
/// This indicator will appear at center of the placeholders container.
///
/// - Parameter wrapper: Wrapper object that requests loading indicator
/// - Returns: Configured instace of AnyLoadingIndicator.
func initialLoadingIndicator(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> AnyLoadingIndicator
/// Delegate method that returns loading idicator for initial loading state.
///
/// - Parameter wrapper: Wrapper object that requests loading indicator.
/// - Returns: Configured instace of AnyLoadingIndicator.
func loadingMoreIndicator(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> AnyLoadingIndicator
/// Delegate method that returns instance of UIButton for "retry load more" action.
///
/// - Parameter wrapper: Wrapper object that requests button for "retry load more" action.
/// - Returns: Configured instace of AnyLoadingIndicator.
func retryLoadMoreButton(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> UIButton
/// Delegate method that returns preferred height for "retry load more" button.
///
/// - Parameter wrapper: Wrapper object that requests height "retry load more" button.
/// - Returns: Preferred height of "retry load more" button.
func retryLoadMoreButtonHeight(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> CGFloat
// Delegate method, used to clear tableView if placeholder is shown.
func clearTableView()
}
/// Class that connects PaginationViewModel with UITableView. It handles all non-visual and visual states.
final public class PaginationTableViewWrapper<Cursor, Delegate: PaginationTableViewWrapperDelegate>
where Delegate.Cursor == Cursor {
private let tableView: UITableView
private let paginationViewModel: PaginationViewModel<Cursor>
private weak var delegate: Delegate?
/// Sets the offset between the real end of the scroll view content and the scroll position,
/// so the handler can be triggered before reaching end. Defaults to 0.0;
public var infiniteScrollTriggerOffset: CGFloat {
get {
return tableView.infiniteScrollTriggerOffset
}
set {
tableView.infiniteScrollTriggerOffset = newValue
}
}
private let disposeBag = DisposeBag()
private var currentPlaceholderView: UIView?
private var currentPlaceholderViewTopConstraint: NSLayoutConstraint?
private let applicationCurrentyActive = Variable<Bool>(true)
/// Initializer with table view, placeholders container view, cusor and delegate parameters.
///
/// - Parameters:
/// - tableView: UITableView instance to work with.
/// - cursor: Cursor object that acts as data source.
/// - delegate: Delegate object for data loading events handling and UI customization.
public init(tableView: UITableView, cursor: Cursor, delegate: Delegate) {
self.tableView = tableView
self.paginationViewModel = PaginationViewModel(cursor: cursor)
self.delegate = delegate
bindViewModelStates()
createRefreshControl()
bindAppStateNotifications()
}
/// Method that reload all data in internal view model.
public func reload() {
paginationViewModel.load(.reload)
}
/// Method acts like reload, but shows initial loading view after being invoked.
public func retry() {
paginationViewModel.load(.retry)
}
/// Method that enables placeholders animation due pull-to-refresh interaction.
///
/// - Parameter scrollObservable: Observable that emits content offset as CGPoint.
public func setScrollObservable(_ scrollObservable: Observable<CGPoint>) {
scrollObservable.subscribe(onNext: { [weak self] offset in
self?.currentPlaceholderViewTopConstraint?.constant = -offset.y
})
.disposed(by: disposeBag)
}
// MARK: - States handling
private func onInitialState() {
//
}
private func onLoadingState(afterState: PaginationViewModel<Cursor>.State) {
if case .initial = afterState {
tableView.isUserInteractionEnabled = false
removeCurrentPlaceholderView()
guard let loadingIndicator = delegate?.initialLoadingIndicator(forPaginationWrapper: self) else {
return
}
let loadingIndicatorView = loadingIndicator.view
loadingIndicatorView.translatesAutoresizingMaskIntoConstraints = true
tableView.backgroundView = loadingIndicatorView
loadingIndicator.startAnimating()
currentPlaceholderView = loadingIndicatorView
} else {
removeInfiniteScroll()
tableView.tableFooterView = nil
}
}
private func onLoadingMoreState(afterState: PaginationViewModel<Cursor>.State) {
if case .error = afterState { // user tap retry button in table footer
tableView.tableFooterView = nil
addInfiniteScroll()
tableView.beginInfiniteScroll(true)
}
}
private func onResultsState(newItems: [Cursor.Element],
inCursor cursor: Cursor,
afterState: PaginationViewModel<Cursor>.State) {
tableView.isUserInteractionEnabled = true
if case .loading = afterState {
delegate?.paginationWrapper(wrapper: self, didReload: newItems, usingCursor: cursor)
removeCurrentPlaceholderView()
tableView.support.refreshControl?.endRefreshing()
if !cursor.exhausted {
addInfiniteScroll()
}
} else if case .loadingMore = afterState {
delegate?.paginationWrapper(wrapper: self, didLoad: newItems, usingCursor: cursor)
tableView.finishInfiniteScroll()
}
}
private func onErrorState(error: Error, afterState: PaginationViewModel<Cursor>.State) {
if case .loading = afterState {
defer {
tableView.support.refreshControl?.endRefreshing()
}
guard let errorView = delegate?.errorPlaceholder(forPaginationWrapper: self, forError: error) else {
return
}
replacePlaceholderViewIfNeeded(with: errorView)
} else if case .loadingMore = afterState {
removeInfiniteScroll()
guard let retryButton = delegate?.retryLoadMoreButton(forPaginationWrapper: self),
let retryButtonHeigth = delegate?.retryLoadMoreButtonHeight(forPaginationWrapper: self) else {
return
}
retryButton.frame = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: retryButtonHeigth)
retryButton.rx.controlEvent(.touchUpInside)
.bind { [weak self] in
self?.paginationViewModel.load(.next)
}
.disposed(by: disposeBag)
tableView.tableFooterView = retryButton
}
}
private func onEmptyState() {
defer {
tableView.support.refreshControl?.endRefreshing()
}
guard let emptyView = delegate?.emptyPlaceholder(forPaginationWrapper: self) else {
return
}
replacePlaceholderViewIfNeeded(with: emptyView)
}
private func replacePlaceholderViewIfNeeded(with placeholderView: UIView) {
tableView.isUserInteractionEnabled = true
removeCurrentPlaceholderView()
placeholderView.translatesAutoresizingMaskIntoConstraints = false
placeholderView.isHidden = false
// I was unable to add pull-to-refresh placeholder scroll behaviour without this trick
let wrapperView = UIView()
wrapperView.addSubview(placeholderView)
let leadingConstraint = placeholderView.leadingAnchor.constraint(equalTo: wrapperView.leadingAnchor)
let trailingConstraint = placeholderView.trailingAnchor.constraint(equalTo: wrapperView.trailingAnchor)
let topConstraint = placeholderView.topAnchor.constraint(equalTo: wrapperView.topAnchor)
let bottomConstraint = placeholderView.bottomAnchor.constraint(equalTo: wrapperView.bottomAnchor)
wrapperView.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
currentPlaceholderViewTopConstraint = topConstraint
tableView.backgroundView = wrapperView
currentPlaceholderView = placeholderView
}
// MARK: - private stuff
private func onExhaustedState() {
removeInfiniteScroll()
}
private func addInfiniteScroll() {
tableView.addInfiniteScroll { [weak paginationViewModel] _ in
paginationViewModel?.load(.next)
}
tableView.infiniteScrollIndicatorView = delegate?.loadingMoreIndicator(forPaginationWrapper: self).view
}
private func removeInfiniteScroll() {
tableView.finishInfiniteScroll()
tableView.removeInfiniteScroll()
}
private func createRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.rx.controlEvent(.valueChanged)
.bind { [weak self] in
self?.reload()
}
.disposed(by: disposeBag)
tableView.support.setRefreshControl(refreshControl)
}
private func bindViewModelStates() {
typealias State = PaginationViewModel<Cursor>.State
paginationViewModel.state.flatMap { [applicationCurrentyActive] state -> Driver<State> in
if applicationCurrentyActive.value {
return .just(state)
} else {
return applicationCurrentyActive
.asObservable()
.filter { $0 }
.delay(0.5, scheduler: MainScheduler.instance)
.asDriver(onErrorJustReturn: true)
.map { _ in state }
}
}
.drive(onNext: { [weak self] state in
switch state {
case .initial:
self?.onInitialState()
case .loading(let after):
self?.onLoadingState(afterState: after)
case .loadingMore(let after):
self?.onLoadingMoreState(afterState: after)
case .results(let newItems, let cursor, let after):
self?.onResultsState(newItems: newItems, inCursor: cursor, afterState: after)
case .error(let error, let after):
self?.delegate?.clearTableView()
self?.onErrorState(error: error, afterState: after)
case .empty:
self?.delegate?.clearTableView()
self?.onEmptyState()
case .exhausted:
self?.onExhaustedState()
}
})
.disposed(by: disposeBag)
}
private func removeCurrentPlaceholderView() {
tableView.backgroundView = nil
}
private func bindAppStateNotifications() {
let notificationCenter = NotificationCenter.default.rx
notificationCenter.notification(.UIApplicationWillResignActive)
.subscribe(onNext: { [weak self] _ in
self?.applicationCurrentyActive.value = false
})
.disposed(by: disposeBag)
notificationCenter.notification(.UIApplicationDidBecomeActive)
.subscribe(onNext: { [weak self] _ in
self?.applicationCurrentyActive.value = true
})
.disposed(by: disposeBag)
}
}

View File

@ -1,154 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
/// Class that encapsulate all pagination logic
public final class PaginationViewModel<C: ResettableCursorType> {
/// Enum contains all possible states for PaginationViewModel class.
///
/// - initial: initial state of view model.
/// Can occur only once after initial binding.
/// - loading: loading state of view model. Contains previous state of view model.
/// Can occur after any state.
/// - loadingMore: loading more items state of view model. Contains previous state of view model.
/// Can occur after error or results state.
/// - results: results state of view model. Contains loaded items, cursor and previous state of view model.
/// Can occur after loading or loadingMore state.
/// - error: error state of view model. Contains received error and previous state of view model.
/// Can occur after loading or loadingMore state.
/// - empty: empty state of view model.
/// Can occur after loading or loadingMore state when we got empty result (zero items).
/// - exhausted: exhausted state of view model.
/// Can occur after results state or after initial->loading state when cursor reports that it's exhausted.
public indirect enum State {
case initial
case loading(after: State)
case loadingMore(after: State)
case results(newItems: [C.Element], inCursor: C, after: State)
case error(error: Error, after: State)
case empty
case exhausted
}
/// Enum represents possible load types for PaginationViewModel class
///
/// - reload: reload all items and reset cursor to initial state.
/// - next: load next batch of items.
/// - retry: reload to initial loading state
public enum LoadType {
case reload
case retry
case next
}
private var cursor: C
private let internalState = Variable<State>(.initial)
private var currentRequest: Disposable?
private let internalScheduler = SerialDispatchQueueScheduler(qos: .default)
/// Current PaginationViewModel state Driver
public var state: Driver<State> {
return internalState.asDriver()
}
/// Initializer with enclosed cursor
///
/// - Parameter cursor: cursor to use for pagination
public init(cursor: C) {
self.cursor = cursor
}
/// Mathod which triggers loading of items.
///
/// - Parameter loadType: type of loading. See LoadType enum.
public func load(_ loadType: LoadType) {
switch loadType {
case .reload:
reload()
case .retry:
reload(isRetry: true)
case .next:
if case .exhausted = internalState.value {
fatalError("You shouldn't call load(.next) after got .exhausted state!")
}
internalState.value = .loadingMore(after: internalState.value)
}
let currentCursor = cursor
currentRequest = currentCursor.loadNextBatch()
.subscribeOn(internalScheduler)
.subscribe(onSuccess: { [weak self] newItems in
self?.onGot(newItems: newItems, using: currentCursor)
}, onError: { [weak self] error in
self?.onGot(error: error)
})
}
private func onGot(newItems: [C.Element], using cursor: C) {
if newItems.isEmpty {
internalState.value = .empty
return
}
internalState.value = .results(newItems: newItems, inCursor: cursor, after: internalState.value)
if cursor.exhausted {
internalState.value = .exhausted
}
}
private func onGot(error: Error) {
if case .exhausted? = error as? CursorError, case .loading(let after) = internalState.value {
switch after {
case .initial, .empty: // cursor exhausted after creation
internalState.value = .empty
default:
internalState.value = .error(error: error, after: internalState.value)
}
} else {
internalState.value = .error(error: error, after: internalState.value)
}
}
private func reload(isRetry: Bool = false) {
currentRequest?.dispose()
cursor = cursor.reset()
if isRetry {
internalState.value = .initial
}
internalState.value = .loading(after: internalState.value)
}
}

View File

@ -0,0 +1,58 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
public enum GeneralDataLoadingState<DS: DataSource>: DataLoadingState {
case initial
case loading
case result(newResult: DS.ResultType, from: DS)
case error(error: Error)
case empty
public typealias DataSourceType = DS
public static var initialState: GeneralDataLoadingState<DS> {
return .initial
}
public static var emptyState: GeneralDataLoadingState<DS> {
return .empty
}
public static func initialLoadingState(after: GeneralDataLoadingState<DS>) -> GeneralDataLoadingState<DS> {
return .loading
}
public static func resultState(result: DS.ResultType,
from: DS,
after: GeneralDataLoadingState<DS>) -> GeneralDataLoadingState<DS> {
return .result(newResult: result, from: from)
}
public static func errorState(error: Error,
after: GeneralDataLoadingState<DS>) -> GeneralDataLoadingState<DS> {
return .error(error: error)
}
}

View File

@ -0,0 +1,60 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
public indirect enum PaginationDataLoadingState<DS: DataSource>: DataLoadingState {
case initial
case initialLoading(after: PaginationDataLoadingState)
case loadingMore(after: PaginationDataLoadingState)
case results(newItems: DS.ResultType, from: DS, after: PaginationDataLoadingState)
case error(error: Error, after: PaginationDataLoadingState)
case empty
case exhausted
public typealias DataSourceType = DS
public static var initialState: PaginationDataLoadingState<DS> {
return .initial
}
public static var emptyState: PaginationDataLoadingState<DS> {
return .empty
}
public static func initialLoadingState(after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
return .initialLoading(after: after)
}
public static func resultState(result: DS.ResultType,
from: DataSourceType,
after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
return .results(newItems: result, from: from, after: after)
}
public static func errorState(error: Error,
after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
return .error(error: error, after: after)
}
}

View File

@ -0,0 +1,33 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
public extension CursorType {
typealias ResultType = [Element]
func resultSingle() -> Single<ResultType> {
return loadNextBatch()
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Touch Instinct
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
@ -22,9 +22,9 @@
import UIKit
public extension PaginationTableViewWrapperDelegate {
public extension PaginationWrapperDelegate {
func emptyPlaceholder(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> UIView {
func emptyPlaceholder() -> UIView {
let placeholder = UIView()
let label = UILabel()
@ -32,15 +32,20 @@ public extension PaginationTableViewWrapperDelegate {
label.text = "There is nothing here"
placeholder.addSubview(label)
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor).isActive = true
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor),
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor)
])
return placeholder
}
func errorPlaceholder(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>,
forError error: Error) -> UIView {
func customInitialLoadingErrorHandling(for error: Error) -> Bool {
return false
}
func errorPlaceholder(for error: Error) -> UIView {
let placeholder = UIView()
let label = UILabel()
@ -48,30 +53,29 @@ public extension PaginationTableViewWrapperDelegate {
label.text = "An error has occurred"
placeholder.addSubview(label)
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor).isActive = true
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor),
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor)
])
return placeholder
}
func initialLoadingIndicator(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>)
-> AnyLoadingIndicator {
func initialLoadingIndicator() -> AnyLoadingIndicator {
let indicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
indicator.color = .gray
return AnyLoadingIndicator(indicator)
}
func loadingMoreIndicator(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>)
-> AnyLoadingIndicator {
func loadingMoreIndicator() -> AnyLoadingIndicator {
let indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
return AnyLoadingIndicator(indicator)
}
func retryLoadMoreButton(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> UIButton {
func footerRetryButton() -> UIButton {
let retryButton = UIButton(type: .custom)
retryButton.backgroundColor = .lightGray
retryButton.setTitle("Retry load more", for: .normal)
@ -79,8 +83,16 @@ public extension PaginationTableViewWrapperDelegate {
return retryButton
}
func retryLoadMoreButtonHeight(forPaginationWrapper wrapper: PaginationTableViewWrapper<Cursor, Self>) -> CGFloat {
func footerRetryButtonHeight() -> CGFloat {
return 44
}
func footerRetryButtonWillAppear() {
// by default - nothing will happen
}
func footerRetryButtonWillDisappear() {
// by default - nothing will happen
}
}

View File

@ -0,0 +1,40 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UICollectionView
extension UICollectionView: PaginationWrappable {
public var scrollView: UIScrollView {
return self
}
public var footerView: UIView? {
get {
return nil
}
set {
// nothing
}
}
}

View File

@ -0,0 +1,40 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UITableView
extension UITableView: PaginationWrappable {
public var scrollView: UIScrollView {
return self
}
public var footerView: UIView? {
get {
return tableFooterView
}
set {
tableFooterView = newValue
}
}
}

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
extension Observable: RxDataSource {
public typealias ResultType = Element
public func resultSingle() -> Single<ResultType> {
return asSingle()
}
}
// waiting for Swift 4.2 release...
// https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md
// extension PrimitiveSequence: RxDataSource where Trait == SingleTrait {
extension Single: RxDataSource {
public typealias ResultType = Element
public func resultSingle() -> Single<ResultType> {
return asObservable().asSingle()
}
}

View File

@ -21,4 +21,4 @@
//
/// Ressetable cursor type that conforms to DataSourceProtocol
public typealias ResettableCursorDataSource = ResettableCursorType & DataSourceProtocol
public typealias ResettableRxDataSourceCursor = ResettableCursorType & RxDataSource

View File

@ -0,0 +1,40 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxCocoa
/// Protocol that describes data loading process
/// with methods reload & retry and current state driver variable.
public protocol DataLoadingModel {
associatedtype LoadingStateType: DataLoadingState
/// Driver, that emits current state of loading process
var stateDriver: Driver<LoadingStateType> { get }
/// Perform (re)load data.
func reload()
/// Attempt to load data again.
func retry()
}

View File

@ -0,0 +1,57 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// Protocol that describes possible states of data loading process.
public protocol DataLoadingState {
associatedtype DataSourceType: DataSource
/// Initial state. Before something will happen.
static var initialState: Self { get }
/// Empty state. When data was requested and empty result was received.
static var emptyState: Self { get }
/// Method that returns loading state after a given state.
///
/// - Parameter after: Previous state of data loading process.
/// - Returns: Instance of loading state with given argument.
static func initialLoadingState(after: Self) -> Self
/// Method that returns result state from a specific data source after a given state.
///
/// - Parameters:
/// - result: DataSource result.
/// - from: DataSource from that a result was received.
/// - after: Previous state of data loading process.
/// - Returns: Instance of result state with given arguments.
static func resultState(result: DataSourceType.ResultType, from: DataSourceType, after: Self) -> Self
/// Method that returns error state with a specific error after a given state.
///
/// - Parameters:
/// - error: An error that happens due data loading process.
/// - after: Previous state of data loading process.
/// - Returns: Instance of error state with given arguments.
static func errorState(error: Error, after: Self) -> Self
}

View File

@ -21,7 +21,7 @@
//
/// Protocol for data sources that has type of loaded result
public protocol DataSourceProtocol {
public protocol DataSource {
associatedtype ResultType

View File

@ -0,0 +1,47 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Protocol that contains scroll view property
public protocol ScrollViewHolder {
var scrollView: UIScrollView { get }
}
/// Protocol that contains background view property
public protocol BackgroundViewHolder {
var backgroundView: UIView? { get set }
}
/// Protocol that contains footer view property
public protocol FooterViewHolder {
var footerView: UIView? { get set }
}
/// Protocol that conforms to ScrollViewHolder, BackgroundViewHolder and FooterViewHolder protocols
public typealias PaginationWrappable = ScrollViewHolder & BackgroundViewHolder & FooterViewHolder

View File

@ -22,12 +22,9 @@
import RxSwift
/// Protocol that requests class to conform ResettableType
/// and return next batch of data on nextBatchObservable call
public protocol TotalCountCursorConfiguration: class, ResettableType {
associatedtype ListingType: TotalCountCursorListingResult
func nextBatchObservable() -> Single<ListingType>
/// Protocol that requests class to conform RxDataSource and ResettableType
/// with constraint ResultType to TotalCountCursorListingResult.
public protocol TotalCountCursorConfiguration: class, RxDataSource, ResettableType
where ResultType: TotalCountCursorListingResult {
}

View File

@ -0,0 +1,33 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
/// Protocol with method for requesting data wrapped with Single.
public protocol RxDataSource: DataSource {
/// Request data and returns Single of data type.
///
/// - Returns: Single sequence with one element.
func resultSingle() -> Single<ResultType>
}

View File

@ -0,0 +1,72 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Type that performs some kind of type erasure for PaginationWrappable.
public final class AnyPaginationWrappable: PaginationWrappable {
private typealias ViewSetter = (UIView?) -> Void
public var footerView: UIView? {
get {
return footerViewBacking
}
set {
footerViewSetter(newValue)
}
}
public var backgroundView: UIView? {
get {
return backgroundViewBacking
}
set {
backgroundViewSetter(newValue)
}
}
public let scrollView: UIScrollView
private let backgroundViewBacking: UIView?
private let footerViewBacking: UIView?
private let backgroundViewSetter: ViewSetter
private let footerViewSetter: ViewSetter
public init<View>(view: View) where View: PaginationWrappable {
self.scrollView = view.scrollView
self.backgroundViewBacking = view.backgroundView
self.footerViewBacking = view.footerView
var localView = view
self.backgroundViewSetter = {
localView.backgroundView = $0
}
self.footerViewSetter = {
localView.footerView = $0
}
}
}

View File

@ -0,0 +1,97 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// PaginationWrapper delegate used for pagination results handling and
/// customization of bound states (loading, empty, error, etc.).
public protocol PaginationWrapperDelegate: class {
associatedtype DataSourceType: DataSource
/// Handles loading new chunk of data.
///
/// - Parameters:
/// - newItems: New items.
/// - dataSource: Data source used to load items
func paginationWrapper(didLoad newItems: DataSourceType.ResultType,
using dataSource: DataSourceType)
/// Handles reloading or initial loading of data.
///
/// - Parameters:
/// - allItems: New items.
/// - dataSource: Data source used to load items
func paginationWrapper(didReload allItems: DataSourceType.ResultType,
using dataSource: DataSourceType)
/// Returns placeholder view for empty state.
///
/// - Returns: Configured instace of UIView.
func emptyPlaceholder() -> UIView
/// Called when initial loading error is occured.
/// It should return true if error is handled and false if it doesn't.
///
/// - Parameters:
/// - error: Error that occured due data loading.
/// - Returns: Bool value. If true - then error placeholder wouldn't be shown.
func customInitialLoadingErrorHandling(for error: Error) -> Bool
/// Returns placeholder view for error state.
///
/// - Parameters:
/// - error: Error that occured due data loading.
/// - Returns: Configured instace of UIView.
func errorPlaceholder(for error: Error) -> UIView
/// Returns loading idicator for initial loading state.
/// This indicator will appear at center of the placeholders container.
///
/// - Returns: Configured instace of AnyLoadingIndicator.
func initialLoadingIndicator() -> AnyLoadingIndicator
/// Returns loading idicator for initial loading state.
///
/// - Returns: Configured instace of AnyLoadingIndicator.
func loadingMoreIndicator() -> AnyLoadingIndicator
/// Returns instance of UIButton for "retry load more" action.
///
/// - Returns: Configured instace of AnyLoadingIndicator.
func footerRetryButton() -> UIButton
/// Returns height for "retry load more" button.
///
/// - Returns: Height of "retry load more" button.
func footerRetryButtonHeight() -> CGFloat
/// Method is called before "retry load more" will be shown.
/// Typically, it's used when you need to show custom footer view.
func footerRetryButtonWillAppear()
/// Method is called before "retry load more" will be hidden.
/// Typically, it's used when you need to hide custom footer view.
func footerRetryButtonWillDisappear()
/// Clears view when placeholder is shown.
func clearView()
}