Merge pull request #116 from TouchInstinct/feature/base_loading_view_model_and_controller

Feature/base loading view model and controller
This commit is contained in:
Ivan Smolin 2018-03-23 14:33:25 +03:00 committed by GitHub
commit a1a8ed5366
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 554 additions and 52 deletions

View File

@ -1 +0,0 @@
code-quality/.swiftlint.yml

View File

@ -1 +0,0 @@
code-quality/.tailor.yml

View File

@ -46,10 +46,12 @@ Pod::Spec.new do |s|
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Classes/Views/XibView/*",
"Sources/Classes/Views/SpinnerView/*",
"Sources/Classes/Views/DefaultPlaceholders/*",
"Sources/Extensions/CABasicAnimation/*",
"Sources/Extensions/CGFloat/CGFloat+Pixels.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/NetworkService/NetworkService+RxLoadImage.swift",
"Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/Support/UIScrollView+Support.swift",
"Sources/Extensions/TableDirector/*",
@ -65,6 +67,7 @@ Pod::Spec.new do |s|
"Sources/Extensions/UIWindow/*",
"Sources/Protocols/LoadingIndicator.swift",
"Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift",
"Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift",
"Sources/Structures/Views/AnyLoadingIndicator.swift",
"Sources/Structures/DrawingOperations/CALayerDrawingOperation.swift",
"Sources/Structures/DrawingOperations/RoundDrawingOperation.swift",

View File

@ -276,6 +276,31 @@
671463CF1EB34B1E00EAB194 /* TestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671463B71EB34B1E00EAB194 /* TestView.xib */; };
67186B311EB248F100CFAFFB /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67186B281EB248F100CFAFFB /* LeadKit.framework */; };
67186B3F1EB24A1900CFAFFB /* LeadKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 67186B201EB247A200CFAFFB /* LeadKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
673CF40B2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */; };
673CF40C2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */; };
673CF40D2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */; };
673CF40E2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */; };
673CF4112063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */; };
673CF4122063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */; };
673CF4132063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */; };
673CF4142063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */; };
673CF4182063D50700C329F6 /* GeneralDataLoadingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */; };
673CF4192063D50700C329F6 /* GeneralDataLoadingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */; };
673CF4222063D90600C329F6 /* DisposeBagHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */; };
673CF4232063D90600C329F6 /* DisposeBagHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */; };
673CF4242063D90600C329F6 /* DisposeBagHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */; };
673CF4252063D90600C329F6 /* DisposeBagHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */; };
673CF42C2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */; };
673CF42D2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */; };
673CF42E2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */; };
673CF4342063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */; };
673CF4352063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */; };
673CF4362063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */; };
673CF4382063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */; };
673CF4392063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */; };
673CF43A2063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */; };
673CF43B2063E80B00C329F6 /* GeneralDataLoadingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */; };
673CF43C2063E80B00C329F6 /* GeneralDataLoadingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */; };
674AF55C1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */; };
674AF55D1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */; };
674AF55E1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */; };
@ -579,6 +604,13 @@
67186B301EB248F100CFAFFB /* LeadKit iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LeadKit iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
67186B411EB24AA000CFAFFB /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
67186C1A1EB24B7800CFAFFB /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingViewModel.swift; sourceTree = "<group>"; };
673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GeneralDataLoadingState+Extensions.swift"; sourceTree = "<group>"; };
673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingController.swift; sourceTree = "<group>"; };
673CF4212063D90600C329F6 /* DisposeBagHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposeBagHolder.swift; sourceTree = "<group>"; };
673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextPlaceholderView.swift; sourceTree = "<group>"; };
673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextWithButtonPlaceholder.swift; sourceTree = "<group>"; };
673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GeneralDataLoadingController+DefaultImplementation.swift"; sourceTree = "<group>"; };
674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActivityIndicatorView+LoadingIndicator.swift"; sourceTree = "<group>"; };
6771DFD71EE99EBA002DCDAE /* DateFormattingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingService.swift; sourceTree = "<group>"; };
6771DFDD1EE99F6F002DCDAE /* DateFormattingArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingArguments.swift; sourceTree = "<group>"; };
@ -783,6 +815,7 @@
671461D41EB3396E00EAB194 /* Views */ = {
isa = PBXGroup;
children = (
673CF42A2063DE3A00C329F6 /* DefaultPlaceholders */,
A6AF3B381F8B957400CDB971 /* SeparatorRowBox */,
A66428A41F8A651700C6308D /* SeparatorCell */,
A6C9A4F71F8BBC0F009311CC /* EmptyCell */,
@ -1012,6 +1045,7 @@
isa = PBXGroup;
children = (
67EB7FC5206148C400BDD9FB /* DataLoading */,
673CF4202063D8EB00C329F6 /* Rx */,
67EB7FF5206175CD00BDD9FB /* Views */,
679C77D41F98F78E0094BE10 /* AlertRepresentable.swift */,
671463A11EB33FF600EAB194 /* Animatable.swift */,
@ -1144,6 +1178,40 @@
path = Tests;
sourceTree = "<group>";
};
673CF4152063AC0000C329F6 /* GeneralDataLoading */ = {
isa = PBXGroup;
children = (
673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */,
673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */,
);
path = GeneralDataLoading;
sourceTree = "<group>";
};
673CF4202063D8EB00C329F6 /* Rx */ = {
isa = PBXGroup;
children = (
673CF4212063D90600C329F6 /* DisposeBagHolder.swift */,
);
path = Rx;
sourceTree = "<group>";
};
673CF42A2063DE3A00C329F6 /* DefaultPlaceholders */ = {
isa = PBXGroup;
children = (
673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */,
673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */,
);
path = DefaultPlaceholders;
sourceTree = "<group>";
};
673CF43D2063E81200C329F6 /* GeneralDataLoading */ = {
isa = PBXGroup;
children = (
673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */,
);
path = GeneralDataLoading;
sourceTree = "<group>";
};
674AF55A1EC45B1600038A8F /* UIActivityIndicatorView */ = {
isa = PBXGroup;
children = (
@ -1200,6 +1268,7 @@
67745284206259C20024EEEF /* DataLoading */ = {
isa = PBXGroup;
children = (
673CF4152063AC0000C329F6 /* GeneralDataLoading */,
67EB7FEF2061681A00BDD9FB /* Cursors */,
671461E81EB3396E00EAB194 /* CursorType */,
67745266206248F00024EEEF /* PaginationDataLoading */,
@ -1230,6 +1299,7 @@
isa = PBXGroup;
children = (
6774529120625D170024EEEF /* GeneralDataLoadingModel.swift */,
673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */,
);
path = GeneralDataLoading;
sourceTree = "<group>";
@ -1280,10 +1350,11 @@
isa = PBXGroup;
children = (
67EB7FE820615E5000BDD9FB /* Cursors */,
673CF43D2063E81200C329F6 /* GeneralDataLoading */,
67EB7FD820615D3100BDD9FB /* PaginationDataLoading */,
67EB7FE320615DE000BDD9FB /* DataSource.swift */,
6774527320624E820024EEEF /* DataLoadingModel.swift */,
67745278206252020024EEEF /* DataLoadingState.swift */,
67EB7FE320615DE000BDD9FB /* DataSource.swift */,
677452A320625FA90024EEEF /* RxDataSource.swift */,
);
path = DataLoading;
@ -2240,6 +2311,8 @@
671462801EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
67EB7FF8206175F700BDD9FB /* PaginationWrappable.swift in Sources */,
671463541EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
673CF4112063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */,
673CF42C2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */,
671463601EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
671462841EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
EFBE57DB1EC361620040E00A /* UIView+Layout.swift in Sources */,
@ -2299,6 +2372,8 @@
677452AE206274630024EEEF /* PaginationWrapper.swift in Sources */,
A6E0DDF11F8A6C80002CA74E /* SeparatorConfiguration.swift in Sources */,
67EB7FEB2061667900BDD9FB /* DefaultTotalCountCursorListingResult.swift in Sources */,
673CF4382063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */,
673CF40B2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */,
6714636C1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */,
6771DFEA1EEA7CB8002DCDAE /* DateFormattingService+MappingTransform.swift in Sources */,
671462A01EB3396E00EAB194 /* Double+Rounding.swift in Sources */,
@ -2308,6 +2383,7 @@
6774529220625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
6714637C1EB3396E00EAB194 /* ImageDrawingOperation.swift in Sources */,
671463341EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
673CF4182063D50700C329F6 /* GeneralDataLoadingController.swift in Sources */,
671462701EB3396E00EAB194 /* CursorError.swift in Sources */,
671463981EB3396E00EAB194 /* AnyLoadingIndicator.swift in Sources */,
671463A71EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */,
@ -2347,6 +2423,8 @@
67EB7FD420615D1700BDD9FB /* ResettableCursorType.swift in Sources */,
6714629C1EB3396E00EAB194 /* CursorType+Slice.swift in Sources */,
6774529F20625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
673CF4342063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */,
673CF4222063D90600C329F6 /* DisposeBagHolder.swift in Sources */,
671463681EB3396E00EAB194 /* ConfigurableView.swift in Sources */,
6771DFE41EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift in Sources */,
);
@ -2395,6 +2473,7 @@
6774527B206252020024EEEF /* DataLoadingState.swift in Sources */,
6714634E1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
6714626A1EB3396E00EAB194 /* NetworkService.swift in Sources */,
673CF4132063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */,
671463421EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462921EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
671463861EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */,
@ -2424,6 +2503,7 @@
6714636E1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */,
67CDEE401EB369BF00895905 /* ConfigurableController.swift in Sources */,
A676AE481F97D28A001F9214 /* String+Extensions.swift in Sources */,
673CF4242063D90600C329F6 /* DisposeBagHolder.swift in Sources */,
671462A21EB3396E00EAB194 /* Double+Rounding.swift in Sources */,
67EB7FF32061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */,
6714625E1EB3396E00EAB194 /* LogFormatter.swift in Sources */,
@ -2433,7 +2513,9 @@
671462721EB3396E00EAB194 /* CursorError.swift in Sources */,
671462961EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */,
671463661EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
673CF43C2063E80B00C329F6 /* GeneralDataLoadingController.swift in Sources */,
67EB7FD120615B8900BDD9FB /* TotalCountCursorConfiguration.swift in Sources */,
673CF40D2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */,
67EB7FC9206148D000BDD9FB /* TotalCountCursorListingResult.swift in Sources */,
6774527620624E820024EEEF /* DataLoadingModel.swift in Sources */,
6771DFE61EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift in Sources */,
@ -2466,6 +2548,7 @@
671462CF1EB3396E00EAB194 /* String+SizeCalculation.swift in Sources */,
671462831EB3396E00EAB194 /* AlamofireRequest+Extensions.swift in Sources */,
677452B820627FE00024EEEF /* PaginationWrappable.swift in Sources */,
673CF40E2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */,
67745283206256A20024EEEF /* RxDataLoadingModel.swift in Sources */,
67EB8003206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */,
671463571EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
@ -2475,6 +2558,7 @@
671462F31EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */,
6771DFE71EE9A00A002DCDAE /* DateFormattingArguments+DateFormatter.swift in Sources */,
6714626B1EB3396E00EAB194 /* NetworkService.swift in Sources */,
673CF43A2063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */,
671463131EB3396E00EAB194 /* UIViewController+DefaultXibName.swift in Sources */,
671463431EB3396E00EAB194 /* ModuleConfigurator.swift in Sources */,
671462931EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
@ -2499,6 +2583,7 @@
677452AC206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
6714632B1EB3396E00EAB194 /* BaseViewModel.swift in Sources */,
677452A220625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */,
673CF4362063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */,
A6F32C0C1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */,
671462AF1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */,
A676AE4F1F9810C1001F9214 /* Any+Cast.swift in Sources */,
@ -2522,10 +2607,12 @@
671462A31EB3396E00EAB194 /* Double+Rounding.swift in Sources */,
6714625F1EB3396E00EAB194 /* LogFormatter.swift in Sources */,
6714630B1EB3396E00EAB194 /* UIView+Rotation.swift in Sources */,
673CF4192063D50700C329F6 /* GeneralDataLoadingController.swift in Sources */,
6714626F1EB3396E00EAB194 /* XibView.swift in Sources */,
6714637F1EB3396E00EAB194 /* ImageDrawingOperation.swift in Sources */,
67EB7FD720615D1700BDD9FB /* ResettableCursorType.swift in Sources */,
671463371EB3396E00EAB194 /* DrawingOperation.swift in Sources */,
673CF42E2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */,
671462731EB3396E00EAB194 /* CursorError.swift in Sources */,
67EB7FDD20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
677452A720625FA90024EEEF /* RxDataSource.swift in Sources */,
@ -2548,6 +2635,7 @@
675C1FB41F97CA32007D5249 /* AppearanceConfigurable.swift in Sources */,
6714631B1EB3396E00EAB194 /* UIWindow+Extensions.swift in Sources */,
671462571EB3396E00EAB194 /* App.swift in Sources */,
673CF4252063D90600C329F6 /* DisposeBagHolder.swift in Sources */,
6714637B1EB3396E00EAB194 /* CALayerDrawingOperation.swift in Sources */,
6774529520625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
6774529D20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
@ -2556,6 +2644,7 @@
6714628F1EB3396E00EAB194 /* CGImage+Alpha.swift in Sources */,
67051ADD1EBC7C36008EADC0 /* SpinnerView.swift in Sources */,
671462771EB3396E00EAB194 /* LeadKitError.swift in Sources */,
673CF4142063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */,
671462DB1EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */,
6774529020625C9E0024EEEF /* GeneralDataLoadingState.swift in Sources */,
67EB7FC3206140E600BDD9FB /* TotalCountCursor.swift in Sources */,
@ -2631,14 +2720,17 @@
6774526D206249E30024EEEF /* UICollectionView+PaginationWrappable.swift in Sources */,
6714627D1EB3396E00EAB194 /* AlamofireManager+Extensions.swift in Sources */,
671462591EB3396E00EAB194 /* Log.swift in Sources */,
673CF4352063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */,
6774527120624A2A0024EEEF /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */,
67EB8002206177D600BDD9FB /* PaginationWrapperDelegate.swift in Sources */,
673CF4232063D90600C329F6 /* DisposeBagHolder.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 */,
671462ED1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */,
6714636D1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */,
673CF42D2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */,
671462A11EB3396E00EAB194 /* Double+Rounding.swift in Sources */,
6714625D1EB3396E00EAB194 /* LogFormatter.swift in Sources */,
671463091EB3396E00EAB194 /* UIView+Rotation.swift in Sources */,
@ -2650,6 +2742,7 @@
6774529320625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
671462711EB3396E00EAB194 /* CursorError.swift in Sources */,
671463991EB3396E00EAB194 /* AnyLoadingIndicator.swift in Sources */,
673CF4392063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */,
671463A81EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */,
671463151EB3396E00EAB194 /* UIViewController+TopVisibleViewController.swift in Sources */,
671462891EB3396E00EAB194 /* CGFloat+Pixels.swift in Sources */,
@ -2664,8 +2757,11 @@
677452A520625FA90024EEEF /* RxDataSource.swift in Sources */,
671462491EB3396E00EAB194 /* FixedPageCursor.swift in Sources */,
671462C91EB3396E00EAB194 /* String+Localization.swift in Sources */,
673CF43B2063E80B00C329F6 /* GeneralDataLoadingController.swift in Sources */,
673CF40C2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */,
671462B91EB3396E00EAB194 /* Sequence+ConcurrentMap.swift in Sources */,
671463751EB3396E00EAB194 /* BorderDrawingOperation.swift in Sources */,
673CF4122063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */,
6714633D1EB3396E00EAB194 /* LoadingIndicator.swift in Sources */,
671463191EB3396E00EAB194 /* UIWindow+Extensions.swift in Sources */,
EFBE57DC1EC361620040E00A /* UIView+Layout.swift in Sources */,

View File

@ -0,0 +1,101 @@
//
// 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 GeneralDataLoadingViewModel<ResultType>: BaseViewModel {
public typealias LoadingModel = GeneralDataLoadingModel<ResultType>
public typealias DataSourceType = Single<ResultType>
public typealias LoadingState = GeneralDataLoadingState<DataSourceType>
private let loadingModel: LoadingModel
private let loadingStateVariable = Variable<LoadingState>(.initial)
public let disposeBag = DisposeBag()
/// Initializer with single result sequence and empty result checker closure.
///
/// - Parameters:
/// - dataSource: A single element sequence.
/// - emptyResultChecker: Closure for checking result on empty state.
public init(dataSource: DataSourceType,
emptyResultChecker: @escaping LoadingModel.EmptyResultChecker = { _ in false }) {
loadingModel = LoadingModel(dataSource: dataSource, emptyResultChecker: emptyResultChecker)
loadingModel.stateDriver
.drive(loadingStateVariable)
.disposed(by: disposeBag)
loadingModel.reload()
}
/// Returns driver that emits current loading state
open var loadingStateDriver: Driver<LoadingState> {
return loadingStateVariable.asDriver()
}
/// By default returns true if loading state == .result.
open var hasContent: Bool {
return currentLoadingState.hasResult
}
/// Returns current result if it exists.
public var currentResult: ResultType? {
return currentLoadingState.currentResult
}
/// Current state of loading process.
private(set) public var currentLoadingState: LoadingState {
get {
return loadingStateVariable.value
}
set {
loadingStateVariable.value = newValue
}
}
/// Manually update loading state.
/// Should be used only in specific situations on your own risk!
///
/// - Parameter newState: New loading state.
public func updateStateManually(to newState: LoadingState) {
currentLoadingState = newState
}
/// Reload data.
public func reload() {
loadingModel.reload()
}
}
public extension GeneralDataLoadingViewModel where ResultType: Collection {
convenience init(dataSource: DataSourceType) {
self.init(dataSource: dataSource) { $0.isEmpty }
}
}

View File

@ -46,7 +46,8 @@ public final class PaginationDataLoadingModel<Cursor: ResettableRxDataSourceCurs
load(.reload)
}
override open func retry() {
/// Attempt to load data again.
open func retry() {
load(.retry)
}

View File

@ -47,20 +47,8 @@ open class RxDataLoadingModel<LoadingStateType: DataLoadingState>: DataLoadingMo
}
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)

View File

@ -0,0 +1,56 @@
//
// 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
internal final class TextPlaceholderView: UIView {
enum PlaceholderText: String {
case empty = "There is nothing here"
case error = "An error has occurred"
case loading = "Loading..."
case retry = "Retry"
case retryLoadMore = "Retry load more"
}
init(title: PlaceholderText) {
super.init(frame: .zero)
let label = UILabel()
label.text = title.rawValue
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,60 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
internal final class TextWithButtonPlaceholder: UIView {
typealias TapHandler = () -> Void
private let tapHandler: TapHandler
init(title: TextPlaceholderView.PlaceholderText,
buttonTitle: TextPlaceholderView.PlaceholderText,
tapHandler: @escaping TapHandler) {
self.tapHandler = tapHandler
super.init(frame: .zero)
let textPlaceholder = TextPlaceholderView(title: title)
let button = UIButton(type: .custom)
button.backgroundColor = .lightGray
button.setTitle(buttonTitle.rawValue, for: .normal)
button.addTarget(self, action: #selector(buttonDidTapped(_:)), for: .touchUpInside)
let stackView = UIStackView(arrangedSubviews: [textPlaceholder, button])
stackView.axis = .vertical
addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func buttonDidTapped(_ button: UIButton) {
tapHandler()
}
}

View File

@ -0,0 +1,91 @@
//
// 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
public extension GeneralDataLoadingController where Self: UIViewController {
// MARK: - DisposeBagHolder default implementation
var disposeBag: DisposeBag {
return viewModel.disposeBag
}
// MARK: - StatefulViewController default implementation
func hasContent() -> Bool {
return viewModel.hasContent
}
// MARK: - GeneralDataLoadingController default implementation
func initialLoadDataLoadingView() {
addViews()
setAppearance()
setupStateViews()
configureBarButtons()
localize()
bindViews()
bindLoadingState()
}
func setupStateViews() {
loadingView = TextPlaceholderView(title: .loading)
errorView = TextWithButtonPlaceholder(title: .error,
buttonTitle: .retryLoadMore,
tapHandler: reload)
emptyView = TextPlaceholderView(title: .empty)
}
func reload() {
viewModel.reload()
}
private var stateChanged: Binder<ViewModelT.LoadingState> {
return Binder(self) { base, value in
switch value {
case .loading:
base.onLoadingState()
case .result(let newResult, _):
base.onResultsState(result: newResult)
case .empty:
base.onEmptyState()
case .error(let error):
base.onErrorState(error: error)
case .initial:
break
}
}
}
}
public extension GeneralDataLoadingController where Self: UIViewController & DisposeBagHolder {
func bindLoadingState() {
viewModel.loadingStateDriver
.drive(stateChanged)
.disposed(by: disposeBag)
}
}

View File

@ -0,0 +1,45 @@
//
// 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 extension GeneralDataLoadingState {
/// Returns true if self == .result.
var hasResult: Bool {
switch self {
case .result:
return true
default:
return false
}
}
/// Returns result if state == .result. Otherwise returns nil.
var currentResult: DS.ResultType? {
switch self {
case .result(let newResult, _):
return newResult
default:
return nil
}
}
}

View File

@ -25,20 +25,7 @@ import UIKit
public extension PaginationWrapperDelegate {
func emptyPlaceholder() -> UIView {
let placeholder = UIView()
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "There is nothing here"
placeholder.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor),
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor)
])
return placeholder
return TextPlaceholderView(title: .empty)
}
func customInitialLoadingErrorHandling(for error: Error) -> Bool {
@ -46,20 +33,7 @@ public extension PaginationWrapperDelegate {
}
func errorPlaceholder(for error: Error) -> UIView {
let placeholder = UIView()
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "An error has occurred"
placeholder.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: placeholder.centerXAnchor),
label.centerYAnchor.constraint(equalTo: placeholder.centerYAnchor)
])
return placeholder
return TextPlaceholderView(title: .error)
}
func initialLoadingIndicator() -> AnyLoadingIndicator {

View File

@ -34,7 +34,4 @@ public protocol DataLoadingModel {
/// Perform (re)load data.
func reload()
/// Attempt to load data again.
func retry()
}

View File

@ -0,0 +1,63 @@
//
// 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 protocol GeneralDataLoadingController: class, ConfigurableController
where ViewModelT: GeneralDataLoadingViewModel<ViewModelResultType> {
associatedtype ViewModelResultType
/// The loading view is shown when the `onLoadingState` method gets called
var loadingView: UIView? { get set }
/// The error view is shown when the `onErrorState` method gets called
var errorView: UIView? { get set }
/// The empty view is shown when the `hasContent` method returns false
var emptyView: UIView? { get set }
/// Method for setting up error, empty and loading placeholders.
func setupStateViews()
/// Method for binding view model state driver to view controller.
func bindLoadingState()
/// Called when retry action performed.
func reload()
/// Shold be called in viewDidLoad() instead of initialLoadView().
func initialLoadDataLoadingView()
/// Called when data loading has started.
func onLoadingState()
/// Called when data loading has finished with non-empty result.
func onResultsState(result: ViewModelResultType)
/// Called when data loading did finished with empty result.
func onEmptyState()
/// Gets called when error is occured during data loading.
///
/// - Parameter error: An error that might have occurred whilst loading
func onErrorState(error: Error)
}

View File

@ -22,26 +22,26 @@
import UIKit
/// Protocol that contains scroll view property
/// Protocol that contains scroll view property.
public protocol ScrollViewHolder {
var scrollView: UIScrollView { get }
}
/// Protocol that contains background view property
/// Protocol that contains background view property.
public protocol BackgroundViewHolder {
var backgroundView: UIView? { get set }
}
/// Protocol that contains footer view property
/// Protocol that contains footer view property.
public protocol FooterViewHolder {
var footerView: UIView? { get set }
}
/// Protocol that conforms to ScrollViewHolder, BackgroundViewHolder and FooterViewHolder protocols
/// Protocol that conforms to ScrollViewHolder, BackgroundViewHolder and FooterViewHolder protocols.
public typealias PaginationWrappable = ScrollViewHolder & BackgroundViewHolder & FooterViewHolder

View File

@ -0,0 +1,30 @@
//
// 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 that contains dispose bag property.
public protocol DisposeBagHolder {
var disposeBag: DisposeBag { get }
}

View File

@ -1 +0,0 @@
code-quality/cpd_script.php