Merge pull request #115 from TouchInstinct/feature/data_loading_and_pagination_wrapper
Feature/data loading and pagination wrapper
This commit is contained in:
commit
cd00fb2f2f
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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>>> {
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,4 +21,4 @@
|
|||
//
|
||||
|
||||
/// Ressetable cursor type that conforms to DataSourceProtocol
|
||||
public typealias ResettableCursorDataSource = ResettableCursorType & DataSourceProtocol
|
||||
public typealias ResettableRxDataSourceCursor = ResettableCursorType & RxDataSource
|
||||
|
|
@ -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()
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
//
|
||||
|
||||
/// Protocol for data sources that has type of loaded result
|
||||
public protocol DataSourceProtocol {
|
||||
public protocol DataSource {
|
||||
|
||||
associatedtype ResultType
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue