diff --git a/.swift-version b/.swift-version index bf77d549..819e07a2 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index be220e12..ae8103ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,40 @@ # Changelog -### 0.9.9 +### 0.9.15 - **Add**: `BaseSearchViewController` class that that allows to enter text for search and then displays search results in table view. - **Add**: `BaseSearchViewModel` class that loads data from a given data source and performs search among the results. - **Add**: `SearchResultsController` protocol that represent a controller able to display search results. - **Add**: `SearchResultsControllerState` enum that represents `SearchResultsController` state. +### 0.9.14 +- **Update**: SwiftDate dependency (~> 6). + +### 0.9.13 +- **Add**: `ApiUploadRequestParameters` struct that defines upload data request parameters. +- **Add**: `rxUploadRequest` method to `NetworkService` class that performs reactive request to upload data. +- **Add**: `uploadResponseModel` method to `SessionManager` extension that executes upload request and serializes response. +- **Add**: `handleMappingError` method to `Error` extension that tries to serialize response from a mapping request error to a model. +- **Add**: `handleMappingError` method to `ObservableType`, `Single`, `Completable` extensions that handles a mapping error and serialize response to a model. +- **Add**: `validate` method to `DataRequest` observable extension that validates status codes and catch network errors. +- **Add**: `dataApiResponse` method to `DataRequest` reactive extension that serializes response into data. +- **Update**: `validStatusCodes` parameter in network methods renamed to `additionalValidStatusCodes`. + +### 0.9.12 +- **Update**: Swift 5 support + +### 0.9.11 +- **[Breaking change]**: Renamed `NumberFormat`'s `allOptions` to `allCases` +- **Fix**: Closure syntax fix. New closure naming. +- **Fix**: Added missing `BasePlaceholderView` protocol function. + +### 0.9.10 +- **Remove**: Removed unused scheme & target +- **Remove**: Cocoapods deintegrated +- **Update**: New closure typealiases + +### 0.9.9 +- **Add**: `validStatusCodes` parameter to request methods in `NetworkService` class, that expands valid status codes for request. +- **Add**: `validStatusCodes` parameter to response methods in `SessionManager` extension, that expands valid status codes for request. + ### 0.9.8 - **Add**: `rxDataRequest` method to `NetworkService` class, that performs reactive request to get data and http response. - **Add**: `responseData` method to `SessionManager` extension, that executes request and returns data. @@ -42,7 +72,7 @@ ### 0.8.13 - **Add**: `configureLayout` method to `InitializeableView` protocol and all implementations. - **Update**: `GeneralDataLoadingViewModel` now can handle state changes and result of data source. Previously it was possible only in view controller. -- **Add**: `GeneralDataLoadingHandler` protocol, that defines methods for common data loading states handling. +- **Add**: `GeneralDataLoadingHandler` protocol, that defines methods for common data loading states handling. - **Add**: `resultObservable` and `resultDriver` properties to `GeneralDataLoadingViewModel`. - **Add**: `hidesWhenStopped` option to `SpinnerView`, so you can stop animation without hiding image inside it. - **Update**: Migrate to Swift 4.2 & Xcode 10. Update dependencies. @@ -93,12 +123,12 @@ ### 0.8.5 -- **Add**: `replaceDataSource` method to `RxNetworkOperationModel`. +- **Add**: `replaceDataSource` method to `RxNetworkOperationModel`. - **Add**: `customErrorHandler` constructor parameter to `RxNetworkOperationModel` and it heirs. ### 0.8.4 -- **Fix**: Add `SeparatorCell` to `Core-iOS-Extension`. +- **Fix**: Add `SeparatorCell` to `Core-iOS-Extension`. - **Fix**: `UIApplication` extensions path for `Core-iOS-Extension` exclusions. ### 0.8.3 diff --git a/Cartfile b/Cartfile index affbd483..d37d2537 100644 --- a/Cartfile +++ b/Cartfile @@ -1,6 +1,6 @@ binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/SwiftDate/SwiftDate.json" -github "ReactiveX/RxSwift" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/Alamofire/Alamofire.json" binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxAlamofire/RxAlamofire.json" -github "maxsokolov/TableKit" -github "pronebird/UIScrollView-InfiniteScroll" -gitHub "Alamofire/Alamofire" "4.8.1" \ No newline at end of file +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/TableKit/TableKit.json" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/UIScrollView_InfiniteScroll/UIScrollView_InfiniteScroll.json" diff --git a/Cartfile.resolved b/Cartfile.resolved index 99f56d6f..a22b45ff 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,6 +1,6 @@ -binary "https://raw.github.com/petropavel13/CarthageBinaries/master/RxAlamofire/RxAlamofire.json" "4.3.0" -binary "https://raw.github.com/petropavel13/CarthageBinaries/master/SwiftDate/SwiftDate.json" "5.1.0" -github "Alamofire/Alamofire" "4.8.1" -github "ReactiveX/RxSwift" "4.4.1" -github "maxsokolov/TableKit" "2.8.1" -github "pronebird/UIScrollView-InfiniteScroll" "1.1.0" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/Alamofire/Alamofire.json" "4.8.1" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxAlamofire/RxAlamofire.json" "4.3.0" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json" "4.4.2" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/SwiftDate/SwiftDate.json" "6.0.1" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/TableKit/TableKit.json" "2.10008.1" +binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/UIScrollView_InfiniteScroll/UIScrollView_InfiniteScroll.json" "1.1.0" diff --git a/LeadKit.podspec b/LeadKit.podspec index 7df7c6fb..909fde5d 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.9.9" + s.version = "0.9.15" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" @@ -88,7 +88,7 @@ Pod::Spec.new do |s| ss.dependency "RxSwift", '~> 4' ss.dependency "RxCocoa", '~> 4' ss.dependency "RxAlamofire", '~> 4' - ss.dependency "SwiftDate", '~> 5.1' + ss.dependency "SwiftDate", '~> 6' ss.ios.dependency "TableKit", '~> 2.8' ss.ios.dependency "UIScrollView-InfiniteScroll", '~> 1.1.0' diff --git a/LeadKit.xcodeproj/project.pbxproj b/LeadKit.xcodeproj/project.pbxproj index 17ed277d..dc42048a 100644 --- a/LeadKit.xcodeproj/project.pbxproj +++ b/LeadKit.xcodeproj/project.pbxproj @@ -182,29 +182,13 @@ 671463A51EB33FF600EAB194 /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463A11EB33FF600EAB194 /* Animatable.swift */; }; 671463A71EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463A61EB340C000EAB194 /* UIViewController+ConfigurableController.swift */; }; 671463A91EB340C000EAB194 /* UIViewController+ConfigurableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463A61EB340C000EAB194 /* UIViewController+ConfigurableController.swift */; }; - 671463B81EB34B1E00EAB194 /* StubCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463AE1EB34B1E00EAB194 /* StubCursor.swift */; }; - 671463BA1EB34B1E00EAB194 /* StubCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463AE1EB34B1E00EAB194 /* StubCursor.swift */; }; - 671463BB1EB34B1E00EAB194 /* CursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463AF1EB34B1E00EAB194 /* CursorTests.swift */; }; - 671463BD1EB34B1E00EAB194 /* CursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463AF1EB34B1E00EAB194 /* CursorTests.swift */; }; - 671463BE1EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B01EB34B1E00EAB194 /* LoadFromNibTests.swift */; }; - 671463C01EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B01EB34B1E00EAB194 /* LoadFromNibTests.swift */; }; - 671463C41EB34B1E00EAB194 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B31EB34B1E00EAB194 /* Post.swift */; }; - 671463C61EB34B1E00EAB194 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B31EB34B1E00EAB194 /* Post.swift */; }; - 671463CA1EB34B1E00EAB194 /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B61EB34B1E00EAB194 /* TestView.swift */; }; - 671463CC1EB34B1E00EAB194 /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671463B61EB34B1E00EAB194 /* TestView.swift */; }; - 671463CD1EB34B1E00EAB194 /* TestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671463B71EB34B1E00EAB194 /* TestView.xib */; }; - 671463CF1EB34B1E00EAB194 /* TestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671463B71EB34B1E00EAB194 /* TestView.xib */; }; 67153E3A207DFADA0049D8C0 /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E39207DFADA0049D8C0 /* RotateDrawingOperation.swift */; }; 67153E3C207DFADA0049D8C0 /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E39207DFADA0049D8C0 /* RotateDrawingOperation.swift */; }; 67153E3D207DFADA0049D8C0 /* RotateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E39207DFADA0049D8C0 /* RotateDrawingOperation.swift */; }; 67153E40207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E3F207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift */; }; 67153E42207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E3F207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift */; }; 67153E43207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67153E3F207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift */; }; - 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, ); }; }; - 671AD25C206A343300EAF887 /* VoidBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD25B206A343300EAF887 /* VoidBlock.swift */; }; - 671AD25E206A343300EAF887 /* VoidBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD25B206A343300EAF887 /* VoidBlock.swift */; }; - 671AD25F206A343300EAF887 /* VoidBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD25B206A343300EAF887 /* VoidBlock.swift */; }; 671AD26C206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD26B206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift */; }; 671AD26E206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD26B206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift */; }; 671AD26F206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AD26B206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift */; }; @@ -356,7 +340,6 @@ 677B06C221187559006C947D /* ViewTextConfigurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677B06BE21187559006C947D /* ViewTextConfigurable.swift */; }; 677B06C4211884F3006C947D /* BaseTextAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677B06C3211884F3006C947D /* BaseTextAttributes.swift */; }; 677B06C7211884F3006C947D /* BaseTextAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 677B06C3211884F3006C947D /* BaseTextAttributes.swift */; }; - 6782BBA91EB31D5A0086E0B8 /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6782BBA01EB31D590086E0B8 /* LeadKit.framework */; }; 678D267920691D8200B05B93 /* DataModelFieldBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D267820691D8200B05B93 /* DataModelFieldBinding.swift */; }; 678D267B20691D8200B05B93 /* DataModelFieldBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D267820691D8200B05B93 /* DataModelFieldBinding.swift */; }; 678D267C20691D8200B05B93 /* DataModelFieldBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D267820691D8200B05B93 /* DataModelFieldBinding.swift */; }; @@ -470,16 +453,13 @@ 6B5B64BACFF8C5487FB0939D /* TableKitViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B66503F2C42D009DEA011 /* TableKitViewModel.swift */; }; 6B5B6EF1577C8CC06E4CCF1B /* Array+RowExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B62E7942E5AEE68A95449 /* Array+RowExtensions.swift */; }; 6B5B6F0BFA22832C47142BAD /* TableKitViewModel+Extenstions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B61443DDAB82927448CAA /* TableKitViewModel+Extenstions.swift */; }; - 72039CE0220899E600875DD4 /* SearchResultsViewControllerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72039CDF220899E600875DD4 /* SearchResultsViewControllerState.swift */; }; - 72039CE122089A3D00875DD4 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7284087522079A4600A20F47 /* SearchResultsViewController.swift */; }; - 72039CE222089A3D00875DD4 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7284087522079A4600A20F47 /* SearchResultsViewController.swift */; }; - 72527D27222E934100CA26BE /* OptionalType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72527D26222E934100CA26BE /* OptionalType.swift */; }; - 7284087422078EB800A20F47 /* BaseSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7284087322078EB800A20F47 /* BaseSearchViewModel.swift */; }; - 7284087622079A4600A20F47 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7284087522079A4600A20F47 /* SearchResultsViewController.swift */; }; 7295473F21E661E6009558E7 /* TitleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7295473E21E661E6009558E7 /* TitleType.swift */; }; 7295474221E6628C009558E7 /* UINavigationItem+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7295474121E6628C009558E7 /* UINavigationItem+Support.swift */; }; 7295474421E66328009558E7 /* UIViewController+UpdateNavigationItemTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7295474321E66328009558E7 /* UIViewController+UpdateNavigationItemTitle.swift */; }; - 72D213A422048180003B4292 /* BaseSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72D213A322048180003B4292 /* BaseSearchViewController.swift */; }; + 72AECC6B224A979D00D12E7C /* BaseSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72AECC69224A979D00D12E7C /* BaseSearchViewController.swift */; }; + 72AECC6C224A979D00D12E7C /* BaseSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72AECC6A224A979D00D12E7C /* BaseSearchViewModel.swift */; }; + 72AECC6F224A97B100D12E7C /* SearchResultsViewControllerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72AECC6E224A97B100D12E7C /* SearchResultsViewControllerState.swift */; }; + 72AECC71224A97F100D12E7C /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72AECC70224A97F000D12E7C /* SearchResultsViewController.swift */; }; 785EDF7C220072B500985ED4 /* SwiftDate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 785EDF76220072B400985ED4 /* SwiftDate.framework */; }; 785EDF7D220072B500985ED4 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 785EDF77220072B400985ED4 /* RxCocoa.framework */; }; 785EDF7E220072B500985ED4 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 785EDF78220072B500985ED4 /* Alamofire.framework */; }; @@ -504,7 +484,16 @@ 78EC7B1522019F5A0007DCFD /* String+TelpromptURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC7B1222019F5A0007DCFD /* String+TelpromptURL.swift */; }; 820CAD8420B43B080033EF94 /* PaginationWrapperDelegate+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820CAD8320B43B080033EF94 /* PaginationWrapperDelegate+DefaultImplementation.swift */; }; 825F8F2820B3384C00594857 /* PaginationWrapperUIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825F8F2720B3384C00594857 /* PaginationWrapperUIDelegate.swift */; }; + 82B4F8DB223903B800F6708C /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82B4F8DA223903B800F6708C /* Block.swift */; }; + 82B4F8DC223903B800F6708C /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82B4F8DA223903B800F6708C /* Block.swift */; }; + 82B4F8DD223903B800F6708C /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82B4F8DA223903B800F6708C /* Block.swift */; }; 82F8BB181F5DDED100C1061B /* Single+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */; }; + 8546C2E3224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E2224E86280059C255 /* ApiUploadRequestParameters.swift */; }; + 8546C2E4224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E2224E86280059C255 /* ApiUploadRequestParameters.swift */; }; + 8546C2E5224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E2224E86280059C255 /* ApiUploadRequestParameters.swift */; }; + 8546C2E8224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E7224E864F0059C255 /* Error+NetworkExtensions.swift */; }; + 8546C2E9224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E7224E864F0059C255 /* Error+NetworkExtensions.swift */; }; + 8546C2EA224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546C2E7224E864F0059C255 /* Error+NetworkExtensions.swift */; }; A658E54D1F8CD7790093527A /* TableRow+SeparatorsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54C1F8CD7790093527A /* TableRow+SeparatorsExtensions.swift */; }; A658E5501F8CD9350093527A /* Array+SeparatorRowBoxExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A658E54F1F8CD9350093527A /* Array+SeparatorRowBoxExtensions.swift */; }; A676AE481F97D28A001F9214 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676AE471F97D28A001F9214 /* String+Extensions.swift */; }; @@ -517,8 +506,6 @@ A676AE571F981130001F9214 /* ObservableMappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676AE541F981121001F9214 /* ObservableMappable.swift */; }; A676AE581F981131001F9214 /* ObservableMappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676AE541F981121001F9214 /* ObservableMappable.swift */; }; A6C9A4FA1F8BBCF2009311CC /* EmptyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6AF3B371F8B956F00CDB971 /* EmptyCell.swift */; }; - A6C9A5051F8BC78F009311CC /* SeparatorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E0DDF01F8A6C80002CA74E /* SeparatorConfiguration.swift */; }; - A6C9A50F1F8BC79D009311CC /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; A6D10EAB1F8A9278003E69DD /* Comparable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D10EAA1F8A9278003E69DD /* Comparable+Extensions.swift */; }; A6E0DDDE1F8A696F002CA74E /* EmptyCellRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A71F8A654600C6308D /* EmptyCellRow.swift */; }; A6E0DDDF1F8A696F002CA74E /* SeparatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A66428A61F8A653600C6308D /* SeparatorCell.swift */; }; @@ -527,8 +514,6 @@ A6F32C081F6EBDAA00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; A6F32C0B1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; A6F32C0C1F6EBE5C00AC08EE /* String+LocalizedComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C071F6EBDAA00AC08EE /* String+LocalizedComponent.swift */; }; - A6F32C101F6EBE9600AC08EE /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C0E1F6EBE8E00AC08EE /* StringExtensionTests.swift */; }; - A6F32C121F6EBE9800AC08EE /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F32C0E1F6EBE8E00AC08EE /* StringExtensionTests.swift */; }; B84CB06A20B702240090DB91 /* Encodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B768620B1CF6700F837C4 /* Encodable+Extensions.swift */; }; B84CB06B20B702260090DB91 /* Encodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B768620B1CF6700F837C4 /* Encodable+Extensions.swift */; }; B84CB06D20B8325D0090DB91 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84CB06C20B8325D0090DB91 /* SessionManager.swift */; }; @@ -537,15 +522,7 @@ B84CB07820B872AD0090DB91 /* Decodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84CB07720B872AD0090DB91 /* Decodable+Extensions.swift */; }; B84CB07A20B872AD0090DB91 /* Decodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84CB07720B872AD0090DB91 /* Decodable+Extensions.swift */; }; B84CB07B20B872AD0090DB91 /* Decodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84CB07720B872AD0090DB91 /* Decodable+Extensions.swift */; }; - B84D64B120A70B7000DD76DA /* NetworkServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84D64B020A70B7000DD76DA /* NetworkServiceTests.swift */; }; - B84D64B320A70B7000DD76DA /* NetworkServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84D64B020A70B7000DD76DA /* NetworkServiceTests.swift */; }; - B85B766820AC4EC600F837C4 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B766620AC4EA300F837C4 /* Album.swift */; }; - B85B766A20AC4EC700F837C4 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B766620AC4EA300F837C4 /* Album.swift */; }; - B85B766D20AC51C600F837C4 /* AlbumsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B766B20AC51BE00F837C4 /* AlbumsContainer.swift */; }; - B85B766F20AC51C700F837C4 /* AlbumsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B766B20AC51BE00F837C4 /* AlbumsContainer.swift */; }; B85B768720B1CF6700F837C4 /* Encodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85B768620B1CF6700F837C4 /* Encodable+Extensions.swift */; }; - D93221EE20A44896003799D5 /* Double+RoundingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93221ED20A44896003799D5 /* Double+RoundingTests.swift */; }; - D93221F020A44896003799D5 /* Double+RoundingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93221ED20A44896003799D5 /* Double+RoundingTests.swift */; }; EF24213A2076D5BD00FA9BE6 /* NetworkServiceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF2421392076D5BD00FA9BE6 /* NetworkServiceConfiguration.swift */; }; EF24213C2076D5C900FA9BE6 /* NetworkServiceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF2421392076D5BD00FA9BE6 /* NetworkServiceConfiguration.swift */; }; EF24213D2076D5CA00FA9BE6 /* NetworkServiceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF2421392076D5BD00FA9BE6 /* NetworkServiceConfiguration.swift */; }; @@ -556,23 +533,6 @@ EFBE57DE1EC361620040E00A /* UIView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFBE57DA1EC361620040E00A /* UIView+Layout.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 67186B321EB248F100CFAFFB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78CFEE211C5C456B00F50370 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 67186B271EB248F100CFAFFB; - remoteInfo = "LeadKit iOS"; - }; - 6782BBAA1EB31D5A0086E0B8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78CFEE211C5C456B00F50370 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6782BB9F1EB31D590086E0B8; - remoteInfo = "LeadKit tvOS"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ 36DAAF502007CC920090BE0D /* UITableView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = ""; }; 36FE776F20F669E300284C09 /* String+ConvertToHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+ConvertToHost.swift"; sourceTree = ""; }; @@ -651,10 +611,8 @@ 67153E3F207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+DegreesRadiansConvertion.swift"; sourceTree = ""; }; 67186B201EB247A200CFAFFB /* LeadKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LeadKit.h; sourceTree = ""; }; 67186B281EB248F100CFAFFB /* LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 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 = ""; }; - 671AD25B206A343300EAF887 /* VoidBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoidBlock.swift; sourceTree = ""; }; 671AD26B206A3E8500EAF887 /* Array+TotalCountCursorListingResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+TotalCountCursorListingResult.swift"; sourceTree = ""; }; 67274768206CCC9D00725163 /* ViewBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewBackground.swift; sourceTree = ""; }; 6727476D206CCDDB00725163 /* ViewBackground+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewBackground+Configuration.swift"; sourceTree = ""; }; @@ -716,7 +674,6 @@ 677B06C3211884F3006C947D /* BaseTextAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTextAttributes.swift; sourceTree = ""; }; 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; }; 6782BBB91EB31DD90086E0B8 /* Info-tvOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; }; 678D267820691D8200B05B93 /* DataModelFieldBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModelFieldBinding.swift; sourceTree = ""; }; 678D269E20692BFF00B05B93 /* TextFieldViewEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldViewEvents.swift; sourceTree = ""; }; @@ -726,7 +683,6 @@ 67952C391EB3203F00B3BA1A /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; 67952C3A1EB3205D00B3BA1A /* Info-watchOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-watchOS.plist"; sourceTree = ""; }; 67952C3B1EB3208000B3BA1A /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; }; - 67952DDC1EB3280900B3BA1A /* Info-iOS-Extensions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS-Extensions.plist"; sourceTree = ""; }; 67952DDE1EB3285A00B3BA1A /* Info-iOS-Extensions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS-Extensions.plist"; sourceTree = ""; }; 67955D51206D216B0021ECD2 /* Singleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Singleton.swift; sourceTree = ""; }; 67990AC4213EA4DB0040D195 /* PlaceholderConfigurable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderConfigurable.swift; sourceTree = ""; }; @@ -770,14 +726,13 @@ 6B5B61443DDAB82927448CAA /* TableKitViewModel+Extenstions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TableKitViewModel+Extenstions.swift"; sourceTree = ""; }; 6B5B62E7942E5AEE68A95449 /* Array+RowExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+RowExtensions.swift"; sourceTree = ""; }; 6B5B66503F2C42D009DEA011 /* TableKitViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableKitViewModel.swift; sourceTree = ""; }; - 72039CDF220899E600875DD4 /* SearchResultsViewControllerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsViewControllerState.swift; sourceTree = ""; }; - 72527D26222E934100CA26BE /* OptionalType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalType.swift; sourceTree = ""; }; - 7284087322078EB800A20F47 /* BaseSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseSearchViewModel.swift; sourceTree = ""; }; - 7284087522079A4600A20F47 /* SearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsViewController.swift; sourceTree = ""; }; 7295473E21E661E6009558E7 /* TitleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleType.swift; sourceTree = ""; }; 7295474121E6628C009558E7 /* UINavigationItem+Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Support.swift"; sourceTree = ""; }; 7295474321E66328009558E7 /* UIViewController+UpdateNavigationItemTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+UpdateNavigationItemTitle.swift"; sourceTree = ""; }; - 72D213A322048180003B4292 /* BaseSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseSearchViewController.swift; sourceTree = ""; }; + 72AECC69224A979D00D12E7C /* BaseSearchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseSearchViewController.swift; sourceTree = ""; }; + 72AECC6A224A979D00D12E7C /* BaseSearchViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseSearchViewModel.swift; sourceTree = ""; }; + 72AECC6E224A97B100D12E7C /* SearchResultsViewControllerState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchResultsViewControllerState.swift; sourceTree = ""; }; + 72AECC70224A97F000D12E7C /* SearchResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchResultsViewController.swift; sourceTree = ""; }; 785EDF76220072B400985ED4 /* SwiftDate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftDate.framework; path = Carthage/Build/iOS/SwiftDate.framework; sourceTree = ""; }; 785EDF77220072B400985ED4 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 785EDF78220072B500985ED4 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; @@ -801,7 +756,10 @@ 78EC7B1222019F5A0007DCFD /* String+TelpromptURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+TelpromptURL.swift"; sourceTree = ""; }; 820CAD8320B43B080033EF94 /* PaginationWrapperDelegate+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaginationWrapperDelegate+DefaultImplementation.swift"; sourceTree = ""; }; 825F8F2720B3384C00594857 /* PaginationWrapperUIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationWrapperUIDelegate.swift; sourceTree = ""; }; + 82B4F8DA223903B800F6708C /* Block.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Block.swift; sourceTree = ""; }; 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Single+DeferredJust.swift"; sourceTree = ""; }; + 8546C2E2224E86280059C255 /* ApiUploadRequestParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiUploadRequestParameters.swift; sourceTree = ""; }; + 8546C2E7224E864F0059C255 /* Error+NetworkExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Error+NetworkExtensions.swift"; sourceTree = ""; }; A658E54C1F8CD7790093527A /* TableRow+SeparatorsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TableRow+SeparatorsExtensions.swift"; sourceTree = ""; }; A658E54F1F8CD9350093527A /* Array+SeparatorRowBoxExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SeparatorRowBoxExtensions.swift"; sourceTree = ""; }; A66428A61F8A653600C6308D /* SeparatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorCell.swift; sourceTree = ""; }; @@ -843,14 +801,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 67186B2D1EB248F100CFAFFB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 67186B311EB248F100CFAFFB /* LeadKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 6782BB8D1EB31CFE0086E0B8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -876,14 +826,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 6782BBA51EB31D5A0086E0B8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6782BBA91EB31D5A0086E0B8 /* LeadKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -900,6 +842,7 @@ isa = PBXGroup; children = ( 6741CEC520E2434900FEC4D9 /* Controllers */, + 72AECC68224A979D00D12E7C /* Search */, 6774527E2062566D0024EEEF /* DataLoading */, 72D213A222048162003B4292 /* Search */, 671461D21EB3396E00EAB194 /* Services */, @@ -961,6 +904,7 @@ children = ( 72039CDE220899D000875DD4 /* Search */, 67274767206CCB6F00725163 /* Views */, + 72AECC6D224A97B100D12E7C /* Search */, 6774528A20625C860024EEEF /* DataLoading */, 671461D81EB3396E00EAB194 /* LeadKitError.swift */, 671461D91EB3396E00EAB194 /* ResizeMode.swift */, @@ -984,6 +928,7 @@ 6771DFE81EEA7C8F002DCDAE /* DateFormattingService */, 671461EA1EB3396E00EAB194 /* Double */, 672947E2206EA59E00AC6B6B /* Drawing */, + 8546C2E6224E864F0059C255 /* Error */, 67153E3E207DFB980049D8C0 /* FloatingPoint */, 676B22A0206A6249002E9F8A /* NSAttributedString */, 673564EF2068C29100F0CBED /* NumberFormattingService */, @@ -1162,7 +1107,7 @@ A676AE4C1F9810C1001F9214 /* Any+Cast.swift */, 671462211EB3396E00EAB194 /* Any+TypeName.swift */, 67FD4381206BD24B005B0C64 /* EqutableOptionalArray.swift */, - 671AD25B206A343300EAF887 /* VoidBlock.swift */, + 82B4F8DA223903B800F6708C /* Block.swift */, ); path = Functions; sourceTree = ""; @@ -1214,6 +1159,7 @@ isa = PBXGroup; children = ( 671462371EB3396E00EAB194 /* ApiRequestParameters.swift */, + 8546C2E2224E86280059C255 /* ApiUploadRequestParameters.swift */, ); path = Api; sourceTree = ""; @@ -1284,7 +1230,6 @@ 671461D61EB3396E00EAB194 /* Enums */, 671461DA1EB3396E00EAB194 /* Extensions */, 6714621F1EB3396E00EAB194 /* Functions */, - 67952DDC1EB3280900B3BA1A /* Info-iOS-Extensions.plist */, 67952C391EB3203F00B3BA1A /* Info-iOS.plist */, 67952C3B1EB3208000B3BA1A /* Info-tvOS.plist */, 67952C3A1EB3205D00B3BA1A /* Info-watchOS.plist */, @@ -1476,7 +1421,7 @@ isa = PBXGroup; children = ( 671462241EB3396E00EAB194 /* ConfigurableController.swift */, - 7284087522079A4600A20F47 /* SearchResultsViewController.swift */, + 72AECC70224A97F000D12E7C /* SearchResultsViewController.swift */, ); path = Controllers; sourceTree = ""; @@ -1957,19 +1902,19 @@ path = TableKitViewModel; sourceTree = ""; }; - 72039CDE220899D000875DD4 /* Search */ = { + 72AECC68224A979D00D12E7C /* Search */ = { isa = PBXGroup; children = ( - 72039CDF220899E600875DD4 /* SearchResultsViewControllerState.swift */, + 72AECC69224A979D00D12E7C /* BaseSearchViewController.swift */, + 72AECC6A224A979D00D12E7C /* BaseSearchViewModel.swift */, ); path = Search; sourceTree = ""; }; - 72D213A222048162003B4292 /* Search */ = { + 72AECC6D224A97B100D12E7C /* Search */ = { isa = PBXGroup; children = ( - 72D213A322048180003B4292 /* BaseSearchViewController.swift */, - 7284087322078EB800A20F47 /* BaseSearchViewModel.swift */, + 72AECC6E224A97B100D12E7C /* SearchResultsViewControllerState.swift */, ); path = Search; sourceTree = ""; @@ -2016,10 +1961,8 @@ isa = PBXGroup; children = ( 67186B281EB248F100CFAFFB /* LeadKit.framework */, - 67186B301EB248F100CFAFFB /* LeadKit iOSTests.xctest */, 6782BB911EB31CFE0086E0B8 /* LeadKit.framework */, 6782BBA01EB31D590086E0B8 /* LeadKit.framework */, - 6782BBA81EB31D5A0086E0B8 /* LeadKit tvOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -2033,6 +1976,14 @@ path = Single; sourceTree = ""; }; + 8546C2E6224E864F0059C255 /* Error */ = { + isa = PBXGroup; + children = ( + 8546C2E7224E864F0059C255 /* Error+NetworkExtensions.swift */, + ); + path = Error; + sourceTree = ""; + }; A66428A41F8A651700C6308D /* SeparatorCell */ = { isa = PBXGroup; children = ( @@ -2170,24 +2121,6 @@ productReference = 67186B281EB248F100CFAFFB /* LeadKit.framework */; productType = "com.apple.product-type.framework"; }; - 67186B2F1EB248F100CFAFFB /* LeadKit iOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 67186B3C1EB248F100CFAFFB /* Build configuration list for PBXNativeTarget "LeadKit iOSTests" */; - buildPhases = ( - 67186B2C1EB248F100CFAFFB /* Sources */, - 67186B2D1EB248F100CFAFFB /* Frameworks */, - 67186B2E1EB248F100CFAFFB /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 67186B331EB248F100CFAFFB /* PBXTargetDependency */, - ); - name = "LeadKit iOSTests"; - productName = "LeadKit iOSTests"; - productReference = 67186B301EB248F100CFAFFB /* LeadKit iOSTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 6782BB901EB31CFE0086E0B8 /* LeadKit watchOS */ = { isa = PBXNativeTarget; buildConfigurationList = 6782BB961EB31CFE0086E0B8 /* Build configuration list for PBXNativeTarget "LeadKit watchOS" */; @@ -2228,24 +2161,6 @@ productReference = 6782BBA01EB31D590086E0B8 /* LeadKit.framework */; productType = "com.apple.product-type.framework"; }; - 6782BBA71EB31D5A0086E0B8 /* LeadKit tvOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6782BBB41EB31D5A0086E0B8 /* Build configuration list for PBXNativeTarget "LeadKit tvOSTests" */; - buildPhases = ( - 6782BBA41EB31D5A0086E0B8 /* Sources */, - 6782BBA51EB31D5A0086E0B8 /* Frameworks */, - 6782BBA61EB31D5A0086E0B8 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 6782BBAB1EB31D5A0086E0B8 /* PBXTargetDependency */, - ); - name = "LeadKit tvOSTests"; - productName = "LeadKit tvOSTests"; - productReference = 6782BBA81EB31D5A0086E0B8 /* LeadKit tvOSTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -2253,19 +2168,13 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Touch Instinct"; TargetAttributes = { 67186B271EB248F100CFAFFB = { CreatedOnToolsVersion = 8.3; DevelopmentTeam = D4HA43V467; - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - }; - 67186B2F1EB248F100CFAFFB = { - CreatedOnToolsVersion = 8.3; - DevelopmentTeam = D4HA43V467; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; 6782BB901EB31CFE0086E0B8 = { @@ -2280,20 +2189,15 @@ LastSwiftMigration = 1000; ProvisioningStyle = Automatic; }; - 6782BBA71EB31D5A0086E0B8 = { - CreatedOnToolsVersion = 8.3; - DevelopmentTeam = D4HA43V467; - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - }; }; }; buildConfigurationList = 78CFEE241C5C456B00F50370 /* Build configuration list for PBXProject "LeadKit" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 78CFEE201C5C456B00F50370; productRefGroup = 78CFEE2B1C5C456B00F50370 /* Products */; @@ -2301,10 +2205,8 @@ projectRoot = ""; targets = ( 67186B271EB248F100CFAFFB /* LeadKit iOS */, - 67186B2F1EB248F100CFAFFB /* LeadKit iOSTests */, 6782BB901EB31CFE0086E0B8 /* LeadKit watchOS */, 6782BB9F1EB31D590086E0B8 /* LeadKit tvOS */, - 6782BBA71EB31D5A0086E0B8 /* LeadKit tvOSTests */, ); }; /* End PBXProject section */ @@ -2317,14 +2219,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 67186B2E1EB248F100CFAFFB /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 671463CD1EB34B1E00EAB194 /* TestView.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 6782BB8F1EB31CFE0086E0B8 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2339,14 +2233,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 6782BBA61EB31D5A0086E0B8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 671463CF1EB34B1E00EAB194 /* TestView.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -2459,6 +2345,7 @@ 67EB7FF8206175F700BDD9FB /* PaginationWrappable.swift in Sources */, 67990AD6213EA6A50040D195 /* ContentLoadingViewModel+Extensions.swift in Sources */, 671463541EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */, + 72AECC6B224A979D00D12E7C /* BaseSearchViewController.swift in Sources */, 673CF4112063ABD100C329F6 /* GeneralDataLoadingState+Extensions.swift in Sources */, 673CF42C2063DE5900C329F6 /* TextPlaceholderView.swift in Sources */, 67ED2BDE20B44DEB00508B3E /* InitializableView.swift in Sources */, @@ -2511,7 +2398,6 @@ A6D10EAB1F8A9278003E69DD /* Comparable+Extensions.swift in Sources */, 671463801EB3396E00EAB194 /* PaddingDrawingOperation.swift in Sources */, 671463281EB3396E00EAB194 /* BaseViewModel.swift in Sources */, - 671AD25C206A343300EAF887 /* VoidBlock.swift in Sources */, A6E0DDDF1F8A696F002CA74E /* SeparatorCell.swift in Sources */, 67153E40207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */, 671462AC1EB3396E00EAB194 /* Observable+DeferredJust.swift in Sources */, @@ -2519,6 +2405,7 @@ 6741CEB420E242C100FEC4D9 /* CollectionViewHolder+ScrollViewHolder.swift in Sources */, A676AE501F9810C1001F9214 /* Any+Cast.swift in Sources */, 78EC7B1322019F5A0007DCFD /* String+TelpromptURL.swift in Sources */, + 72AECC6F224A97B100D12E7C /* SearchResultsViewControllerState.swift in Sources */, 6714627C1EB3396E00EAB194 /* SessionManager+Extensions.swift in Sources */, 671462D41EB3396E00EAB194 /* TableDirector+Extensions.swift in Sources */, 67E352572119ACF30035BDDB /* ViewTextConfigurable+Extensions.swift in Sources */, @@ -2530,6 +2417,7 @@ 67E902512125B064008EDF45 /* BuildInNumberTypes+NSNumberConvertible.swift in Sources */, A676AE551F98112E001F9214 /* ObservableMappable.swift in Sources */, 6741CEA520E2418200FEC4D9 /* TableViewHolder.swift in Sources */, + 8546C2E3224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */, A6E0DDE11F8A696F002CA74E /* SeparatorRowBox.swift in Sources */, A6E0DDDE1F8A696F002CA74E /* EmptyCellRow.swift in Sources */, 6762131820A0BBA30034EEF1 /* TableSection+Extensions.swift in Sources */, @@ -2545,6 +2433,7 @@ B84CB06D20B8325D0090DB91 /* SessionManager.swift in Sources */, A658E5501F8CD9350093527A /* Array+SeparatorRowBoxExtensions.swift in Sources */, 673564F12068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift in Sources */, + 72AECC6C224A979D00D12E7C /* BaseSearchViewModel.swift in Sources */, 7295474221E6628C009558E7 /* UINavigationItem+Support.swift in Sources */, 7295474421E66328009558E7 /* UIViewController+UpdateNavigationItemTitle.swift in Sources */, 671462EC1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */, @@ -2636,11 +2525,14 @@ 67EB7FD420615D1700BDD9FB /* ResettableCursorType.swift in Sources */, 6741CEA120E2416C00FEC4D9 /* ScrollViewHolder.swift in Sources */, 6714629C1EB3396E00EAB194 /* CursorType+Slice.swift in Sources */, + 8546C2E8224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */, 6774529F20625EEE0024EEEF /* PaginationDataLoadingModel.swift in Sources */, 678D267920691D8200B05B93 /* DataModelFieldBinding.swift in Sources */, + 72AECC71224A97F100D12E7C /* SearchResultsViewController.swift in Sources */, 673CF4342063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */, 673CF4222063D90600C329F6 /* DisposeBagHolder.swift in Sources */, 67DB776D210871E8001CB56B /* BaseCollectionContentController.swift in Sources */, + 82B4F8DB223903B800F6708C /* Block.swift in Sources */, 671463681EB3396E00EAB194 /* ConfigurableView.swift in Sources */, 6B5B64BACFF8C5487FB0939D /* TableKitViewModel.swift in Sources */, 6B5B6F0BFA22832C47142BAD /* TableKitViewModel+Extenstions.swift in Sources */, @@ -2648,25 +2540,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 67186B2C1EB248F100CFAFFB /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A6C9A5051F8BC78F009311CC /* SeparatorConfiguration.swift in Sources */, - D93221EE20A44896003799D5 /* Double+RoundingTests.swift in Sources */, - 671463CA1EB34B1E00EAB194 /* TestView.swift in Sources */, - 671463B81EB34B1E00EAB194 /* StubCursor.swift in Sources */, - B85B766D20AC51C600F837C4 /* AlbumsContainer.swift in Sources */, - 671463BB1EB34B1E00EAB194 /* CursorTests.swift in Sources */, - A6F32C101F6EBE9600AC08EE /* StringExtensionTests.swift in Sources */, - 671463BE1EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */, - A6C9A50F1F8BC79D009311CC /* Comparable+Extensions.swift in Sources */, - B85B766820AC4EC600F837C4 /* Album.swift in Sources */, - 671463C41EB34B1E00EAB194 /* Post.swift in Sources */, - B84D64B120A70B7000DD76DA /* NetworkServiceTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 6782BB8C1EB31CFE0086E0B8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2686,6 +2559,7 @@ 67274781206CD3BD00725163 /* ViewText+Extensions.swift in Sources */, 6713C23920AF0C4D00875921 /* NetworkOperationState.swift in Sources */, 671463561EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */, + 82B4F8DC223903B800F6708C /* Block.swift in Sources */, 671463621EB3396E00EAB194 /* SupportProtocol.swift in Sources */, 678D26A220692BFF00B05B93 /* TextFieldViewEvents.swift in Sources */, 671462861EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */, @@ -2704,6 +2578,7 @@ 6741C41120EAC88800418D08 /* GeneralDataLoadingViewModel+Extensions.swift in Sources */, 671463861EB3396E00EAB194 /* ResizeDrawingOperation.swift in Sources */, 671463921EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */, + 8546C2E4224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */, 67745282206256A20024EEEF /* RxDataLoadingModel.swift in Sources */, 6714629A1EB3396E00EAB194 /* CGSize+Resize.swift in Sources */, 671463321EB3396E00EAB194 /* CursorType.swift in Sources */, @@ -2781,8 +2656,7 @@ 6714638E1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */, 6774529420625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */, 67FDC2611FA310EA00C76A77 /* RequestError.swift in Sources */, - 671AD25E206A343300EAF887 /* VoidBlock.swift in Sources */, - 72039CE122089A3D00875DD4 /* SearchResultsViewController.swift in Sources */, + 8546C2E9224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */, 671462521EB3396E00EAB194 /* StaticCursor.swift in Sources */, 67ED2BE720B44F4300508B3E /* InitializableView+DefaultImplementation.swift in Sources */, 6714629E1EB3396E00EAB194 /* CursorType+Slice.swift in Sources */, @@ -2797,6 +2671,7 @@ buildActionMask = 2147483647; files = ( 6714634B1EB3396E00EAB194 /* ResettableType.swift in Sources */, + 82B4F8DD223903B800F6708C /* Block.swift in Sources */, 671462E71EB3396E00EAB194 /* UIColor+Hex.swift in Sources */, 67ED2BE120B44DEB00508B3E /* InitializableView.swift in Sources */, 67990AD9213EA6A50040D195 /* ContentLoadingViewModel+Extensions.swift in Sources */, @@ -2886,7 +2761,6 @@ B84CB07B20B872AD0090DB91 /* Decodable+Extensions.swift in Sources */, 671463731EB3396E00EAB194 /* ApiRequestParameters.swift in Sources */, 671462EF1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */, - 671AD25F206A343300EAF887 /* VoidBlock.swift in Sources */, 6774526A206249360024EEEF /* UITableView+PaginationWrappable.swift in Sources */, 6714636F1EB3396E00EAB194 /* XibNameProtocol.swift in Sources */, EFBE57DE1EC361620040E00A /* UIView+Layout.swift in Sources */, @@ -2949,6 +2823,7 @@ 6774529520625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */, 6713C23A20AF0C4D00875921 /* NetworkOperationState.swift in Sources */, 6774529D20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */, + 8546C2E5224E86280059C255 /* ApiUploadRequestParameters.swift in Sources */, 6732F245214C189100B446F2 /* Single+DeferredJust.swift in Sources */, 6714632F1EB3396E00EAB194 /* ConfigurableController.swift in Sources */, 67990ACD213EA5B70040D195 /* ContentLoadingViewModel.swift in Sources */, @@ -2967,6 +2842,7 @@ 6714638F1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */, 677B06A321186A69006C947D /* SharedSequence+Extensions.swift in Sources */, 67E6C2381EBB32F5007842A6 /* SingleLoadCursor.swift in Sources */, + 8546C2EA224E864F0059C255 /* Error+NetworkExtensions.swift in Sources */, 67C7B17C2068BB1C00C9EDA3 /* NumberFormattingService.swift in Sources */, 671462531EB3396E00EAB194 /* StaticCursor.swift in Sources */, 67E352502119ABE40035BDDB /* UITextField+ViewTextConfigurable.swift in Sources */, @@ -2976,38 +2852,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 6782BBA41EB31D5A0086E0B8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B85B766F20AC51C700F837C4 /* AlbumsContainer.swift in Sources */, - 671463CC1EB34B1E00EAB194 /* TestView.swift in Sources */, - B84D64B320A70B7000DD76DA /* NetworkServiceTests.swift in Sources */, - 671463BA1EB34B1E00EAB194 /* StubCursor.swift in Sources */, - 671463BD1EB34B1E00EAB194 /* CursorTests.swift in Sources */, - B85B766A20AC4EC700F837C4 /* Album.swift in Sources */, - A6F32C121F6EBE9800AC08EE /* StringExtensionTests.swift in Sources */, - D93221F020A44896003799D5 /* Double+RoundingTests.swift in Sources */, - 671463C01EB34B1E00EAB194 /* LoadFromNibTests.swift in Sources */, - 671463C61EB34B1E00EAB194 /* Post.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 67186B331EB248F100CFAFFB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 67186B271EB248F100CFAFFB /* LeadKit iOS */; - targetProxy = 67186B321EB248F100CFAFFB /* PBXContainerItemProxy */; - }; - 6782BBAB1EB31D5A0086E0B8 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6782BB9F1EB31D590086E0B8 /* LeadKit tvOS */; - targetProxy = 6782BBAA1EB31D5A0086E0B8 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ 67186B3A1EB248F100CFAFFB /* Debug */ = { isa = XCBuildConfiguration; @@ -3035,7 +2881,7 @@ PRODUCT_NAME = LeadKit; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -3064,43 +2910,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "ru.touchin.LeadKit-iOS"; PRODUCT_NAME = LeadKit; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; - }; - name = Release; - }; - 67186B3D1EB248F100CFAFFB /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = D4HA43V467; - INFOPLIST_FILE = "Tests/Info-iOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "ru.touchin.LeadKit-iOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; - }; - name = Debug; - }; - 67186B3E1EB248F100CFAFFB /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = D4HA43V467; - INFOPLIST_FILE = "Tests/Info-iOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "ru.touchin.LeadKit-iOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -3228,48 +3038,11 @@ }; name = Release; }; - 6782BBB51EB31D5A0086E0B8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = D4HA43V467; - INFOPLIST_FILE = "Tests/Info-tvOS.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "ru.touchin.LeadKit-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; - TVOS_DEPLOYMENT_TARGET = 10.2; - }; - name = Debug; - }; - 6782BBB61EB31D5A0086E0B8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = D4HA43V467; - INFOPLIST_FILE = "Tests/Info-tvOS.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "ru.touchin.LeadKit-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_VERSION = 4.2; - TVOS_DEPLOYMENT_TARGET = 10.2; - }; - name = Release; - }; 78CFEE3C1C5C456B00F50370 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3328,6 +3101,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3388,15 +3162,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 67186B3C1EB248F100CFAFFB /* Build configuration list for PBXNativeTarget "LeadKit iOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 67186B3D1EB248F100CFAFFB /* Debug */, - 67186B3E1EB248F100CFAFFB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 6782BB961EB31CFE0086E0B8 /* Build configuration list for PBXNativeTarget "LeadKit watchOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3415,15 +3180,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 6782BBB41EB31D5A0086E0B8 /* Build configuration list for PBXNativeTarget "LeadKit tvOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6782BBB51EB31D5A0086E0B8 /* Debug */, - 6782BBB61EB31D5A0086E0B8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 78CFEE241C5C456B00F50370 /* Build configuration list for PBXProject "LeadKit" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS Extensions.xcscheme b/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS Extensions.xcscheme deleted file mode 100644 index c4a9d46e..00000000 --- a/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS Extensions.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS.xcscheme b/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS.xcscheme index 879a6705..4e71a311 100644 --- a/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS.xcscheme +++ b/LeadKit.xcodeproj/xcshareddata/xcschemes/LeadKit iOS.xcscheme @@ -1,6 +1,6 @@ 4.5) - - RxSwift (~> 4) - - RxAtomic (4.4.0) - - RxCocoa (4.4.0): - - RxSwift (~> 4.0) - - RxSwift (4.4.0): - - RxAtomic (~> 4.4) - - SwiftDate (5.1.0) - - SwiftLint (0.30.1) - - TableKit (2.8.1) - - UIScrollView-InfiniteScroll (1.1.0) - -DEPENDENCIES: - - RxAlamofire - - RxCocoa - - RxSwift - - SwiftDate - - SwiftLint - - TableKit - - UIScrollView-InfiniteScroll - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Alamofire - - RxAlamofire - - RxAtomic - - RxCocoa - - RxSwift - - SwiftDate - - SwiftLint - - TableKit - - UIScrollView-InfiniteScroll - -SPEC CHECKSUMS: - Alamofire: 16ce2c353fb72865124ddae8a57c5942388f4f11 - RxAlamofire: 09624d0f2d48ed8b686e4eb4cf68e28cbd2df556 - RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f - RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749 - RxSwift: 5976ecd04fc2fefd648827c23de5e11157faa973 - SwiftDate: 6329e58969a2de31cea7f1ee1143b247693196e7 - SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7 - TableKit: 18a0049dea981c1106409bdeebc763ef74d36f02 - UIScrollView-InfiniteScroll: 3ef456bcbe759c19f510a383cff96e6647c98c98 - -PODFILE CHECKSUM: abec65fc699c906f3429fd91a31217e4263a7e88 - -COCOAPODS: 1.6.0.rc.1 diff --git a/Sources/Classes/Search/BaseSearchViewController.swift b/Sources/Classes/Search/BaseSearchViewController.swift index e9b96d5f..2e170cc1 100644 --- a/Sources/Classes/Search/BaseSearchViewController.swift +++ b/Sources/Classes/Search/BaseSearchViewController.swift @@ -46,6 +46,7 @@ where ViewModel: BaseSearchViewModel { self.searchResultsController = searchResultsController self.searchController = UISearchController(searchResultsController: searchResultsController) super.init(viewModel: viewModel) + initialLoadView() } required public init?(coder aDecoder: NSCoder) { @@ -56,7 +57,6 @@ where ViewModel: BaseSearchViewModel { open override func bindViews() { super.bindViews() - viewModel.itemsViewModelsDriver .drive(onNext: { [weak self] viewModels in self?.handle(itemViewModels: viewModels) @@ -93,8 +93,8 @@ where ViewModel: BaseSearchViewModel { open override func configureAppearance() { super.configureAppearance() - definesPresentationContext = true + configureSearchBarAppearance(searchController.searchBar) customView.tableView.tableHeaderView?.backgroundColor = searchBarColor } @@ -183,6 +183,10 @@ where ViewModel: BaseSearchViewModel { /// override in subclass return .zero } + + open func configureSearchBarAppearance(_ searchBar: UISearchBar) { + // override in subclass + } } extension BaseSearchViewController { diff --git a/Sources/Classes/Search/BaseSearchViewModel.swift b/Sources/Classes/Search/BaseSearchViewModel.swift index e9e5cdc3..6c2671e0 100644 --- a/Sources/Classes/Search/BaseSearchViewModel.swift +++ b/Sources/Classes/Search/BaseSearchViewModel.swift @@ -39,13 +39,17 @@ open class BaseSearchViewModel: GeneralDataLoadingViewModel .map { [weak self] items in self?.viewModels(from: items) } - .filterNil() + .flatMap { Observable.from(optional: $0) } .share(replay: 1, scope: .forever) .asDriver(onErrorDriveWith: .empty()) } + open var searchDebounceInterval: RxTimeInterval { + return 1 + } + open var searchResultsDriver: Driver<[ItemViewModel]> { - return searchTextRelay.throttle(1, scheduler: MainScheduler.instance) + return searchTextRelay.debounce(searchDebounceInterval, scheduler: MainScheduler.instance) .withLatestFrom(loadingResultObservable) { ($0, $1) } .flatMapLatest { [weak self] searchText, items -> Observable in self?.search(by: searchText, from: items).asObservable() ?? .empty() @@ -53,7 +57,7 @@ open class BaseSearchViewModel: GeneralDataLoadingViewModel .map { [weak self] items in self?.viewModels(from: items) } - .filterNil() + .flatMap { Observable.from(optional: $0) } .share(replay: 1, scope: .forever) .asDriver(onErrorDriveWith: .empty()) } @@ -70,10 +74,6 @@ open class BaseSearchViewModel: GeneralDataLoadingViewModel return searchText.bind(to: searchTextRelay) } - open func onDidSelect(item: Item) { - // override in subclass - } - private func viewModels(from items: ItemsList) -> [ItemViewModel] { return items.map { self.viewModel(from: $0) } } @@ -82,14 +82,14 @@ open class BaseSearchViewModel: GeneralDataLoadingViewModel return loadingStateDriver .asObservable() .map { $0.result } - .filterNil() + .flatMap { Observable.from(optional: $0) } } open var loadingErrorObservable: Observable { return loadingStateDriver .asObservable() .map { $0.error } - .filterNil() + .flatMap { Observable.from(optional: $0) } } open var firstLoadingResultObservable: Single { diff --git a/Sources/Classes/Services/NetworkService.swift b/Sources/Classes/Services/NetworkService.swift index 08879f45..10cb8f8e 100644 --- a/Sources/Classes/Services/NetworkService.swift +++ b/Sources/Classes/Services/NetworkService.swift @@ -55,39 +55,69 @@ open class NetworkService { /// Perform reactive request to get mapped ObservableMappable model and http response /// - /// - Parameter parameters: api parameters to pass Alamofire - /// - Parameter decoder: json decoder to decode response data + /// - Parameters: + /// - parameters: api parameters to pass to Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data /// - Returns: Observable of tuple containing (HTTPURLResponse, ObservableMappable) public func rxObservableRequest(with parameters: ApiRequestParameters, + additionalValidStatusCodes: Set = [], decoder: JSONDecoder = JSONDecoder()) -> Observable> { return sessionManager.rx.responseObservableModel(requestParameters: parameters, + additionalValidStatusCodes: additionalValidStatusCodes, decoder: decoder) .counterTracking(for: self) } /// Perform reactive request to get mapped ImmutableMappable model and http response /// - /// - Parameter parameters: api parameters to pass Alamofire - /// - Parameter decoder: json decoder to decode response data + /// - Parameters: + /// - parameters: api parameters to pass to Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data /// - Returns: Observable of tuple containing (HTTPURLResponse, ImmutableMappable) - public func rxRequest(with parameters: ApiRequestParameters, decoder: JSONDecoder = JSONDecoder()) + public func rxRequest(with parameters: ApiRequestParameters, + additionalValidStatusCodes: Set = [], + decoder: JSONDecoder = JSONDecoder()) -> Observable> { return sessionManager.rx.responseModel(requestParameters: parameters, + additionalValidStatusCodes: additionalValidStatusCodes, decoder: decoder) .counterTracking(for: self) } /// Perform reactive request to get data and http response /// - /// - Parameter parameters: api parameters to pass Alamofire + /// - Parameters: + /// - parameters: api parameters to pass to Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes /// - Returns: Observable of tuple containing (HTTPURLResponse, Data) - public func rxDataRequest(with parameters: ApiRequestParameters) + public func rxDataRequest(with parameters: ApiRequestParameters, additionalValidStatusCodes: Set = []) -> Observable { - return sessionManager.rx.responseData(requestParameters: parameters) + return sessionManager.rx.responseData(requestParameters: parameters, + additionalValidStatusCodes: additionalValidStatusCodes) + .counterTracking(for: self) + } + + /// Perform reactive request to upload data and get Observable model and http response + /// + /// - Parameters: + /// - parameters: api upload parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data + /// - Returns: Observable of model response + public func rxUploadRequest(with parameters: ApiUploadRequestParameters, + additionalValidStatusCodes: Set = [], + decoder: JSONDecoder = JSONDecoder()) + -> Observable> { + + return sessionManager.rx.uploadResponseModel(requestParameters: parameters, + additionalValidStatusCodes: additionalValidStatusCodes, + decoder: decoder) .counterTracking(for: self) } } diff --git a/Sources/Classes/Views/BasePlaceholderView/BasePlaceholerView.swift b/Sources/Classes/Views/BasePlaceholderView/BasePlaceholerView.swift index 479b9ce2..3e5e77d9 100644 --- a/Sources/Classes/Views/BasePlaceholderView/BasePlaceholerView.swift +++ b/Sources/Classes/Views/BasePlaceholderView/BasePlaceholerView.swift @@ -75,6 +75,10 @@ open class BasePlaceholderView: UIView, InitializableView { open func localize() { // override in subclass } + + open func configureLayout() { + // override in subclass + } } public extension BasePlaceholderView { diff --git a/Sources/Extensions/Alamofire/DataRequest+Extensions.swift b/Sources/Extensions/Alamofire/DataRequest+Extensions.swift index 458559ba..78177d07 100644 --- a/Sources/Extensions/Alamofire/DataRequest+Extensions.swift +++ b/Sources/Extensions/Alamofire/DataRequest+Extensions.swift @@ -34,12 +34,13 @@ public extension Reactive where Base: DataRequest { /// - Parameter decoder: JSONDecoder used to decode a decodable type /// - Returns: Observable with HTTP URL Response and target object func apiResponse(mappingQueue: DispatchQueue = .global(), decoder: JSONDecoder) - -> Observable<(response: HTTPURLResponse, model: T)> { + -> Observable> { return response(onQueue: mappingQueue) .tryMapResult { response, data in (response, try decoder.decode(T.self, from: data)) } + .catchAsRequestError(with: self.base) } /// Method that serializes response into target object @@ -47,7 +48,7 @@ public extension Reactive where Base: DataRequest { /// - Parameter mappingQueue: The dispatch queue to use for mapping /// - Returns: Observable with HTTP URL Response and target object func observableApiResponse(mappingQueue: DispatchQueue = .global(), decoder: JSONDecoder) - -> Observable<(response: HTTPURLResponse, model: T)> { + -> Observable> { return response(onQueue: mappingQueue) .tryMapObservableResult { response, value in @@ -55,6 +56,17 @@ public extension Reactive where Base: DataRequest { return T.create(from: json, with: decoder) .map { (response, $0) } } + .catchAsRequestError(with: self.base) + } + + /// Method that serializes response into data + /// + /// - Parameter mappingQueue: The dispatch queue to use for mapping + /// - Returns: Observable with HTTP URL Response and data + func dataApiResponse(mappingQueue: DispatchQueue) -> Observable { + return response(onQueue: mappingQueue) + .map { $0 as SessionManager.DataResponse } + .catchAsRequestError(with: self.base) } private func response(onQueue queue: DispatchQueue) -> Observable<(HTTPURLResponse, Data)> { @@ -62,6 +74,19 @@ public extension Reactive where Base: DataRequest { } } +public extension ObservableType where E == DataRequest { + + /// Method that validates status codes and catch network errors + /// + /// - Parameter statusCodes: set of status codes to validate + /// - Returns: Observable on self + func validate(statusCodes: Set) -> Observable { + return map { $0.validate(statusCode: statusCodes) } + .catchAsRequestError() + } +} + + private extension ObservableType where E == ServerResponse { func tryMapResult(_ transform: @escaping (E) throws -> R) -> Observable { @@ -87,3 +112,41 @@ private extension ObservableType where E == ServerResponse { } } } + +private extension ObservableType { + + func catchAsRequestError(with request: DataRequest? = nil) -> Observable { + return catchError { error in + let resultError: RequestError + let response = request?.delegate.data + + switch error { + case let requestError as RequestError: + resultError = requestError + + case let urlError as URLError: + switch urlError.code { + case .notConnectedToInternet: + resultError = .noConnection + + default: + resultError = .network(error: urlError, response: response) + } + + case let afError as AFError: + switch afError { + case .responseSerializationFailed, .responseValidationFailed: + resultError = .invalidResponse(error: afError, response: response) + + default: + resultError = .network(error: afError, response: response) + } + + default: + resultError = .network(error: error, response: response) + } + + throw resultError + } + } +} diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index 161230ba..a8afc976 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -68,9 +68,11 @@ public extension Reactive where Base: SessionManager { /// Method which executes request with given api parameters /// - /// - Parameter requestParameters: api parameters to pass Alamofire + /// - Parameters: + /// - requestParameters: api parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes /// - Returns: Observable with request - func apiRequest(requestParameters: ApiRequestParameters) + func apiRequest(requestParameters: ApiRequestParameters, additionalValidStatusCodes: Set) -> Observable { let requestObservable: Observable @@ -104,92 +106,83 @@ public extension Reactive where Base: SessionManager { } return requestObservable - .map { $0.validate(statusCode: self.base.acceptableStatusCodes) } - .catchAsRequestError() + .validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes)) } /// Method that executes request and serializes response into target object /// - /// - Parameter requestParameters: api parameters to pass Alamofire - /// - Parameter decoder: json decoder to decode response data + /// - Parameters: + /// - requestParameters: api parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data /// - Returns: Observable with HTTP URL Response and target object func responseModel(requestParameters: ApiRequestParameters, + additionalValidStatusCodes: Set, decoder: JSONDecoder) -> Observable> { - return apiRequest(requestParameters: requestParameters) + return apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes) .flatMap { $0.rx.apiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder) - .catchAsRequestError(with: $0) } } /// Method that executes request and serializes response into target object /// - /// - Parameter requestParameters: api parameters to pass Alamofire - /// - Parameter decoder: json decoder to decode response data + /// - Parameters: + /// - requestParameters: api parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data /// - Returns: Observable with HTTP URL Response and target object func responseObservableModel(requestParameters: ApiRequestParameters, + additionalValidStatusCodes: Set, decoder: JSONDecoder) -> Observable> { - return apiRequest(requestParameters: requestParameters) + return apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes) .flatMap { $0.rx.observableApiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder) - .catchAsRequestError(with: $0) } } /// Method that executes request and returns data /// - /// - Parameter requestParameters: api parameters to pass Alamofire + /// - Parameters: + /// - requestParameters: api parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes /// - Returns: Observable with HTTP URL Response and Data - func responseData(requestParameters: ApiRequestParameters) + func responseData(requestParameters: ApiRequestParameters, additionalValidStatusCodes: Set) -> Observable { - return apiRequest(requestParameters: requestParameters) + return apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes) .flatMap { - $0.rx.responseResult(queue: self.base.mappingQueue, - responseSerializer: DataRequest.dataResponseSerializer()) - .map { $0 as SessionManager.DataResponse } - .catchAsRequestError(with: $0) + $0.rx.dataApiResponse(mappingQueue: self.base.mappingQueue) } } -} -private extension ObservableType { - func catchAsRequestError(with request: DataRequest? = nil) -> Observable { - return catchError { error in - let resultError: RequestError - let response = request?.delegate.data + /// Method that executes upload request and serializes response into target object + /// + /// - Parameters: + /// - requestParameters: api upload parameters to pass Alamofire + /// - additionalValidStatusCodes: set of additional valid status codes + /// - decoder: json decoder to decode response data + /// - Returns: Observable with HTTP URL Response and target object + func uploadResponseModel(requestParameters: ApiUploadRequestParameters, + additionalValidStatusCodes: Set, + decoder: JSONDecoder) + -> Observable> { - switch error { - case let requestError as RequestError: - resultError = requestError + return Observable.deferred { - case let urlError as URLError: - switch urlError.code { - case .notConnectedToInternet: - resultError = .noConnection + let urlRequest = try URLRequest(url: requestParameters.url, method: .post, headers: requestParameters.headers) + let data = try requestParameters.formData.encode() - default: - resultError = .network(error: urlError, response: response) - } - - case let afError as AFError: - switch afError { - case .responseSerializationFailed, .responseValidationFailed: - resultError = .invalidResponse(error: afError, response: response) - - default: - resultError = .network(error: afError, response: response) - } - - default: - resultError = .network(error: error, response: response) + return self.upload(data, urlRequest: urlRequest) + .map { $0 as DataRequest } + .validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes)) + .flatMap { + $0.rx.apiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder) + } } - - throw resultError - } } } diff --git a/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift b/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift index 9eb78816..20f7a487 100644 --- a/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift +++ b/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift @@ -236,7 +236,7 @@ public extension UIImage { } @available(iOS 10.0, tvOS 10.0, *) -private extension DrawingOperation { +internal extension DrawingOperation { func imageFromNewRenderer(scale: CGFloat) -> UIImage { let ctxSize = contextSize diff --git a/Sources/Extensions/Error/Error+NetworkExtensions.swift b/Sources/Extensions/Error/Error+NetworkExtensions.swift new file mode 100644 index 00000000..872a0508 --- /dev/null +++ b/Sources/Extensions/Error/Error+NetworkExtensions.swift @@ -0,0 +1,103 @@ +// +// Copyright (c) 2019 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 Error { + + var requestError: RequestError? { + return self as? RequestError + } + + /// Method that tries to serialize response from a mapping request error to a model + /// + /// - Parameter decoder: json decoder to decode response data + /// - Returns: optional target object + /// - Throws: an error during decoding + func handleMappingError(with decoder: JSONDecoder = JSONDecoder()) throws -> T? { + guard let self = requestError, case .mapping(_, let response) = self else { + return nil + } + + return try decoder.decode(T.self, from: response) + } +} + +public extension ObservableType { + + /// Method that handles a mapping error and serialize response to a model + /// + /// - Parameters: + /// - decoder: json decoder to decode response data + /// - handler: closure that recieves serialized response + /// - Returns: Observable on caller + func handleMappingError(with decoder: JSONDecoder = JSONDecoder(), + handler: @escaping ParameterClosure) -> Observable { + return self.do(onError: { error in + guard let errorModel = try error.handleMappingError(with: decoder) as T? else { + return + } + + handler(errorModel) + }) + } +} + +public extension PrimitiveSequence where Trait == SingleTrait { + + /// Method that handles a mapping error and serialize response to a model + /// + /// - Parameters: + /// - decoder: json decoder to decode response data + /// - handler: closure that recieves serialized response + /// - Returns: Single on caller + func handleMappingError(with decoder: JSONDecoder = JSONDecoder(), + handler: @escaping ParameterClosure) -> PrimitiveSequence { + return self.do(onError: { error in + guard let errorModel = try error.handleMappingError(with: decoder) as T? else { + return + } + + handler(errorModel) + }) + } +} + +public extension PrimitiveSequence where Trait == CompletableTrait, Element == Never { + + /// Method that handles a mapping error and serialize response to a model + /// + /// - Parameters: + /// - decoder: json decoder to decode response data + /// - handler: closure that recieves serialized response + /// - Returns: Completable + func handleMappingError(with decoder: JSONDecoder = JSONDecoder(), + handler: @escaping ParameterClosure) -> Completable { + return self.do(onError: { error in + guard let errorModel = try error.handleMappingError(with: decoder) as T? else { + return + } + + handler(errorModel) + }) + } +} diff --git a/Sources/Extensions/NumberFormattingService/NumberFormattingService+DefaultImplementation.swift b/Sources/Extensions/NumberFormattingService/NumberFormattingService+DefaultImplementation.swift index 8908bf2e..e8302f51 100644 --- a/Sources/Extensions/NumberFormattingService/NumberFormattingService+DefaultImplementation.swift +++ b/Sources/Extensions/NumberFormattingService/NumberFormattingService+DefaultImplementation.swift @@ -26,7 +26,7 @@ public extension NumberFormattingService { /// Computed static property. Use only once for `formatters` field implementation! static var computedFormatters: [NumberFormatType: NumberFormatter] { - return Dictionary(uniqueKeysWithValues: NumberFormatType.allOptions.map { ($0, $0.numberFormatter) }) + return Dictionary(uniqueKeysWithValues: NumberFormatType.allCases.map { ($0, $0.numberFormatter) }) } func numberFormatter(for format: NumberFormatType) -> NumberFormatter { diff --git a/Sources/Extensions/UIKit/UIViewController/UIViewController+ConfigurableController.swift b/Sources/Extensions/UIKit/UIViewController/UIViewController+ConfigurableController.swift index 1384e2fd..5ba82451 100644 --- a/Sources/Extensions/UIKit/UIViewController/UIViewController+ConfigurableController.swift +++ b/Sources/Extensions/UIKit/UIViewController/UIViewController+ConfigurableController.swift @@ -25,7 +25,7 @@ import UIKit.UIViewController public extension ConfigurableController where Self: UIViewController { func initializeView() { - assertionFailure("Use \(initialLoadView) for UIViewController instead!") + assertionFailure("Use \(String(describing: initialLoadView)) for UIViewController instead!") } /// Method that should be called in viewDidLoad method of UIViewController. diff --git a/Sources/Functions/VoidBlock.swift b/Sources/Functions/Block.swift similarity index 63% rename from Sources/Functions/VoidBlock.swift rename to Sources/Functions/Block.swift index e7cf2f7c..98c2e15f 100644 --- a/Sources/Functions/VoidBlock.swift +++ b/Sources/Functions/Block.swift @@ -20,8 +20,20 @@ // THE SOFTWARE. // -/// Closure that takes no arguments and return Void. -public typealias VoidBlock = () -> Void +/// Closure with custom arguments and return value. +public typealias Closure = (Input) -> Output -/// Closure that takes no arguments, may throw error and return Void. +/// Closure with no arguments and custom return value. +public typealias ResultClosure = () -> Output + +/// Closure that takes custom arguments and returns Void. +public typealias ParameterClosure = Closure + +/// Closure that takes no arguments and returns Void. +public typealias VoidBlock = ResultClosure + +/// Closure with custom arguments and return value, may throw an error. +public typealias ThrowableClosure = (Input) throws -> Output + +/// Closure that takes no arguments, may throw an error and returns Void. public typealias ThrowableVoidBlock = () throws -> Void diff --git a/Sources/Info-iOS-Extensions.plist b/Sources/Info-iOS-Extensions.plist deleted file mode 100644 index 399882e1..00000000 --- a/Sources/Info-iOS-Extensions.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 0.5.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Sources/Info-iOS.plist b/Sources/Info-iOS.plist index 1d137243..38e95826 100644 --- a/Sources/Info-iOS.plist +++ b/Sources/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.7 + 0.9.14 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Info-tvOS.plist b/Sources/Info-tvOS.plist index 1d137243..38e95826 100644 --- a/Sources/Info-tvOS.plist +++ b/Sources/Info-tvOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.7 + 0.9.14 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Info-watchOS.plist b/Sources/Info-watchOS.plist index 1d137243..38e95826 100644 --- a/Sources/Info-watchOS.plist +++ b/Sources/Info-watchOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.7 + 0.9.14 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Protocols/NumberFormattingService/NumberFormat.swift b/Sources/Protocols/NumberFormattingService/NumberFormat.swift index 91ced382..818eb65a 100644 --- a/Sources/Protocols/NumberFormattingService/NumberFormat.swift +++ b/Sources/Protocols/NumberFormattingService/NumberFormat.swift @@ -23,10 +23,7 @@ import Foundation /// Protocol for describing number format. -public protocol NumberFormat: Hashable { - - /// All available options. - static var allOptions: [Self] { get } +public protocol NumberFormat: Hashable, CaseIterable { /// A NumberFormatter instance for this format. var numberFormatter: NumberFormatter { get } diff --git a/Sources/Structures/Api/ApiUploadRequestParameters.swift b/Sources/Structures/Api/ApiUploadRequestParameters.swift new file mode 100644 index 00000000..7f6b3628 --- /dev/null +++ b/Sources/Structures/Api/ApiUploadRequestParameters.swift @@ -0,0 +1,114 @@ +// +// Copyright (c) 2019 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 Alamofire +import MobileCoreServices + +private enum Constants { + + static let contentTypeKey = "Content-Type" + static let contentTypeValue = "multipart/form-data; " + static let boundaryKey = "boundary" +} + +enum UploadParametersError: Error { + + case unableGetMimeType +} + + +/// Struct which keeps base parameters required for upload api request +public struct ApiUploadRequestParameters { + + let formData: MultipartFormData + let url: URLConvertible + let headers: HTTPHeaders + + /// ApiUploadRequestParameters initializator (You can get mime type from data using "Swime" pod) + /// + /// - Parameters: + /// - data: data to upload + /// - url: request url + /// - additionalHeaders: request additional headers exсept Content-Type + /// - fileName: file name with extension + /// - name: file name + /// - mimeType: file MIME-type + /// - Throws: UploadParametersError + public init(data: Data, + url: URLConvertible, + additionalHeaders: HTTPHeaders?, + fileName: String, + name: String? = nil, + mimeType: String? = nil) throws { + + let formData = MultipartFormData() + + self.url = url + self.headers = ApiUploadRequestParameters.configureHTTPHeaders(with: additionalHeaders ?? HTTPHeaders(), + formData: formData) + + let name = name ?? (fileName as NSString).deletingPathExtension + + let fileMimeType: String + + if let mimeType = mimeType { + fileMimeType = mimeType + } else { + fileMimeType = try ApiUploadRequestParameters.getFileMimeType(from: fileName) + } + + formData.append(data, withName: name, fileName: fileName, mimeType: fileMimeType) + + self.formData = formData + } +} + +private extension ApiUploadRequestParameters { + + static func configureHTTPHeaders(with headers: HTTPHeaders, + formData: MultipartFormData) -> HTTPHeaders { + + var requestHeaders = headers + + let boundary = "\(Constants.boundaryKey)=\(formData.boundary)" + + requestHeaders[Constants.contentTypeKey] = Constants.contentTypeValue + boundary + + return requestHeaders + } + + static func getFileMimeType(from fileName: String) throws -> String { + let fileExtension = (fileName as NSString).pathExtension + + guard let utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, + fileExtension as CFString, + nil)?.takeRetainedValue(), + let mimeType = UTTypeCopyPreferredTagWithClass(utiType, + kUTTagClassMIMEType)?.takeRetainedValue() as? String else { + + assertionFailure("Unable to get mime type from file name") + throw UploadParametersError.unableGetMimeType + } + + return mimeType + } +} diff --git a/Tests/NetworkServiceTests.swift b/Tests/NetworkServiceTests.swift index c310fffe..94a76dba 100644 --- a/Tests/NetworkServiceTests.swift +++ b/Tests/NetworkServiceTests.swift @@ -54,7 +54,7 @@ final class NetworkServiceTests: XCTestCase { var receivedModel: Album? var error: Error? let requestCompletedExpectation = expectation(description: "Request completed") - let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums/1") + let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums/1", parameters: [:]) // when networkService.rxRequest(with: apiRequest) @@ -80,7 +80,7 @@ final class NetworkServiceTests: XCTestCase { var response: [Album]? var error: Error? let requestCompletedExpectation = expectation(description: "Request completed") - let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums") + let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums", parameters: [:]) //when networkService.rxRequest(with: apiRequest) @@ -110,7 +110,7 @@ final class NetworkServiceTests: XCTestCase { var receivedModel: Album? var error: Error? let requestCompletedExpectation = expectation(description: "Request completed") - let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums/1") + let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums/1", parameters: [:]) // when networkService.rxObservableRequest(with: apiRequest) @@ -136,7 +136,7 @@ final class NetworkServiceTests: XCTestCase { var receivedModel: AlbumContainer? var error: Error? let requestCompletedExpectation = expectation(description: "Request completed") - let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums") + let apiRequest = ApiRequestParameters(url: networkService.configuration.baseUrl + "/albums", parameters: [:]) // when networkService.rxObservableRequest(with: apiRequest) diff --git a/iOS.playground/Contents.swift b/iOS.playground/Contents.swift index 2a779426..8b137891 100644 --- a/iOS.playground/Contents.swift +++ b/iOS.playground/Contents.swift @@ -1,3 +1 @@ -import LeadKit -let str = "Hello, LeadKit playground"