diff --git a/.swiftlint.yml b/.swiftlint.yml deleted file mode 120000 index 0add564e..00000000 --- a/.swiftlint.yml +++ /dev/null @@ -1 +0,0 @@ -code-quality/.swiftlint.yml \ No newline at end of file diff --git a/.tailor.yml b/.tailor.yml deleted file mode 120000 index 73afece8..00000000 --- a/.tailor.yml +++ /dev/null @@ -1 +0,0 @@ -code-quality/.tailor.yml \ No newline at end of file diff --git a/LeadKit.podspec b/LeadKit.podspec index 5908a6f1..0c02a36d 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -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", diff --git a/LeadKit.xcodeproj/project.pbxproj b/LeadKit.xcodeproj/project.pbxproj index 0d830e52..56fe6894 100644 --- a/LeadKit.xcodeproj/project.pbxproj +++ b/LeadKit.xcodeproj/project.pbxproj @@ -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 = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 67186C1A1EB24B7800CFAFFB /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; + 673CF40A2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingViewModel.swift; sourceTree = ""; }; + 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GeneralDataLoadingState+Extensions.swift"; sourceTree = ""; }; + 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralDataLoadingController.swift; sourceTree = ""; }; + 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposeBagHolder.swift; sourceTree = ""; }; + 673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextPlaceholderView.swift; sourceTree = ""; }; + 673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextWithButtonPlaceholder.swift; sourceTree = ""; }; + 673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GeneralDataLoadingController+DefaultImplementation.swift"; sourceTree = ""; }; 674AF55B1EC45B1600038A8F /* UIActivityIndicatorView+LoadingIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActivityIndicatorView+LoadingIndicator.swift"; sourceTree = ""; }; 6771DFD71EE99EBA002DCDAE /* DateFormattingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingService.swift; sourceTree = ""; }; 6771DFDD1EE99F6F002DCDAE /* DateFormattingArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormattingArguments.swift; sourceTree = ""; }; @@ -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 = ""; }; + 673CF4152063AC0000C329F6 /* GeneralDataLoading */ = { + isa = PBXGroup; + children = ( + 673CF4372063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift */, + 673CF4102063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift */, + ); + path = GeneralDataLoading; + sourceTree = ""; + }; + 673CF4202063D8EB00C329F6 /* Rx */ = { + isa = PBXGroup; + children = ( + 673CF4212063D90600C329F6 /* DisposeBagHolder.swift */, + ); + path = Rx; + sourceTree = ""; + }; + 673CF42A2063DE3A00C329F6 /* DefaultPlaceholders */ = { + isa = PBXGroup; + children = ( + 673CF42B2063DE5900C329F6 /* TextPlaceholderView.swift */, + 673CF4332063E29B00C329F6 /* TextWithButtonPlaceholder.swift */, + ); + path = DefaultPlaceholders; + sourceTree = ""; + }; + 673CF43D2063E81200C329F6 /* GeneralDataLoading */ = { + isa = PBXGroup; + children = ( + 673CF4172063D50700C329F6 /* GeneralDataLoadingController.swift */, + ); + path = GeneralDataLoading; + sourceTree = ""; + }; 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 = ""; @@ -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 */, diff --git a/Sources/Classes/DataLoading/GeneralDataLoading/GeneralDataLoadingViewModel.swift b/Sources/Classes/DataLoading/GeneralDataLoading/GeneralDataLoadingViewModel.swift new file mode 100644 index 00000000..646095f3 --- /dev/null +++ b/Sources/Classes/DataLoading/GeneralDataLoading/GeneralDataLoadingViewModel.swift @@ -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: BaseViewModel { + + public typealias LoadingModel = GeneralDataLoadingModel + public typealias DataSourceType = Single + public typealias LoadingState = GeneralDataLoadingState + + private let loadingModel: LoadingModel + + private let loadingStateVariable = Variable(.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 { + 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 } + } + +} diff --git a/Sources/Classes/DataLoading/PaginationDataLoading/PaginationDataLoadingModel.swift b/Sources/Classes/DataLoading/PaginationDataLoading/PaginationDataLoadingModel.swift index dc513b07..03627cf9 100644 --- a/Sources/Classes/DataLoading/PaginationDataLoading/PaginationDataLoadingModel.swift +++ b/Sources/Classes/DataLoading/PaginationDataLoading/PaginationDataLoadingModel.swift @@ -46,7 +46,8 @@ public final class PaginationDataLoadingModel: 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) diff --git a/Sources/Classes/Views/DefaultPlaceholders/TextPlaceholderView.swift b/Sources/Classes/Views/DefaultPlaceholders/TextPlaceholderView.swift new file mode 100644 index 00000000..f0715b76 --- /dev/null +++ b/Sources/Classes/Views/DefaultPlaceholders/TextPlaceholderView.swift @@ -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") + } + +} diff --git a/Sources/Classes/Views/DefaultPlaceholders/TextWithButtonPlaceholder.swift b/Sources/Classes/Views/DefaultPlaceholders/TextWithButtonPlaceholder.swift new file mode 100644 index 00000000..4ac62d2b --- /dev/null +++ b/Sources/Classes/Views/DefaultPlaceholders/TextWithButtonPlaceholder.swift @@ -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() + } + +} diff --git a/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift b/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift new file mode 100644 index 00000000..7a970932 --- /dev/null +++ b/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift @@ -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 { + 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) + } + +} diff --git a/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingState+Extensions.swift b/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingState+Extensions.swift new file mode 100644 index 00000000..01eda183 --- /dev/null +++ b/Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingState+Extensions.swift @@ -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 + } + } + +} diff --git a/Sources/Extensions/DataLoading/PaginationDataLoading/PaginationWrapperDelegate+DefaultImplementation.swift b/Sources/Extensions/DataLoading/PaginationDataLoading/PaginationWrapperDelegate+DefaultImplementation.swift index 6b5c5ac4..3f5da223 100644 --- a/Sources/Extensions/DataLoading/PaginationDataLoading/PaginationWrapperDelegate+DefaultImplementation.swift +++ b/Sources/Extensions/DataLoading/PaginationDataLoading/PaginationWrapperDelegate+DefaultImplementation.swift @@ -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 { diff --git a/Sources/Protocols/DataLoading/DataLoadingModel.swift b/Sources/Protocols/DataLoading/DataLoadingModel.swift index a55496ff..9a0bf8a1 100644 --- a/Sources/Protocols/DataLoading/DataLoadingModel.swift +++ b/Sources/Protocols/DataLoading/DataLoadingModel.swift @@ -34,7 +34,4 @@ public protocol DataLoadingModel { /// Perform (re)load data. func reload() - /// Attempt to load data again. - func retry() - } diff --git a/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift b/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift new file mode 100644 index 00000000..fdf48f87 --- /dev/null +++ b/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift @@ -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 { + + 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) + +} diff --git a/Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift b/Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift index e68f85db..250d0bb3 100644 --- a/Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift +++ b/Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift @@ -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 diff --git a/Sources/Protocols/Rx/DisposeBagHolder.swift b/Sources/Protocols/Rx/DisposeBagHolder.swift new file mode 100644 index 00000000..69c514d9 --- /dev/null +++ b/Sources/Protocols/Rx/DisposeBagHolder.swift @@ -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 } + +} diff --git a/cpd_script.php b/cpd_script.php deleted file mode 120000 index 120866ca..00000000 --- a/cpd_script.php +++ /dev/null @@ -1 +0,0 @@ -code-quality/cpd_script.php \ No newline at end of file