Compare commits

...

1284 Commits

Author SHA1 Message Date
Ivan Smolin 3d57f5ccc4 Merge pull request 'HolderViewSkeletonsConfiguration, CALayer support for DashedBoundsLayer' (#29) from feature/skeletons_holder_configuration into master
Reviewed-on: #29
2024-02-12 10:52:50 +03:00
Ivan Smolin e8780d0238 Update: `ViewSkeletonsConfiguration`. It's possible to enable or disable animation for specific skeletons now.
Added: `HolderViewSkeletonsConfiguration` for skeleton root view configuration
Added: `DashedBoundsLayer` can now be applied to `CALayer`
2024-02-09 16:14:32 +03:00
Ivan Smolin b7862cb891 Merge pull request 'fix: revert `TextSkeletonsConfiguration` line height calculation' (#28) from fix/skeleton_line_height_calculation into master
Reviewed-on: #28
2024-02-01 14:41:53 +03:00
Ivan Smolin e8ca5ad039 fix: revert `TextSkeletonsConfiguration` line height calculation 2024-02-01 14:35:27 +03:00
Ivan Smolin 8fe2bfd6e8 Merge pull request 'feature/update_table_kit_repo' (#27) from feature/update_table_kit_repo into master
Reviewed-on: #27
2024-02-01 10:45:47 +03:00
Ivan Smolin 18e5421e14 feat: Update: use TouchInstinct `TableKit` fork instead of original one
Update: remove default value from `BoolValueDefaultsStorage`
2024-02-01 10:14:21 +03:00
Ivan Smolin 5549854c75 feat: update tablekit repo 2024-01-31 12:18:40 +03:00
Ivan Smolin af7c5bc9fd Merge pull request 'fix: build and warnings' (#26) from fix/build_and_warnings into master
Reviewed-on: #26
2024-01-30 21:27:35 +03:00
Ivan Smolin 6a7af59ffc fix: build and warnings 2024-01-30 21:25:13 +03:00
Ivan Smolin 36b4f84306 Merge pull request 'Added: `xcprivacy` files' (#25) from fix/minor_fixes_and_xcprivacy into master
Reviewed-on: #25
2024-01-30 20:51:04 +03:00
Ivan Smolin 5c7df9373d Added: `xcprivacy` files
Update: Correctly detect app reinstall in `AppInstallLifetimeSingleValueStorage`
Update: use `xHeight` instead of `pointSize` for default skeleton line height calculation
Update: update `linkTextAttributes` in `UITextView` when setting interactive url parts
2024-01-30 20:40:14 +03:00
Ivan Smolin 5c44471dbb Merge pull request 'fix: change UIButton event propogation avoidance method' (#24) from fix/uibutton_event_propogation_fix into master
Reviewed-on: #24
2023-11-24 12:57:00 +03:00
Ivan Smolin 7a7987239d fix: change UIButton event propogation avoidance method 2023-11-23 21:16:36 +03:00
Ivan Smolin b65029056f Merge pull request 'fix: StatefulButton state configuration for iOS 15+' (#23) from fix/stateful_button_state into master
Reviewed-on: #23
2023-11-08 18:20:01 +03:00
Ivan Smolin 815d749bd9 fix: StatefulButton state configuration for iOS 15+ 2023-11-08 18:17:10 +03:00
Ivan Smolin 03059969b2 Merge pull request 'feat: Set reasonable defaults for SkeletonConfiguration' (#22) from feature/skeleton_configuration_defaults into master
Reviewed-on: #22
2023-11-07 16:39:55 +03:00
Ivan Smolin 1a9ed8353b feat: Set reasonable defaults for SkeletonConfiguration 2023-11-07 16:29:10 +03:00
Ivan Smolin 03aa25e529 Merge pull request 'fix: Changed access level from internal to public of title and subtitle view in BaseTitleSubtitleView' (#21) from fix/base_title_subtitle_view_access_level into master
Reviewed-on: #21
2023-11-03 22:24:57 +03:00
Ivan Smolin 48a2b47428 fix: Changed access level from internal to public of title and subtitle view in BaseTitleSubtitleView 2023-11-03 22:21:05 +03:00
Ivan Smolin d06c1c35d4 Merge pull request 'feat: Added: BaseTitleSubtitleView which can be inherited for fine-tuning skeletons and other behavior.' (#20) from feature/base_title_subtitle_view into master
Reviewed-on: #20
2023-11-03 19:38:19 +03:00
Ivan Smolin 9fab98b560 feat: Added: BaseTitleSubtitleView which can be inherited for fine-tuning skeletons and other behavior.
Update: Changed lines number calculation method in TextSkeletonsConfiguration.
2023-11-03 18:56:26 +03:00
Ivan Smolin 94cf900f7c Merge pull request 'feat: Added: maxWidth parameter to BaseViewSkeletonsConfiguration.' (#19) from feature/skeletons_configuration into master
Reviewed-on: #19
2023-11-02 21:55:21 +03:00
Ivan Smolin c1e96eee35 feat: Added: maxWidth parameter to BaseViewSkeletonsConfiguration.
Added: custom SkeletonConfigurations for nested SkeletonPresenters.
Update: Many fixes and improvements to TextSkeletonsConfiguration.
2023-11-02 21:40:01 +03:00
Ivan Smolin ed8a2113c4 Merge pull request 'feat: Skeletonable can now control custom geometry change notification.' (#18) from feature/skeletons_tuning into master
Reviewed-on: #18
2023-11-01 15:55:48 +03:00
Ivan Smolin 8652795ddb feat: Skeletonable can now control custom geometry change notification.
Filter hidden views from skeletonable views by default.
2023-11-01 14:45:57 +03:00
Ivan Smolin 38dc604cbc Merge pull request 'feat: DefaultTitleSubtitleView support for separated configuration of title and subtitle labels layout.' (#17) from feature/layout_improvenments into master
Reviewed-on: #17
2023-10-30 16:36:31 +03:00
Ivan Smolin abe9ad5dc1 feat: DefaultTitleSubtitleView support for separated configuration of title and subtitle labels layout.
BaseListItemView fixed trailing insets when trailing view is hidden.
2023-10-30 15:21:55 +03:00
Ivan Smolin abd18d848c Merge pull request 'fix: layout DSL heuristics' (#16) from feature/layout_dsl_heuristics into master
Reviewed-on: #16
2023-10-23 14:05:16 +03:00
Ivan Smolin 4a076b8865 fix: layout DSL heuristics 2023-10-20 20:40:21 +03:00
Ivan Smolin 767c19d17b Merge pull request 'feat: Custom string attributes to `BaseTextAttributes`' (#15) from feature/uiviewbackground into master
Reviewed-on: #15
2023-10-09 23:52:09 +03:00
Ivan Smolin 90cd941eff docs: update docs 2023-10-09 23:30:37 +03:00
Ivan Smolin 2bf1fc052a feat: Custom string attributes to `BaseTextAttributes`
- Customizeable `UIViewBackground` and `UIViewBorder` for `UIView.Appearance`
- Keychain single value storage for codable models -`CodableSingleValueKeychainStorage`
- Renamed methods `startAnimation` and `stopAnimation` of `SkeletonPresenter`, so it won't conflict with `Animatable` protocol anymore
2023-10-09 23:03:51 +03:00
Ivan Smolin a03fc1f7ee Merge pull request 'build: fix compile issue' (#14) from build/fix_compile_issue into master
Reviewed-on: #14
2023-09-06 12:07:18 +03:00
Ivan Smolin 7945aa3a62 build: fix compile issue 2023-09-06 11:51:16 +03:00
Ivan Smolin 93b0f61b00 Merge pull request 'feat: TIApplication module and other fixes and improvements' (#13) from feature/ti_application into master
Reviewed-on: #13
2023-09-06 11:31:36 +03:00
Ivan Smolin a0c7faa4a3 fix: code review notes 2023-09-05 16:49:05 +03:00
Ivan Smolin 60734996f6 feat: add universal DSL to TISwiftUtils, remove unused code 2023-08-24 12:07:43 +03:00
Ivan Smolin eaea4abd75 feat: TIApplication module and other fixes and improvements 2023-08-23 20:49:23 +03:00
Ivan Smolin b8611321fb Merge pull request 'feature/bottom-sheet' (#11) from feature/bottom-sheet into master
Reviewed-on: #11
2023-07-28 16:18:36 +03:00
Ivan Smolin 13a5925443 fix: review notes 2023-07-28 16:08:35 +03:00
Ivan Smolin c485434f51 build: add makefile for parallel execution of pod repo push action 2023-07-26 22:24:02 +03:00
Ivan Smolin 843a887ec7 fix: move presentation detents settings to modal view controller appearance, force use nan for undefined layout dimension, fix related layout issues 2023-07-26 17:31:28 +03:00
Ivan Smolin 8007532351 fix: keyboard overlapping for footer and content view of BaseModalViewController 2023-07-25 19:09:21 +03:00
Ivan Smolin 0ef1edfacb build: move included pan modal sources to separate dependency 2023-07-25 17:32:21 +03:00
Ivan Smolin 27d5a3a9ca fix: fix code review notes 2023-07-24 20:48:46 +03:00
Ivan Smolin 4e8f60543d Merge branch 'master' into feature/bottom-sheet 2023-07-24 11:23:40 +03:00
Ivan Smolin c2b31a90d6 Merge pull request 'feature/stateful_button_improvements' (#12) from feature/stateful_button_improvements into master
Reviewed-on: #12
2023-07-24 10:00:37 +03:00
Ivan Smolin 1a4c42fa46 fix: StatefulButton appearance configuration 2023-07-17 18:51:41 +03:00
Ivan Smolin 094c0c40d8 build: prevent compile-time failure in application extensions 2023-07-11 16:30:34 +03:00
Ivan Smolin b22fd239e9 fix: text size calculation in cluster icon renderer 2023-07-10 18:38:41 +03:00
Ivan Smolin 65ae079e62 fix: conform TemplateDrawingOperation to OrientationAwareDrawingOperation 2023-07-10 17:25:24 +03:00
Ivan Smolin 7c8a29a7f9 build: update changelog and podspec versions 2023-07-10 16:37:27 +03:00
Ivan Smolin df2faa4cd5 feat: MarkerIconFactory can now return optional image. In this case MapManagers will show the default marker icon 2023-07-10 16:17:29 +03:00
Ivan Smolin 36019f7429 feat: TICoreGraphicsUtils module for drawing operations and other CoreGraphics related functionality 2023-07-10 16:17:29 +03:00
Ivan Smolin 5245d48a8a feat: stateful button improvements 2023-07-10 16:17:28 +03:00
Nikita Semenov 83655d2bac Merge pull request 'feat: migrating storages' (#10) from feature/migrating_storage into master
Reviewed-on: #10
2023-07-09 22:33:51 +03:00
Nikita Semenov 6b7be340f5 fix: added default logger parameter 2023-07-09 22:15:36 +03:00
Nikita Semenov 6084dd5fec fix: move tifoundationlogger to it's own folder 2023-07-09 22:05:58 +03:00
Nikita Semenov 85b206bf18 fix: move tifoundationlogger to it's own folder 2023-07-09 21:51:41 +03:00
Nikita Semenov da527644a7 fix: move tifoundationlogger to it's own folder 2023-07-07 15:39:11 +03:00
Nikita Semenov 45c060403f fix: logic of getting value from storage 2023-07-07 15:17:54 +03:00
Nikita Semenov a79ff67a38 fix: logic of deletion 2023-07-07 14:00:43 +03:00
Nikita Semenov c631053131 fix: code review notes 2023-07-07 09:01:17 +03:00
Nikita Semenov c55b8f73a9 fix: code review notes 2023-07-06 18:42:18 +03:00
Nikita Semenov 25c0d04d11 feat: update tests, migration fixes, factory method for migration storage added 2023-07-06 12:34:48 +03:00
Nikita Semenov b97ea5bc8f docs: update appearance of view controller in documentation 2023-07-03 10:17:47 +03:00
Nikita Semenov 06a83190ab fix: refactoring with new layout configuration api 2023-07-03 08:46:16 +03:00
Nikita Semenov 3f112d2d26 Merge branch 'master' into feature/bottom-sheet
# Conflicts:
#	CHANGELOG.md
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIDeeplink/TIDeeplink.podspec
#	TIDeveloperUtils/TIDeveloperUtils.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITextProcessing/TITextProcessing.podspec
#	TIUIElements/Sources/Views/Helpers/WrappedViewLayout+Helpers.swift
#	TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderImageView.swift
#	TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderView.swift
#	TIUIElements/Sources/Wrappers/Constraints/CenterConstraints.swift
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIWebView/TIWebView.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2023-07-03 07:31:44 +03:00
Nikita Semenov 808d40eca5 feat: added bodal wrapper view controller 2023-07-03 01:47:52 +03:00
Nikita Semenov 46ecd6970f fix: migration api refactoring + test coverage 2023-07-02 18:00:38 +03:00
Nikita Semenov 0776c99e38 Merge branch 'master' into feature/migrating_storage
# Conflicts:
#	CHANGELOG.md
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIDeeplink/TIDeeplink.podspec
#	TIDeveloperUtils/TIDeveloperUtils.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITextProcessing/TITextProcessing.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIWebView/TIWebView.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
#	docs/tikeychainutils/singlevaluestorage.md
2023-06-30 22:03:35 +03:00
Nikita Semenov a06f4952b9 fix: reimagine migration storage api 2023-06-28 09:26:15 +03:00
Ivan Smolin 77559babdb Merge pull request 'feature/stack_appearance_layout' (#9) from feature/stack_appearance_layout into master
Reviewed-on: #9
2023-06-26 19:51:15 +03:00
Ivan Smolin 86413b3bb4 build: fix code review notes 2023-06-26 14:06:56 +03:00
Nikita Semenov 9056b2fe8c feat: migrating storages 2023-06-25 20:14:06 +03:00
Ivan Smolin 0e45bb462d build: update changelog and up version 2023-06-17 01:56:50 +03:00
Ivan Smolin e8b026302e BaseStackView with configurable items appearance
CollectionTableViewCell self-sizing
ViewAppearance.WrappedViewLayout support for all WrappedViewHolders
ViewCallbacks support for all BaseInitializeableViews
2023-06-17 01:42:14 +03:00
Ivan Smolin 818d4dbe8d Merge pull request 'feature/flat_map_async_operation_result_type_codable_storage' (#8) from feature/flat_map_async_operation_result_type_codable_storage into master
Reviewed-on: #8
2023-06-15 13:30:22 +03:00
Nikita Semenov 86fddafcdf docs: added playground for bottom sheet 2023-06-13 10:33:20 +03:00
Nikita Semenov 919423ecda feat: completed bottom sheet api 2023-06-13 09:29:53 +03:00
Nikita Semenov c06bb56964 fix: remove unused file 2023-06-13 05:59:22 +03:00
Nikita Semenov b141dc5a45 feat: pan modal changes 2023-06-12 22:05:03 +03:00
Ivan Smolin 1e3b986c83 fix: iOS 12 crash - failed to demangle superclass of UIClosureObserverOperation from mangled name '\M^? \^Hp\M-}\M^?' 2023-06-09 16:09:17 +03:00
Ivan Smolin 2ea88a94aa build: fix excluded files pattern in TIFoundationUtils 2023-06-09 12:42:55 +03:00
Ivan Smolin 1be28959bc docs: update playground pages for AsyncOperation and SingleValueExpirationStorage 2023-06-09 11:45:55 +03:00
Ivan Smolin bf613b99e8 build: update changelog and podspec version, fix playground compile issues 2023-06-09 10:14:07 +03:00
Ivan Smolin 23c17c9d85 feat: Added flatMap operator for AsyncOperation
CodableKeyValueStorage now returns Swift.Result with typed errors.
SingleValueExpirationStorage for time aware entries (expirable api tokens, etc.)
AsyncOperation variants of process methods in NetworkServices.
2023-06-09 10:00:12 +03:00
Ivan Smolin f50bb09ad8 Merge pull request 'feature/async_single_value_storage_map_ui_settings' (#7) from feature/async_single_value_storage_map_ui_settings into master
Reviewed-on: #7
2023-06-07 20:00:41 +03:00
Ivan Smolin 005d80c531 feat: Added UserLocationFetcher helper that requests authorization and subscribes to user location updates 2023-06-05 14:36:45 +03:00
Ivan Smolin 33cc31b957 docs: update podspecs and changelog 2023-05-31 18:19:43 +03:00
Ivan Smolin 19a319f03c build: fix playground and podspec issues 2023-05-31 17:33:22 +03:00
Ivan Smolin 193a060cff feat: add AsyncSingleValueStorage for TINetworkingCache 2023-05-31 17:33:22 +03:00
Ivan Smolin a5bc2dc8f0 feat: current location marker and other ui settings to supported maps 2023-05-31 17:33:22 +03:00
Ivan Smolin 5a74c342d9 Merge pull request 'feature/single_value_storages' (#6) from feature/single_value_storages into master
Reviewed-on: #6
2023-05-31 17:18:13 +03:00
Ivan Smolin 5bb3092726 build: podspec and misc fixes 2023-05-29 12:27:59 +03:00
Ivan Smolin 83a3e5b491 build: fix swiftlint issues 2023-05-26 16:07:40 +03:00
Ivan Smolin 7b9e8b0885 feat: add RecoverableErrorType typealias to DefaultRecoverableNetworkService 2023-05-26 10:49:17 +03:00
Ivan Smolin 529277d098 feat: use DecodingError instead of untyped error in TINetworking decoding 2023-05-25 18:17:49 +03:00
Ivan Smolin ecfb83bafa feat: add TILogging module and TINetworking error logging 2023-05-25 11:42:56 +03:00
Ivan Smolin c0189dc7ae fix: fingerprints update in DefaultFingerprintsProvider 2023-05-24 16:14:39 +03:00
Ivan Smolin a0401bc9fa build: bump podspec versions 2023-05-24 15:51:28 +03:00
Ivan Smolin 43a12e322f feat: add TIKeychainUtils playground with SingleValueStorage examples 2023-05-24 15:50:59 +03:00
Ivan Smolin 5ca564476a feat: `SingleValueStorage` implementations + `AppInstallLifetimeSingleValueStorage` for automatically removing keychain items on app reinstall.
`DefaultRecoverableJsonNetworkService` supports iOS 12.
2023-05-24 10:57:17 +03:00
Ivan Smolin 0e0a8d733e Merge pull request 'feature/http_status_codes_for_error_responses' (#5) from feature/http_status_codes_for_error_responses into master
Reviewed-on: #5
2023-05-24 10:04:07 +03:00
Ivan Smolin 7fd33b6157 build: update build scripts, disable autocorrection dy default 2023-05-24 09:28:20 +03:00
Ivan Smolin 4f0c9a8ed1 build: update min deployment target for Xcode 14 2023-05-22 13:35:04 +03:00
Ivan Smolin e7517c23f8 build: update podspec version 2023-05-22 11:57:27 +03:00
Ivan Smolin 975435bb90 build: update podspecs 2023-05-19 18:30:53 +03:00
Ivan Smolin fd0365a370 fix: code lint issues in TINetworking 2023-05-19 18:23:19 +03:00
Ivan Smolin 4ea76a8499 feat: add SwiftLint 2023-05-19 17:48:08 +03:00
Ivan Smolin 6358386303 feat: added HTTP status codes to `EndpointErrorResult.apiError` responses 2023-05-19 16:30:37 +03:00
Nikita Semenov dd4c9072a9 Merge pull request 'feat: updated gitmodules urls' (#4) from fix/git_modules into master
Reviewed-on: #4
2023-04-27 13:59:21 +03:00
Nikita Semenov 5aff5f99bb docs: bumbed version + changelog update 2023-04-27 13:50:52 +03:00
Nikita Semenov 4fe395d295 feat: updated gitmodules urls 2023-04-27 13:47:58 +03:00
Vladimir Makarov a925f70c04 Merge pull request 'fix: `Antlr4` dependency added to podspec' (#3) from fix/Antlr4_dependency into master
Reviewed-on: #3
2023-04-21 12:17:46 +03:00
Vladimir Makarov 41d18bf3a6 fix: `Antlr4` dependency added to podspec 2023-04-21 11:57:07 +03:00
Vladimir Makarov fc6a735d94 Merge pull request 'TITextProcessing' (#1) from feature/TITextProcessing into master
Reviewed-on: #1
2023-04-21 11:27:15 +03:00
Vladimir Makarov 77ce6a1c95 feat: `Private methods` MARK changed to `Public methods` 2023-04-21 11:23:15 +03:00
Vladimir Makarov cf54d8c798 feat: Protocols, default implementations, playground and markdown added 2023-04-20 12:31:09 +03:00
Vladimir Makarov 93a8ee68f3 `TITextProcessing` for regex and text formatting added 2023-04-19 14:22:05 +03:00
Vladimir Makarov 5a14c61a9a Merge pull request 'fix: Podspecs' source and homepage urls' (#2) from fix/pods_source_url into master
Reviewed-on: #2
2023-04-19 14:00:21 +03:00
Vladimir Makarov 4a9355fd80 fix: Podspecs' source and homepage urls 2023-04-19 13:20:18 +03:00
Nikita Semenov 45e2b9ff19 Merge branch 'feature/deeplink_api' into 'master'
Feature/deeplink api

See merge request touchinstinct/LeadKit!13
2023-04-14 09:56:53 +00:00
Nikita Semenov 26866427a9 fix: source path to TIDeeplink library 2023-04-14 12:55:37 +03:00
Nikita Semenov 26a7578dc8 refactor: minor code style refactoring 2023-04-14 12:09:12 +03:00
Nikita Semenov 0612370587 docs: docs refactoring + extensions reorganization for handler and mapper 2023-04-13 14:53:57 +03:00
Nikita Semenov fe80863656 fix: remove meta doc file 2023-04-12 13:46:35 +03:00
Nikita Semenov 3dd0c34d27 fix: code review notes 2023-04-12 13:35:04 +03:00
Nikita Semenov 56527b6dba fix: code review notes 2023-04-12 13:02:20 +03:00
Nikita Semenov af044aa591 feat: integrate new base navigation deeplink handler 2023-04-04 13:21:50 +03:00
Nikita Semenov c442ee2623 Merge branch 'deeplink_generics_experimential' into feature/deeplink_api
# Conflicts:
#	TIDeepLink/Sources/DeeplinkHandler/BaseNavigationStackDeeplinkHandler.swift
#	TIDeepLink/Sources/DeeplinkHandler/Helpers/UIViewController+DeeplinkHandler.swift
2023-04-04 12:10:21 +03:00
Nikita Semenov 3ccadd07b4 fix: remove tiuielements updates 2023-04-04 12:08:39 +03:00
Nikita Semenov 9a9b57df4b refactor: code refactoring 2023-04-04 12:04:45 +03:00
Ivan Smolin 06b687c47e feat: experiments with deeplink generics 2023-04-03 14:01:12 +03:00
Nikita Semenov a99c29ea73 feat: update findHandler method to findAction 2023-04-01 17:46:13 +03:00
Nikita Semenov 270ac1a4d5 fix: code review notes 2023-03-31 10:19:45 +03:00
Nikita Semenov 22b133648b feat: bumped podspecs versions 2023-03-30 15:19:57 +03:00
Nikita Semenov 2245765b27 Merge branch 'master' into feature/deeplink_api
# Conflicts:
#	CHANGELOG.md
2023-03-30 15:19:27 +03:00
Nikita Semenov 30479ae8b8 Merge branch 'feature/skeletons_status' into 'master'
feat: added callbacks for views when skeletons status changed

See merge request touchinstinct/LeadKit!14
2023-03-30 12:10:12 +00:00
Nikita Semenov 8d253d1458 docs: added documentation for new Skeletonable callbacks API 2023-03-30 15:07:43 +03:00
Nikita Semenov dcc9d23676 docs: small refactoring of documentation 2023-03-30 13:44:30 +03:00
Nikita Semenov d9e4ea5ae8 feat: updated deeplink model + documentation for deeplinks api added 2023-03-30 13:41:06 +03:00
Nikita Semenov 775f95a931 fix: podspecs file for TIUIElements 2023-03-29 15:28:38 +03:00
Nikita Semenov 39bad32c49 fix: changed names of views properties in SkeletonLayer 2023-03-29 11:50:48 +03:00
Nikita Semenov 05a6236425 fix: podfile for TIUIElements 2023-03-29 10:00:22 +03:00
Nikita Semenov 7f45ce0594 Merge branch 'master' into feature/skeletons_status
# Conflicts:
#	CHANGELOG.md
2023-03-29 09:58:28 +03:00
Nikita Semenov c407dabdf5 Merge branch 'master' into feature/deeplink_api
# Conflicts:
#	CHANGELOG.md
2023-03-28 17:42:10 +03:00
Nikita Semenov 29d7a6ca65 Merge branch 'feature/placeholder_api' into 'master'
feat: placeholder api

See merge request touchinstinct/LeadKit!11
2023-03-28 12:22:55 +00:00
Nikita Semenov 2d0819064a Merge branch 'feature/image_placeholder_api' into 'feature/placeholder_api'
feat: placeholder image view

See merge request touchinstinct/LeadKit!12
2023-03-28 12:06:50 +00:00
Nikita Semenov 76da4ab223 feat: added callbacks for views when skeletons status changed 2023-03-28 13:15:07 +03:00
Nikita Semenov 66508d505d feat: bump pods version + changelog updates 2023-03-27 10:36:09 +03:00
Nikita Semenov c8985cde1e Merge branch 'master' into feature/deeplink_api
# Conflicts:
#	LeadKit.podspec
#	Package.swift
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIDeveloperUtils/TIDeveloperUtils.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
#	project-scripts/push_to_podspecs.sh
2023-03-27 10:34:04 +03:00
Nikita Semenov 1bc200034c fix: typos 2023-03-20 13:47:55 +03:00
Nikita Semenov cde2420f7d fix: code review notes 2023-03-18 12:51:58 +03:00
Nikita Semenov 026700b4c7 feat: placeholder image view 2023-03-17 16:39:35 +03:00
Nikita Semenov 4ccebe8e8b fix: code review notes 2023-03-17 11:46:44 +03:00
Nikita Semenov f3ed27e83f fix: typo 2023-03-17 11:38:58 +03:00
Nikita Semenov a234943394 fix: code review notes 2023-03-17 11:34:21 +03:00
Nikita Semenov 1374c4df1f feat: update placeholders interactions with opend keyboard 2023-03-17 11:10:07 +03:00
Nikita Semenov 318fd40f0b docs: updated first example of usage PlaceholderFactory 2023-03-16 19:12:46 +03:00
Nikita Semenov 5ebb97de4c fix: applyBaseStyle method signature 2023-03-16 18:09:29 +03:00
Nikita Semenov 507fc8fa05 fix: defaults string names changed by code style 2023-03-16 15:49:55 +03:00
Nikita Semenov 1932262ad5 fix: code review notes 2023-03-16 14:25:42 +03:00
Nikita Semenov 2c23e86852 fix: Constaints usage 2023-03-15 17:24:20 +03:00
Nikita Semenov 164edf9a5d feat: placeholder api 2023-03-15 17:10:10 +03:00
Nikita Semenov 2f71b10dc0 Merge branch 'feature/new_appearance_configurations' into 'master'
feat: new appearance configurations

See merge request touchinstinct/LeadKit!10
2023-03-15 13:55:42 +00:00
Nikita Semenov 9d99d4e4e3 feat: moved corner configuration to UIViewBorder 2023-03-14 18:27:40 +03:00
Nikita Semenov 9cc412208c docs: updated documentation for skeletons 2023-03-14 16:20:18 +03:00
Nikita Semenov a8fc13ff1e fix: CHANGELOG last version number 2023-03-14 15:44:26 +03:00
Nikita Semenov 22fc660e56 fix: code review notes 2023-03-14 15:43:43 +03:00
Nikita Semenov 5d2bea19fb fix: access control for wrappedViewLayout changed to internal 2023-03-14 14:31:37 +03:00
Nikita Semenov e2c9c6c102 feat: new appearance configurations 2023-03-14 14:29:29 +03:00
Vladimir Makarov 332e895659 Merge branch 'feature/maps_placemark_icon_updating' into 'master'
feat: Placemark appearance updating added

See merge request touchinstinct/LeadKit!3
2023-03-13 09:55:44 +00:00
Vladimir Makarov 9dca21afd9 `BaseItemPlacemarkManager` for single placemark managers added 2023-03-13 10:54:37 +01:00
Vladimir Makarov 511c2b9653 Optional for `placemarkPosition` removed 2023-03-13 10:47:35 +01:00
Vladimir Makarov 2b1511657d `BaseClusterPlacemarkManager` inheritance updated, optional casts removed 2023-03-13 10:47:35 +01:00
Vladimir Makarov f91453a065 Libraries versions updated to 1.38.0, CHANGELOG updated 2023-03-13 10:47:35 +01:00
Vladimir Makarov 23b74ec3d5 `CacheKeyProvider` updated 2023-03-13 10:47:35 +01:00
Vladimir Makarov 4c973b393d `BaseClusterPlacemarkManager` added 2023-03-13 10:47:35 +01:00
Vladimir Makarov 0090c83f87 Libraries versions updated to 1.36.2 2023-03-13 10:47:35 +01:00
Vladimir Makarov 144ea7b703 feat: Placemark appearance updating added 2023-03-13 10:47:35 +01:00
Nikita Semenov 278e175f3a Merge branch 'feature/update_TIDeveloperUtils_dependencies' into 'master'
fix: dependencies

See merge request touchinstinct/LeadKit!9
2023-03-13 08:33:52 +00:00
Nikita Semenov 6af9a64135 fix: dependencies 2023-03-13 11:24:12 +03:00
Nikita Semenov efde6153a8 Merge branch 'feature/skeletons_api' into 'master'
Feature/skeletons api

See merge request touchinstinct/LeadKit!1
2023-03-13 07:46:08 +00:00
Nikita Semenov e3ae781f1d fix: typo + small refactoring 2023-03-13 10:42:17 +03:00
Nikita Semenov 23c2cbacea fix: code review notes 2023-03-11 22:07:06 +03:00
Nikita Semenov 40aa2876d1 fix: code review notes 2023-03-09 22:01:45 +03:00
Nikita Semenov 2250b1b4d9 fix: code review notes 2023-03-09 20:51:25 +03:00
Nikita Semenov 19bb08aa66 feat: added new sys of hidding views below the skeletons 2023-03-09 17:49:37 +03:00
Nikita Semenov 597755474c fix: url to source in podspecs 2023-03-09 12:49:56 +03:00
Nikita Semenov cb29a3e9ca fix: code review notes 2023-03-09 12:48:24 +03:00
Nikita Semenov eafb434c88 Merge branch 'feature/skeletons_docs' into 'feature/skeletons_api'
Feature/skeletons docs

See merge request touchinstinct/LeadKit!5
2023-03-07 17:51:06 +00:00
Nikita Semenov cfd5d5f2f8 fix: code review notes 2023-03-07 20:23:58 +03:00
Nikita Semenov 9c8510af14 feat: updated skeletons documentation for new api and code review comment 2023-03-07 18:42:03 +03:00
Nikita Semenov e942d08503 Merge branch 'feature/skeletons_api' into feature/skeletons_docs 2023-03-07 14:42:07 +03:00
Nikita Semenov 2c081d508f Merge branch 'master' into feature/skeletons_api 2023-03-07 14:39:22 +03:00
Nikita Semenov f01644b408 Merge branch 'feature/changed_skeletons_presenting_system' into 'feature/skeletons_api'
feat: removed SkeletonsPresenter and move all logic to extensions of UIView and UIViewController

See merge request touchinstinct/LeadKit!6
2023-03-07 10:08:20 +00:00
Nikita Semenov b533eaaae6 fix: remove useless baseView 2023-03-07 13:00:34 +03:00
Vladimir Makarov 9db353b360 Merge branch 'feature/yandex_min_deployment_target' into 'master'
TIYandexMapUtils min deployment target updated

See merge request touchinstinct/LeadKit!8
2023-03-07 09:55:09 +00:00
Vladimir Makarov e4c84ca511 `TIYandexMapUtils` min deployment target updated 2023-03-07 10:32:35 +01:00
Vladimir Makarov ce2f3ca064 Merge branch 'feature/yandex_maps' into 'master'
YandexMapsMobile version updated, map manager memory leak removed

See merge request touchinstinct/LeadKit!7
2023-03-07 09:13:08 +00:00
Vladimir Makarov cbd38b84e2 Podspec versions updated 2023-03-07 09:44:26 +01:00
Vladimir Makarov c8a4b0bd51 `YandexMapsMobile` version updated, map manager memory leak removed 2023-03-07 09:41:18 +01:00
Nikita Semenov 7a0747843a feat: removed computed @objc properties 2023-03-07 11:38:09 +03:00
Nikita Semenov 343d36cb85 feat: removed SkeletonsPresenter and move all logic to extensions of UIView and UIViewController 2023-03-07 10:11:56 +03:00
Nikita Semenov 29347d77e1 fix: code review notes 2023-03-06 19:27:56 +03:00
Nikita Semenov 64604bdce0 Merge branch 'master' into feature/skeletons_api
# Conflicts:
#	CHANGELOG.md
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIDeveloperUtils/TIDeveloperUtils.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIWebView/TIWebView.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2023-03-06 19:27:47 +03:00
Nikita Semenov e27c844f92 Merge branch 'fix/remove_logger_api' into 'master'
fix: removed custom logger wrapper

See merge request touchinstinct/LeadKit!4
2023-03-06 11:58:52 +00:00
Nikita Semenov 01d99cb246 docs: change release version 2023-03-06 14:57:52 +03:00
Nikita Semenov 48f6655efc fix: logic of font size determination 2023-03-06 14:55:36 +03:00
Nikita Semenov bc9ac01463 fix: compilation of the projects 2023-03-06 13:40:12 +03:00
Nikita Semenov dda06ece3c fix: dependencies of TIDeveloperUtils module 2023-03-06 10:59:55 +03:00
Nikita Semenov 5ea58f3746 feat: update TIDeveloperUtils module, removed TILogger module 2023-03-06 10:47:09 +03:00
Nikita Semenov 2061050c78 fix: removed custom logger wrapper 2023-03-05 17:52:00 +03:00
Nikita Semenov e199cd4220 docs: skeletons playground page 2023-03-05 17:42:45 +03:00
Nikita Semenov 6e30957fe5 docs: skeletons api playground page 2023-03-05 17:41:11 +03:00
Nikita Semenov 84e7093903 Merge branch 'fix/repo_urls' into 'master'
Fix/repo urls

See merge request touchinstinct/LeadKit!2
2023-03-03 16:30:51 +00:00
Nikita Semenov 1da2d4d501 fix: bump version back + update push to podspec script 2023-03-03 17:47:25 +03:00
Nikita Semenov 118bca1c9d feat: added default implementation of to protocol 2023-03-03 14:39:32 +03:00
Nikita Semenov fbab4a491b fix: code review notes 2023-03-03 14:35:46 +03:00
Nikita Semenov fd2fa45909 feat: padding configuration for skeleton layer 2023-03-03 14:01:48 +03:00
Nikita Semenov e471bae469 docs: bumps podspec version of pods 2023-03-02 16:54:47 +03:00
Nikita Semenov 698243ee39 fix: updated url to ti repositories 2023-03-02 16:48:17 +03:00
Nikita Semenov 59ef1093c7 fix: change default shape of UIImageViews 2023-03-01 20:40:33 +03:00
Nikita Semenov 7442884856 fix: minor changes 2023-03-01 20:39:04 +03:00
Nikita Semenov 6e506aa385 feat: updated exporting of environment variable 2023-03-01 19:16:33 +03:00
Nikita Semenov 9f5d7387d7 feat: skeletons api 2023-03-01 19:05:52 +03:00
Ivan Smolin e9b32ce326
Merge pull request #345 from TouchInstinct/fix/push_podspecs_fixups
build: push podspecs fixups
2023-03-01 09:54:44 +03:00
Ivan Smolin a9a8ddde9e build: push podspecs fixups 2023-02-28 16:59:57 +03:00
Ivan Smolin 893f6f191d
Merge pull request #344 from TouchInstinct/feature/documentation_generation
feat: Auto documentation generation
2023-02-28 15:04:08 +03:00
Ivan Smolin 56d1ee998d build: remove --use-cache flag due build failure 2023-02-28 14:56:36 +03:00
Ivan Smolin 55fe6b7126 docs: fix review notes 2023-02-28 14:56:36 +03:00
Ivan Smolin 5f7e0bf273 feat: Auto documentation generation for `TIFoundationUtils` playground and compile checks for playground before release
`AsyncOperation` fixed ordering of chain operations execution
2023-02-28 14:56:36 +03:00
Nikita Semenov 3a321a7fbf
Merge pull request #343 from TouchInstinct/feature/swiftui_previews
feat: extensions for SwiftUI previews
2023-02-21 09:04:31 +03:00
Nikita Semenov 275afb655f fix: push to podspec update 2023-02-15 12:46:29 +03:00
Nikita Semenov f3c5002f4e feat: added dashed layer support for UIViewController 2023-02-14 20:12:33 +03:00
Nikita Semenov 3406962d21 docs: updated release version in changelog 2023-02-14 19:54:14 +03:00
Nikita Semenov f1d5b27f3d feat: DashedBoundsLayer for debugging frames of the views visually 2023-02-14 19:53:14 +03:00
Nikita Semenov f4a516bf86 feat: rebase preview extensions to new framework TIDeveloperUtils 2023-02-14 17:52:20 +03:00
Nikita Semenov 46be3ce9de feat: extensions for SwiftUI previews 2023-02-14 16:34:46 +03:00
Nikita Semenov 30c5b72b26
Merge pull request #342 from TouchInstinct/feature/base_views
Feature/base views
2023-02-14 14:34:29 +03:00
Nikita Semenov f67005df71
Merge pull request #339 from TouchInstinct/feature/appearance_customization_model
feat: api for configuration Views' appearance and layout
2023-02-14 11:51:55 +03:00
Nikita Semenov 05fa3dad31
Merge pull request #340 from TouchInstinct/feature/view_containers
feat: container views
2023-02-14 11:39:28 +03:00
Nikita Semenov 0c3b987370 fix: code review notes 2023-02-13 21:19:35 +03:00
Nikita Semenov 7765c01074 fix: code review notes 2023-02-12 18:35:28 +03:00
Nikita Semenov f2c390f71a feat: updated method to hide and show subtitle of DefaultTitleSubtitleView 2023-02-12 18:15:31 +03:00
Nikita Semenov 7e41552521 feat: DefaultTitleSubtileView, BaseListItemView, StatefulButton appearance configuration update 2023-02-11 21:22:03 +03:00
Nikita Semenov bd7d31cf67 fix: code review notes 2023-02-10 17:53:21 +03:00
Nikita Semenov 4e1270205e docs: added change log information 2023-02-10 16:28:53 +03:00
Nikita Semenov c5209dc9f6 feat: container views 2023-02-10 15:27:35 +03:00
Nikita Semenov 3ef71a6892 fix: updated 1.33.0 version in changelog 2023-02-09 23:18:11 +03:00
Nikita Semenov 04ee83b8df fix: code review notes 2023-02-09 21:41:52 +03:00
Nikita Semenov 6ff2c8bf37 fix: access to with(appearance:) method 2023-02-09 17:48:14 +03:00
Nikita Semenov 2d3e12164d feat: api for configuration Views' appearance and layout 2023-02-09 15:17:02 +03:00
Nikita Semenov fffe5bd8d4
Merge pull request #336 from TouchInstinct/feature/web_view
Feature/web view
2023-01-30 13:22:26 +03:00
Nikita Semenov f6e00bb53a fix: code review notes 2023-01-30 11:42:06 +03:00
Nikita Semenov a67c42886d Merge branch 'master' into feature/web_view
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2023-01-30 11:37:24 +03:00
Nikita Semenov 407995db35 Merge branch 'master' into feature/deeplink_api
# Conflicts:
#	LeadKit.podspec
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TILogging/TILogging.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2023-01-22 20:11:32 +03:00
Nikita Semenov 63777fef99 fix: update an approach to handling operation in TIDeeplinkService 2023-01-22 20:09:24 +03:00
Ivan Smolin 79a5cc3665
Merge pull request #335 from TouchInstinct/feature/uitextview_url_interactive
feat: URLInteractiveTextView for terms and conditions hints in login flow
2023-01-11 18:28:01 +03:00
Nikita Semenov 0204aa9b10 feat: added json decoder parameter for js error model init 2023-01-11 14:46:37 +03:00
Nikita Semenov 79a5ed4149 fix: remove redundant constants 2023-01-11 14:35:19 +03:00
Nikita Semenov 3bab367ce2 fix: code review notes 2023-01-11 14:19:21 +03:00
Nikita Semenov caeded9561 fix: searching deeplink handler in navigation stack 2023-01-11 13:25:20 +03:00
Nikita Semenov 3d5aa7a41d fix: remove default implementation of deeplink handler in service object 2023-01-11 12:02:07 +03:00
Nikita Semenov 47217f7a59 fix: paramenters for js error model to correspond to js exception models 2023-01-10 20:00:03 +03:00
Nikita Semenov e4c8118b6b fix: code review notes 2023-01-10 19:53:47 +03:00
Nikita Semenov f885183499 fix: updated push to podspecs script 2023-01-10 19:27:53 +03:00
Nikita Semenov aff54859eb feat: complete deeplink api 2023-01-10 19:26:46 +03:00
Nikita Semenov 3e7307424a fix: change types of parameters for js errors 2023-01-09 15:29:41 +03:00
Nikita Semenov a7e44a3d9a feat: initial deep link settings 2023-01-06 07:20:10 +07:00
Nikita Semenov c75ff4c1d0 fix: code review notes 2022-12-30 17:15:46 +07:00
Nikita Semenov b98678b235 fix: code review notes 2022-12-30 02:24:19 +03:00
Ivan Smolin c7ba0d1f30 fix: add link to each part of attributed text 2022-12-27 14:03:08 +03:00
Nikita Semenov 9d3bbc9c71 fix: code review notes 2022-12-27 00:36:31 +03:00
Nikita Semenov 7add7c46ab fix: bump podspec version 2022-12-26 11:19:47 +03:00
Ivan Smolin 724ed400c4 style: review notes 2022-12-26 10:50:29 +03:00
Ivan Smolin ad5fbc96fb fix: initializEable -> initializAble 2022-12-26 10:50:13 +03:00
Nikita Semenov d12e07484d feat: custom webview api 2022-12-26 10:36:58 +03:00
Ivan Smolin 5017e7e7b7 feat: URLInteractiveTextView for terms and conditions hints in login flow 2022-12-23 11:09:55 +03:00
Ivan Smolin 6e442ce37f
Merge pull request #334 from TouchInstinct/feature/pin_code_token_storage
feat: Base classes for encryption and decryption user token with pin code or biometry + Pin code validators
2022-12-21 15:29:11 +03:00
Ivan Smolin e6864f3911 build: move TIFoundationUitls/DataStorage/* to TIFoundationUitls/DataStorage/Sources/ 2022-12-21 15:24:48 +03:00
Ivan Smolin 18cd001ab9 feat: Base classes for encryption and decryption user token with pin code or biometry
Pin code validators
2022-12-19 16:32:44 +03:00
Ivan Smolin 8c696af6d9
Merge pull request #333 from TouchInstinct/feature/base_text_attributes_fixes
fix: correct detection of the necessity of using attributed string
2022-12-15 19:46:22 +05:00
Ivan Smolin 1fb3ca883b
fix typo 2022-12-15 15:50:12 +05:00
Ivan Smolin 3730736319 fix: correct detection of the necessity of using attributed string 2022-12-15 14:03:08 +05:00
Ivan Smolin 9dcdd1c63c
Merge pull request #331 from TouchInstinct/feature/1.29.0_features
BaseTextAttributes, Operation and other
2022-12-09 13:38:54 +03:00
Ivan Smolin ba929fd344 fix: code review notes 2022-12-07 11:05:16 +03:00
Ivan Smolin 2a89de15e3 feat: - Added: BaseTextAttributes can now measure text size and provides paragraph style configuration API.
- Removed: ViewText. Was fully replaced with BaseTextAttributes
- Fixed: Operation.flattenDependencies used in Operation.add(to:waitUntilFinished:) now works correctly.
- Added: Now it's possible to add dependent operation to start of the queue.
2022-12-03 22:55:08 +03:00
Nikita Semenov f7596a73d8
Merge pull request #330 from TouchInstinct/fix/push_to_podspecs
feat: added new pod to the script
2022-11-18 19:25:51 +03:00
Nikita Semenov 39ddcf498f feat: added new pod to the script 2022-11-18 19:23:54 +03:00
Nikita Semenov 98ee540aac
Merge pull request #327 from TouchInstinct/feature/logging_api
Логирование
2022-11-18 19:04:22 +03:00
Nikita Semenov 4778f2e70d feat: code review notes 2022-11-18 18:53:28 +03:00
Nikita Semenov 1c1fa1290b feat: added new system of registering for shacking motion event 2022-11-18 17:13:22 +03:00
Nikita Semenov d2ed1e837a fix: ambiguous usage of view output protocol 2022-11-17 10:32:04 +03:00
Nikita Semenov 401d9365d0 fix: code review notes 2022-11-17 10:30:11 +03:00
Nikita Semenov 5159ee5a4d fix: code review notes 2022-11-11 09:07:25 +03:00
Nikita Semenov dffb4c6015 fix: code review notes 2022-11-10 17:46:25 +03:00
Nikita Semenov f8b7934204 fix: code review notes + change gcd on async/await 2022-11-09 21:14:58 +03:00
Nikita Semenov 9fa4b7c058
Merge branch 'master' into feature/logging_api 2022-11-08 18:15:19 +03:00
Nikita Semenov e1c9596010 fix: default value for presenting method 2022-11-08 18:11:39 +03:00
Nikita Semenov d0576acd95 fix: chacking for opend logging list view controller 2022-11-08 18:06:32 +03:00
Nikita Semenov 13cf92c9c1 refactor: change podspec version + changelog updated 2022-11-08 17:47:01 +03:00
Nikita Semenov d6069db9df Merge branch 'filters_api' into feature/logging_api 2022-11-08 17:36:31 +03:00
Nikita Semenov 69c2a85718 feat: change logic of registration on shaking motion 2022-11-08 17:34:55 +03:00
Nikita Semenov 4ac0eace66 fix: os log store scope 2022-11-08 15:15:48 +03:00
Nikita Semenov 77abc2c5a5 fix: typo 2022-11-08 15:13:01 +03:00
Nikita Semenov 8ffd2b589b fix: check on shacking motion in toggling window 2022-11-08 15:08:29 +03:00
Vladimir Makarov 6ccb8fcdc3
Merge pull request #329 from TouchInstinct/fix/weak_target_reference
Weak target reference capturing in `RefreshControl` fixed
2022-10-18 20:13:42 +05:00
Vladimir Makarov 8e0043cd48 fix: weak target reference in `RefreshControl` fixed 2022-10-18 16:21:38 +05:00
Nikita Semenov 31ddf69dc0
Merge pull request #328 from TouchInstinct/filters_api
fix: podspec configuration
2022-10-11 10:47:36 +03:00
Nikita Semenov 592036951a fix: update min version 2022-10-11 10:47:21 +03:00
Nikita Semenov 01931a9e62 fix: podspec configuration 2022-10-11 09:36:35 +03:00
Nikita Semenov ea13f317b9
Merge pull request #320 from TouchInstinct/filters_api
Фильтры с тегами
2022-10-11 09:28:50 +03:00
Nikita Semenov 7e62f9b571 Merge branch 'master' into filters_api
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIEcommerce/TIEcommerce.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2022-10-11 08:56:10 +03:00
Grigory Boyko 7cfd6f8424 feat: update version 2022-10-11 08:52:46 +03:00
Grigory Boyko 4f0a652e09 fix: add escaping 2022-10-11 08:52:14 +03:00
Grigory Boyko da98e5ff35 fix: add optional 2022-10-11 08:52:14 +03:00
Grigory Boyko 83e5caa13a feat: add failure completion in RequestExecutor 2022-10-11 08:52:14 +03:00
Ivan Smolin a53c015b79 fix: Use OperationQueue instead of NSLock in `DefaultTokenInterceptor` 2022-10-11 08:51:41 +03:00
Grigory Boyko 873b2ecbcb feat: change push_to_podspecs.sh 2022-10-11 08:50:39 +03:00
Nikita Semenov 3157cac5d5
Merge pull request #324 from TouchInstinct/feature/range_filters
Range filters
2022-10-11 08:29:11 +03:00
Nikita Semenov f60a443eed fix: file creator prefix removed 2022-10-04 15:32:42 +03:00
Nikita Semenov f230add1ed refactor: naming changes 2022-10-04 12:41:32 +03:00
Nikita Semenov 444d3b159d fix: code review notes 2022-10-04 12:35:46 +03:00
Nikita Semenov f4dc72b61f refactor: typos 2022-10-04 12:08:38 +03:00
Nikita Semenov 54a01db216 feat: open logs on shacking motion + code review notes 2022-10-04 11:02:05 +03:00
Nikita Semenov 72aabd4412 fix: code review notes + small fixes 2022-10-03 12:42:26 +03:00
Nikita Semenov 070d7c199a fix: code review notes 2022-10-03 12:01:32 +03:00
Nikita Semenov 2d9bb4a5d5 refactor: added license and replace some helper objects 2022-10-03 08:03:25 +03:00
Nikita Semenov 3af9cc5b35 refactor: remove blank space 2022-10-02 22:28:13 +03:00
Nikita Semenov f6f4d2f214 fix: added more visibility for logging presenter button 2022-10-02 21:38:40 +03:00
Nikita Semenov e9edf3ab21 feat: added logger views 2022-10-02 21:11:52 +03:00
Nikita Semenov ff720fca0d feat: initial list view version 2022-09-29 15:44:27 +03:00
Nikita Semenov 31582b3bc5 fix: errors 2022-09-27 20:12:55 +03:00
Nikita Semenov 3bb9e74461 fix: package file update 2022-09-27 20:05:28 +03:00
Nikita Semenov 37818d3153 feat: basic logger 2022-09-27 20:00:44 +03:00
Grigory Boyko e2f9b481ab
Merge pull request #326 from TouchInstinct/feature/add_escaping
fix: add escaping
2022-09-26 14:37:55 +03:00
Grigory Boyko fffa5aa6eb feat: update version 2022-09-26 14:33:11 +03:00
Grigory Boyko 826c66f8af fix: add escaping 2022-09-26 12:34:53 +03:00
Grigory Boyko 91097e2f65
Merge pull request #325 from TouchInstinct/fix/add_failure_completion
feat: add failure completion in RequestExecutor
2022-09-23 18:44:35 +03:00
Grigory Boyko da4389aa03 fix: add optional 2022-09-23 18:34:37 +03:00
Grigory Boyko 81792aba72 feat: add failure completion in RequestExecutor 2022-09-23 15:03:54 +03:00
Nikita Semenov a8785b35e8 fix: code review notes 2022-09-21 19:58:24 +03:00
Nikita Semenov ca7bf6326d refactor: naming fixes 2022-09-21 18:05:17 +03:00
Nikita Semenov 116d2154f8 feat: range filters view 2022-09-21 17:50:13 +03:00
Nikita Semenov dde0eba7a8
Merge pull request #322 from TouchInstinct/feature/list_filters
Фильтры в виде списка
2022-09-06 18:48:26 +03:00
Nikita Semenov f3081861a0 fix: review notes 2022-09-06 15:42:12 +03:00
Nikita Semenov 698b79d10f refactor: spaces in switch 2022-09-05 17:19:28 +03:00
Nikita Semenov 21a73a8f7c fix: code review notes 2022-09-05 17:13:02 +03:00
Nikita Semenov 3696269635 fix: picker selection 2022-09-02 12:01:51 +03:00
Nikita Semenov 92f06c83a4 fix: code review notes 2022-09-02 11:51:37 +03:00
Nikita Semenov 2eea25b4af docs: changelog update 2022-08-31 19:09:48 +03:00
Nikita Semenov 090b86563d fix: multiselection table setup 2022-08-31 19:01:10 +03:00
Nikita Semenov c25ff8c431 fix: picker system 2022-08-31 18:53:40 +03:00
Nikita Semenov 77f6208ff5 fix: logic of picking cells + refactoring 2022-08-31 17:52:56 +03:00
Nikita Semenov eaa787e00f fix: image view size determination 2022-08-30 22:04:44 +03:00
Nikita Semenov 8cbf5b96e6 fix: container table view cell subviews 2022-08-30 22:01:06 +03:00
Nikita Semenov fa130aecdb fix: style in init of table view 2022-08-30 21:58:24 +03:00
Nikita Semenov 635a9bb9b0 fix: image of selected view 2022-08-30 21:51:16 +03:00
Nikita Semenov d16fc21d7e fix: table view delegate methods 2022-08-30 21:45:36 +03:00
Nikita Semenov 4880dcad27 fix: initialisation of table view logic 2022-08-30 21:42:51 +03:00
Nikita Semenov 0bc8574a32 fix: update table director with diffable data source 2022-08-30 21:34:29 +03:00
Nikita Semenov ec1fe892ad fix: merging filters_api 2022-08-30 14:13:11 +03:00
Nikita Semenov d9cdfdec0f Merge branch 'filters_api' into feature/list_filters
# Conflicts:
#	TIEcommerce/Sources/Filters/FiltersCollectionCell/Models/FilterCellStateAppearance.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionCell/Models/FilterCellViewModelProtocol.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionCell/ViewModels/DefaultFilterCellViewModel.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionCell/Views/DefaultFilterCollectionCell.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Helpers/Array+FilterPropertyValueRepresenter.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Helpers/UICollectionViewLayout+DefaultLayout.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Models/DefaultFilterPropertyValue.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Models/FiltersLayoutConfiguration.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Protocols/FilterPropertyValueRepresenter.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/ViewModels/DefaultFilterViewModel.swift
#	TIEcommerce/Sources/Filters/FiltersCollectionView/Views/BaseFiltersCollectionView.swift
#	TIEcommerce/Sources/Filters/Helpers/UICollectionViewLayout+DefaultLayout.swift
#	TIEcommerce/Sources/Filters/Models/BaseFilterCellAppearance.swift
#	TIEcommerce/Sources/Filters/Models/DefaultFilterModel.swift
#	TIEcommerce/Sources/Filters/Models/DefaultFilterPropertyValue.swift
#	TIEcommerce/Sources/Filters/Models/FilterCellViewModelProtocol.swift
#	TIEcommerce/Sources/Filters/Models/FiltersLayoutConfiguration.swift
#	TIEcommerce/Sources/Filters/Protocols/FilterCellAppearanceProtocol.swift
#	TIEcommerce/Sources/Filters/Protocols/FilterPropertyValueRepresenter.swift
#	TIEcommerce/Sources/Filters/Protocols/FilterRepresenter.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Helpers/UICollectionViewLayout+DefaultLayout.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Models/BaseFilterCellAppearance.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Models/DefaultFilterModel.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Models/DefaultFilterPropertyValue.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Models/FiltersLayoutConfiguration.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Protocols/FilterCellAppearanceProtocol.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Protocols/FilterCellViewModelProtocol.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Protocols/FilterPropertyValueRepresenter.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Protocols/FilterRepresenter.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Protocols/FiltersViewModelProtocol.swift
#	TIEcommerce/Sources/Filters/TagsFilters/ViewModels/DefaultFilterCellViewModel.swift
#	TIEcommerce/Sources/Filters/TagsFilters/ViewModels/DefaultFiltersViewModel.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Views/BaseFiltersCollectionView.swift
#	TIEcommerce/Sources/Filters/TagsFilters/Views/DefaultFilterCollectionCell.swift
#	TIEcommerce/Sources/Filters/ViewModels/DefaultFilterCellViewModel.swift
#	TIEcommerce/Sources/Filters/Views/BaseFiltersCollectionView.swift
#	TIEcommerce/Sources/Filters/Views/DefaultFilterCollectionCell.swift
#	TIEcommerce/TIEcommerce.podspec
#	TISwiftUtils/Sources/Extensions/Array/Array+SafeSubscript.swift
2022-08-30 13:59:09 +03:00
Ivan Smolin 6abac15fc7
Merge pull request #323 from TouchInstinct/fix/token_interactor
fix: Use OperationQueue instead of NSLock in `DefaultTokenInterceptor`
2022-08-29 10:40:41 +03:00
Ivan Smolin 0171f9d64f fix: Use OperationQueue instead of NSLock in `DefaultTokenInterceptor` 2022-08-26 22:08:52 +03:00
Nikita Semenov fc6ec80a29 fix: code review notes 2022-08-11 20:01:52 +03:00
Nikita Semenov 9eaf42aa4e fix: code review notes 2022-08-11 14:13:21 +03:00
Nikita Semenov 0abda665bf fix: code review notes 2022-08-11 02:25:58 +03:00
Nikita Semenov 95651b2b58 refactor: remove comments 2022-08-11 00:47:54 +03:00
Nikita Semenov 60d1fd2044 fix: public typealias 2022-08-11 00:45:23 +03:00
Nikita Semenov a253639073 fix: additional appearance configuration 2022-08-11 00:43:15 +03:00
Nikita Semenov 901cdc571f fix: renames + additional configuration 2022-08-11 00:41:18 +03:00
Nikita Semenov 08fdb69d68 fix: code review notes 2022-08-11 00:31:36 +03:00
Nikita Semenov ab9fafeb19 fix: review notes 2022-08-10 16:35:55 +03:00
Nikita Semenov 50d3d5a36b fix: selection and deselection of cells 2022-08-10 15:54:30 +03:00
Nikita Semenov 53a0ced7f6 fix: review notes 2022-08-10 14:26:24 +03:00
Nikita Semenov 3cd6f0a9d0 fix: refactor + applying snapshot 2022-08-09 18:22:38 +03:00
Nikita Semenov 048b5c1f83 fix: applying snapshot 2022-08-09 18:05:51 +03:00
Nikita Semenov 95fc92dfca fix: fix exclusion of cells 2022-08-09 18:02:30 +03:00
Nikita Semenov 73abd949eb fix: remove class property identifier 2022-08-09 17:01:10 +03:00
Nikita Semenov e28d71caf7 fix: cell registration 2022-08-09 16:55:25 +03:00
Nikita Semenov 5eb0183d92 fix: code review notes 2022-08-09 16:41:52 +03:00
Nikita Semenov 373b0db19e fix: cell configuration 2022-08-05 14:50:39 +03:00
Nikita Semenov 95a0045582 fix: review notes 2022-08-05 14:29:18 +03:00
Grigory Boyko 2c8fc0a8a5
Merge pull request #321 from TouchInstinct/feature/ecommerce
feat: change push_to_podspecs.sh
2022-08-05 10:29:06 +07:00
Nikita Semenov 9888bb22d5 fix: base table view constraints 2022-08-04 22:57:55 +03:00
Nikita Semenov 6ef2c83990 fix: access to view model initializer + renames 2022-08-04 22:49:47 +03:00
Nikita Semenov 4c525b8dad feat: list of filters 2022-08-04 22:45:06 +03:00
Nikita Semenov ae52ec03c6 fix: edge insets init + refactoring 2022-08-04 21:34:14 +03:00
Nikita Semenov 7bef631668 fix: usage of generics 2022-08-04 18:49:23 +03:00
Nikita Semenov 2748d5e3b0 fix: type casting 2022-08-04 18:45:38 +03:00
Nikita Semenov 47280c7cd7 fix: review notes 2022-08-04 18:20:08 +03:00
Nikita Semenov c583b8a98a fix: logic of selecting filters 2022-08-04 17:23:58 +03:00
Nikita Semenov 7f6883c7b7 fix: view model configuration 2022-08-04 17:01:19 +03:00
Nikita Semenov ef1eb2d8ac fix: collection view life cycle 2022-08-04 16:53:33 +03:00
Nikita Semenov 0534d41bae fix: update collection view with diffable data source 2022-08-04 16:44:19 +03:00
Nikita Semenov 7c479ff428 fix: default inset of the cells 2022-08-03 21:49:17 +03:00
Nikita Semenov b6321d3b19 fix: collection view life cycle 2022-08-03 21:47:05 +03:00
Nikita Semenov 82d52f4a40 fix: double view model assignment 2022-08-03 21:40:43 +03:00
Nikita Semenov af1c9da523 fix: creating of wrapped view 2022-08-03 21:38:57 +03:00
Nikita Semenov cead71df3d fix: update podscpecs versions 2022-08-03 21:34:07 +03:00
Nikita Semenov 18b48957a4 Merge branch 'master' into filters_api
# Conflicts:
#	CHANGELOG.md
2022-08-03 21:32:23 +03:00
Nikita Semenov edec8bc94f fix: refactoring and review changes 2022-08-03 21:27:30 +03:00
Nikita Semenov 276fbc8c10 fix: excluding selected cells 2022-08-03 20:55:22 +03:00
Nikita Semenov 62aecb0941 fix: layout + collection cell updates 2022-08-03 20:28:15 +03:00
Nikita Semenov c6d17e96ab fix: remove excessive level of an abstraction 2022-08-03 19:57:33 +03:00
Grigory Boyko 8466a4a4db feat: change push_to_podspecs.sh 2022-08-03 23:17:01 +07:00
boykogri 30cf6856b1
Merge pull request #319 from TouchInstinct/feature/ecommerce
Feature/ecommerce
2022-08-03 22:15:22 +07:00
Grigory Boyko b7b95f0ef4 feat: change version 2022-08-03 22:14:30 +07:00
Grigory Boyko 617ebbfde1 fix: Comments from pull request 2022-08-03 16:33:48 +07:00
Grigory Boyko 651892d182 fix: edit changelog 2022-08-02 22:16:37 +07:00
Grigory Boyko 28126dae4b feat: edit changelog and podspec summary 2022-08-02 22:14:38 +07:00
Grigory Boyko b57f205812 feat: add TINetworking dependency 2022-08-02 22:08:44 +07:00
Grigory Boyko 50d59857a7 fix: change podspec to 1.27.0 2022-08-02 21:41:45 +07:00
Grigory Boyko 8cfd3b466c fix: Remove optional for cart 2022-08-02 17:08:03 +07:00
Grigory Boyko 1db3bdb944 feat: Update podspec to 1.27, small models changes 2022-08-02 16:21:00 +07:00
Grigory Boyko d5479f745c feat: Add promocodes and bonuses logic 2022-08-02 12:18:09 +07:00
Grigory Boyko eb66a4e8ca fix: Comments from pull request 2022-08-02 11:38:35 +07:00
Nikita Semenov 76f6635cbc fix: view holder protocol udpate 2022-08-01 19:42:28 +03:00
Nikita Semenov de3aad7147 fix: bug with excluding selected cells 2022-08-01 17:42:56 +03:00
Grigory Boyko 343c40888d fix: add actual changes 2022-08-01 21:11:39 +07:00
Grigory Boyko 179a368fe5 fix: Delete useless protocols, add cartRequestExecutor 2022-08-01 21:09:49 +07:00
Nikita Semenov 50f6670710 fix: default cell corner radius 2022-08-01 16:49:23 +03:00
Nikita Semenov 104116fd36 fix: change default cell appearance 2022-08-01 16:41:45 +03:00
Grigory Boyko 48ac99c7d8 feat: Add receipt, cart service 2022-08-01 19:29:35 +07:00
Nikita Semenov fa29f08d2d fix: bugs with access control 2022-08-01 12:29:42 +03:00
Nikita Semenov 9e96b83c1e fix: refactor conformance to collection holder protocol 2022-08-01 12:25:29 +03:00
Grigory Boyko c92b9bf563 feat: Add CartEditable block 2022-08-01 15:17:21 +07:00
Nikita Semenov c85ab9113b fix: access of filters collection view + change log update 2022-08-01 09:48:57 +03:00
Nikita Semenov 761e4b98bf fix: update podspec version 2022-08-01 09:42:22 +03:00
Grigory Boyko ea1392f279 fix: package.swift file 2022-08-01 08:59:54 +07:00
Nikita Semenov 01c4ef6f44 fix: code refactoring + add additional protocols 2022-07-31 21:01:09 +03:00
Nikita Semenov b52d57a5e7 feat: initial filters api 2022-07-31 13:53:28 +03:00
Grigory Boyko cddae04f2e feat: Add initial files for Cart module 2022-07-29 21:08:11 +07:00
Alexey Gurin 826471c825
Merge pull request #318 from TouchInstinct/feature/pass_url_to_request_error
Pass url to request error
2022-07-28 18:34:54 +04:00
Alexey Gurin 6810fac839 feat: Up dependencies version to 1.25.0 2022-07-28 17:36:07 +04:00
Alexey Gurin a6b63c237b feat: Add changelog for 1.25.0 2022-07-28 17:33:18 +04:00
Nikita Semenov 6eefc607e5
Merge pull request #316 from TouchInstinct/fix/spm_dependencies
fix: dependencies of core libraries
2022-07-28 13:49:42 +03:00
Alexey Gurin db19e24bc4 feat: Update RequestError cases and url passing to them 2022-07-28 14:28:05 +04:00
Alexey Gurin af73b2964f feat: Add url passing to response validation methods 2022-07-28 14:25:32 +04:00
Nikita Semenov b2c6f7b852 fix: dependencies of core libraries 2022-07-28 12:43:54 +03:00
Nikita Semenov 0803060787
Merge pull request #315 from TouchInstinct/feature/alerts_api
alerts api
2022-07-28 11:55:33 +03:00
Nikita Semenov 17a70d613b docs: code review notes 2022-07-27 17:37:34 +03:00
Nikita Semenov a917723dcb docs: code review notes 2022-07-27 16:08:13 +03:00
Nikita Semenov 174d472f1b revert: deleted alert modifier 2022-07-27 14:40:38 +03:00
Nikita Semenov 9a427adab7 fix: code review notes + added custom modifier for alerts 2022-07-27 13:58:44 +03:00
Nikita Semenov cfbf53faf8 fix: code review notes 2022-07-26 15:33:50 +03:00
Nikita Semenov bf46b602a3 fix: code review notes 2022-07-26 15:20:33 +03:00
Nikita Semenov a88a85fe75 fix: move alerts into TIUIKitCore 2022-07-26 15:09:42 +03:00
Nikita Semenov 34c7407ecb docs: SUI alerts 2022-07-26 13:59:25 +03:00
Nikita Semenov ffa66048b0 fix: removed TISwiftUIElements lib and moved components to TISwiftUICore 2022-07-26 13:53:25 +03:00
Nikita Semenov 8138291153 feat: SUI alerts added to different library 2022-07-26 13:40:56 +03:00
Nikita Semenov 7b79d6b250 Merge branch 'master' into feature/alerts_api
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	TIAppleMapUtils/TIAppleMapUtils.podspec
#	TIAuth/TIAuth.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIGoogleMapUtils/TIGoogleMapUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TIMapUtils/TIMapUtils.podspec
#	TIMoyaNetworking/TIMoyaNetworking.podspec
#	TINetworking/TINetworking.podspec
#	TINetworkingCache/TINetworkingCache.podspec
#	TIPagination/TIPagination.podspec
#	TISwiftUICore/TISwiftUICore.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
#	TIYandexMapUtils/TIYandexMapUtils.podspec
2022-07-26 13:02:35 +03:00
Nikita Semenov 693843bb70 fix: code review notes 2022-07-25 19:01:55 +03:00
Nikita Semenov 24d1384e0f
doc: implement documentation for alerts api 2022-07-25 16:36:32 +03:00
Nikita Semenov b96478c248 fix: dialogue type alert actions' styles 2022-07-25 15:53:38 +03:00
Nikita Semenov 37b7224264 feat: added default localization provider for actions + documentation added 2022-07-25 15:34:04 +03:00
Nikita Semenov c223a026f2 fix: custom alerts configuration for sui + version control changes 2022-07-25 14:08:01 +03:00
Ivan Smolin a89c620d91
Merge pull request #314 from TouchInstinct/feature/TIUIKitCore_updates
feat: WrappedViewHolder, ReconfigurableView, UITextView BaseTextAttributes, ReusableUIViewPresenter
2022-07-25 11:48:19 +03:00
Nikita Semenov 000e88f98a fix: small access control fixes 2022-07-24 15:34:09 +03:00
Nikita Semenov 52751dc801 feat: added initial alerts factory implementation 2022-07-24 15:25:28 +03:00
Ivan Smolin 88da2ab508 feat: UITextView now support configuration with BaseTextAttributes
ReconfigurableView & ChangeableViewModel for non-destructing state update
WrappedViewHolder protocol with table/collection view cell implementations
2022-07-18 20:19:02 +03:00
Ivan Smolin 7e319dcb03
Merge pull request #313 from TouchInstinct/fix/DefaultTokenInterceptor_adapt_check
feat: Asynchronous request preprocessing
2022-07-18 13:35:58 +03:00
Ivan Smolin 9828284c20 feat: Asynchronous request preprocessing 2022-06-28 11:13:31 +03:00
Ivan Smolin 5e43ca7fe2
Merge pull request #312 from TouchInstinct/feature/token_interceptor
feat: ApiInteractor, TokenInterceptor, FingerprintsTrustEvaluator and more
2022-06-27 10:58:08 +03:00
Ivan Smolin 8fc1ebab77 feat: ApiInteractor, TokenInterceptor, FingerprintsTrustEvaluator and more 2022-06-17 10:43:35 +03:00
Ivan Smolin e3fbfbd981
Merge pull request #311 from TouchInstinct/feature/request_preprocessor
feat: add request preprocessor for OpenAPI security requirements
2022-06-10 16:09:51 +03:00
Ivan Smolin 784743082d feat: add request preprocessor for OpenAPI security requirements 2022-06-10 14:53:51 +03:00
Ivan Smolin c5db547b82
Merge pull request #310 from TouchInstinct/feature/TIAuth_TISwiftUICore
feat: TIAuth module + SwiftUIPresenter & UIViewControllerPresenter
2022-06-08 17:02:19 +03:00
Ivan Smolin 9230450bb7 fix: code review notes 2022-06-08 13:58:15 +03:00
Ivan Smolin 3d3d94412a feat: Add presenter protocols to `TISwiftUICore` and `TIUIKitCore` modules
Add `CodeConfirmPresenter` protocol and `DefaultCodeConfirmPresenter` implementation in `TIAuth` module
2022-06-02 15:33:29 +03:00
Ivan Smolin 4608de4779
Merge pull request #309 from TouchInstinct/feature/map_managers
Feature/map managers
2022-06-01 14:10:26 +03:00
Ivan Smolin b39b937eaf fix: code review notes 2022-05-26 11:15:55 +03:00
Ivan Smolin c4a4714d37 feat: ability to change markers on map 2022-05-24 13:42:39 +03:00
Ivan Smolin 2050382308 feat: add MapManagers for routine maps configuration 2022-05-24 13:42:38 +03:00
Ivan Smolin ac977d35ed
Merge pull request #307 from TouchInstinct/feature/map_camera_update
feat: add smooth CameraUpdate actions for supported maps
2022-05-24 13:42:13 +03:00
Ivan Smolin 2181f5f0a3 fix: store MarkerManager in userData of YMKMapObject 2022-05-23 18:04:38 +03:00
Ivan Smolin f61bb8ef12 feat: add smooth CameraUpdate actions for supported maps 2022-05-23 17:34:03 +03:00
Timur Kayumov 23b9e8ac7a
Merge pull request #308 from TouchInstinct/feature/recoverable_network_service_errors_forwarding
feat: add error forwarding from error handlers
2022-05-20 12:38:05 +02:00
Timur Kayumov cfa80a3747 feat: add error forwarding from error handlers 2022-05-20 03:36:48 +02:00
Ivan Smolin e18db61746
Merge pull request #306 from TouchInstinct/feature/date_formatters_reuse_pool_thread_safe
fix: DateFormattersReusePool and ISO8601DateFormattersReusePool are now thread safe
2022-04-27 14:00:14 +03:00
Ivan Smolin d568963784 fix: DateFormattersReusePool and ISO8601DateFormattersReusePool are now thread safe 2022-04-27 11:47:53 +03:00
Ivan Smolin e31283cb67
Merge pull request #305 from TouchInstinct/feature/TIMapUtils
TIMapUtils
2022-04-27 11:29:36 +03:00
Ivan Smolin be0681397d fix: code review notes 2022-04-27 09:59:30 +03:00
Ivan Smolin 47ff4d949c feat: TIMapUtils, TIAppleMapUtils, TIGoogleMapUtils and TIYandexMapUtils modules for map items clustering and interacting with them 2022-04-26 14:55:04 +03:00
Ivan Smolin ceab75e3b5 fix: *DateFormatterReusePool workaround for swift downcasting bug 2022-04-18 20:34:16 +03:00
Ivan Smolin 70c4b4e00d
Merge pull request #304 from TouchInstinct/feature/TINetworkingCache
TINetworkingCache
2022-04-18 12:48:09 +03:00
Ivan Smolin 532e54fe9e feat: Network services in TIMoyaNetworking now passes MoyaError in result of EnpointRequest execution.
feat: TINetworkingCache module - caching results of EndpointRequests.
2022-04-15 14:21:28 +03:00
bekray 19134573fa
Merge pull request #303 from TouchInstinct/fix/fix_gray_table_section
fix: gray table section background
2022-04-12 18:53:02 +03:00
Yurii Pakhomov cb50db9f34 fix: renamed empty table section creating method 2022-04-12 18:39:42 +03:00
Yurii Pakhomov 241e7c211e fix: gray table section background 2022-04-12 18:12:38 +03:00
Ivan Smolin 0cf3e4473c
Merge pull request #302 from TouchInstinct/feature/date_formatter_default_locale
feat: DateFormatters properties preset in reuse pools
2022-04-12 17:51:15 +03:00
Ivan Smolin 274baa8b90
Merge pull request #301 from TouchInstinct/fix/query_array_parameter_encoding
fix: array encoding for `QueryStringParameterEncoding`
2022-04-12 17:30:06 +03:00
Ivan Smolin 25265d1797 feat: DateFormatters properties preset in reuse pools 2022-04-12 17:12:17 +03:00
Ivan Smolin 0dffbcbb26 fix: array encoding for `QueryStringParameterEncoding` 2022-04-12 15:13:14 +03:00
Ivan Smolin 9a3d061c63
Merge pull request #300 from TouchInstinct/feature/array_dates_coding
feat: add [Date] coding methods
2022-04-08 12:03:26 +03:00
Ivan Smolin 98f55093ac feat: add [Date] coding methods 2022-04-07 20:25:08 +03:00
Ivan Smolin d31476aaec
Merge pull request #299 from TouchInstinct/feature/plugin_response_processing
DisplayDecodingErrorPlugin and other improvements
2022-04-06 23:04:17 +03:00
Ivan Smolin 10ec9408ad feat: Change access modifiers in `DefaultJsonNetworkService` from `public` to `open`, added additional Moya plugins processing
add `DisplayDecodingErrorPlugin` for showing developer-frendly decoding error messages
add Gemfile for cocoapods versioning
2022-04-06 20:57:55 +03:00
Ivan Smolin e67136013c
Merge pull request #298 from TouchInstinct/fix/another_build_fix
fix: add TIFoundationUtils dependency to TIMoyaNetworking module
2022-04-01 15:19:53 +03:00
Ivan Smolin ab4faa2868 fix: add TIFoundationUtils dependency to TIMoyaNetworking module 2022-04-01 15:15:09 +03:00
Ivan Smolin fa5fffc593
Merge pull request #297 from TouchInstinct/fix/build_failure
fix: remove TIFoundationUtils imports from TIMoyaNetworking
2022-04-01 14:54:23 +03:00
Ivan Smolin 692f448b46 fix: remove TIFoundationUtils imports from TIMoyaNetworking 2022-04-01 14:44:21 +03:00
Ivan Smolin f01096dfb7
Merge pull request #296 from TouchInstinct/feature/date_formatting_fixes
fix: Try parse date in ISO8601 format appending `.withFractionalSeconds` if `.withInternetDateTime` fails
2022-04-01 14:24:49 +03:00
Ivan Smolin db81ff7567 fix: Try parse date in ISO8601 format appending `.withFractionalSeconds` if `.withInternetDateTime` fails 2022-04-01 13:14:55 +03:00
Ivan Smolin 8ce9a0183a
Merge pull request #295 from TouchInstinct/fix/header_parameters_encoding
Fix/header parameters encoding
2022-03-31 21:05:13 +03:00
Ivan Smolin 5f551fd799 feat: conform Cancellables to CancellableTask 2022-03-31 21:02:57 +03:00
Ivan Smolin 8ff4199ed0 fix: HeaderParameterEncoding value transform 2022-03-31 14:16:48 +03:00
Ivan Smolin 11f6b5dbd0 fix: HeaderParameterEncoding array encoding 2022-03-30 20:16:33 +03:00
Ivan Smolin 8a482cc186
Merge pull request #294 from TouchInstinct/feature/recoverable_fixes
fix: DefaultRecoverableNetworkService `request` parameter was renamed to prevent ambiguous reference
2022-03-30 15:57:33 +03:00
Ivan Smolin 0ad47b3f34 fix: DefaultRecoverableNetworkService `request` parameter was renamed to prevent ambgious reference 2022-03-29 21:20:15 +03:00
Ivan Smolin 3e5d8d13fb
Merge pull request #292 from TouchInstinct/feature/tinetworking_nullable_body
Few fixes for code generation
2022-03-29 12:55:54 +03:00
Ivan Smolin 1dded1a7d2
Merge branch 'master' into feature/tinetworking_nullable_body 2022-03-29 12:53:46 +03:00
Vladimir Makarov 2bb6e7d9b2
Merge pull request #293 from TouchInstinct/fix/request_timeout
timeoutIntervalForRequest for sessionConfiguration in NetworkServiceConfiguration added
2022-03-29 14:50:28 +05:00
Ivan Smolin f0f29a4464 fix: recursive call of process(request:errorHandlers:mapMoyaError) 2022-03-29 12:47:41 +03:00
Ivan Smolin 7def493512 feat: EndpointRequest Body can take a nil value as well as Parameter value 2022-03-29 11:43:13 +03:00
Vladimir Makarov 8f6e43f9de timeoutIntervalForRequest for sessionConfiguration in NetworkServiceConfiguration added 2022-03-29 13:34:40 +05:00
Ivan Smolin 1c38c41507
Merge pull request #291 from TouchInstinct/feature/tinetworking_codegen_adaptation
Feature/tinetworking codegen adaptation
2022-03-10 10:24:17 +03:00
Ivan Smolin 373a61835b refactor: change methods ordering according to protection level 2022-03-09 12:27:51 +03:00
Ivan Smolin aacd025382 docs: update CHANGELOG & bump podspec versions 2022-03-05 19:55:13 +03:00
Ivan Smolin b42fcd596f refactor: use default server from network service, simplify recoverable requests 2022-03-05 19:41:50 +03:00
Ivan Smolin b52dd87f55 feat: AdditionalHeadersPlugin for Moya 2022-03-04 17:55:20 +03:00
Ivan Smolin 203e2e9091 feat: ISO8601 DateFormattersReusePool 2022-03-04 17:26:04 +03:00
Ivan Smolin aa2dc3880e feat: add SuccessResponse generic argument to EndpointRequest 2022-03-04 12:18:15 +03:00
Ivan Smolin 6ba07b25ad
Merge pull request #290 from TouchInstinct/feature/recoverable_network_service
feat: add recoverable network service with error handling chain
2022-02-28 11:27:49 +03:00
Ivan Smolin 27c2897b60 feat: add recoverable network service with error handling chain 2022-02-25 14:50:04 +03:00
Ivan Smolin 68e5cb55b1
Merge pull request #289 from TouchInstinct/feature/TIMoyaNetworking
feat: add TIMoyaNetoworking target; async closure typealiases to TISwiftUtils; Date formatting & decoding helpers to TIFoundationUtils
2022-02-24 14:35:35 +03:00
Ivan Smolin 51a40a60f2 feat: async closures support for ClosureAsyncOperation + cancellable support 2022-02-21 17:24:14 +03:00
Ivan Smolin 6cf16b74a7 feat: add TIMoyaNetoworking target; async closure typealiases to TISwiftUtils; Date formatting & decoding helpers to TIFoundationUtils 2022-02-18 22:26:09 +03:00
Ivan Smolin 7c5c76ba0a
Merge pull request #288 from TouchInstinct/fix/TIFoundationUtils_podspec
fix: TIFoundationUtils podspec
2022-02-17 19:36:51 +03:00
Ivan Smolin 72dab661f2 fix: TIFoundationUtils podspec 2022-02-17 19:35:15 +03:00
Ivan Smolin 05c3273fd8
Merge pull request #287 from TouchInstinct/feature/AsyncOperation
feat: add AsyncOperation - generic subclass of Operation with chaining and result observation support
2022-02-17 19:20:51 +03:00
Ivan Smolin 89a0bf247f fix: code style notes 2022-02-17 17:36:28 +03:00
Ivan Smolin 4c7b7a76c1 feat: add AsyncOperation - generic subclass of Operation with chaining and result observation support 2022-02-15 21:30:27 +03:00
Ivan Smolin 3c6a98a582
Merge pull request #284 from TouchInstinct/build/fix_podspecs
build: fix podspecs
2021-10-04 15:00:30 +03:00
Ivan Smolin c1212bb3b0 build: fix podspecs 2021-09-30 12:01:18 +03:00
Ivan Smolin 9df3ccb30d
Merge pull request #283 from TouchInstinct/feature/TINetworking
Feature/ti networking
2021-09-30 10:34:17 +03:00
Ivan Smolin 4b818afa85 refactor: fix code review notes 2021-09-27 10:55:55 +03:00
Ivan Smolin 8397f15ec5 docs: add copyrights 2021-09-27 10:55:32 +03:00
Ivan Smolin b8550bd8d5 refactor: use TISwiftUtils in TINetworking 2021-09-17 19:55:31 +03:00
Ivan Smolin 4082ddb30e Merge branch 'master' into feature/TINetworking 2021-09-17 19:47:43 +03:00
Ivan Smolin c2df8511f2 refactor: fix code review notes 2021-09-17 19:45:06 +03:00
Loupehope 902120a483
Merge pull request #281 from TouchInstinct/feature/pretty_timer
Add pretty timer
2021-09-17 19:06:40 +03:00
Vlad Suhomlinov fc4ae9f9e2 docs: correct typo 2021-09-16 23:33:24 +03:00
Vlad Suhomlinov b4ad6165ff refactor: add convenience init 2021-09-16 11:51:28 +03:00
Vlad Suhomlinov c25dd3656d Merge branch 'master' into feature/pretty_timer
# Conflicts:
#	LeadKit.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TIKeychainUtils/TIKeychainUtils.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
2021-09-16 10:19:22 +03:00
Loupehope f1b72bad8c
docs: update docs 2021-09-16 10:17:46 +03:00
Vlad Suhomlinov 77a8d43499 refactor: add protocol functions 2021-09-16 09:03:50 +03:00
Vlad Suhomlinov d66620d906 refactor: add pause and resume 2021-09-16 08:50:35 +03:00
Vlad Suhomlinov d23257e963 refactor: correct self capture 2021-09-16 00:59:21 +03:00
Ivan Smolin 1fcc478372 build: add podspec for TINetworking & update version 2021-09-14 17:15:42 +03:00
Ivan Smolin 3dd6fa9830 move DecodingClosure out of ResponseType 2021-09-14 16:58:02 +03:00
Ivan Smolin ff47d2c855 Merge branch 'master' into feature/TINetworking 2021-09-14 16:47:16 +03:00
Ivan Smolin 89a3bfe697 ResponseType decoding simplified and other enhancements 2021-09-14 16:46:34 +03:00
Loupehope e249e64ec6
Merge pull request #282 from TouchInstinct/fix/dependencies
Fix: podspec dependencies
2021-09-09 09:15:30 +03:00
Vlad Suhomlinov f9858286d8 Revert "refactor: unable BUILD_LIBRARY_FOR_DISTRIBUTION"
This reverts commit fee9d4ac62.
2021-09-08 21:14:47 +03:00
Vlad Suhomlinov fee9d4ac62 refactor: unable BUILD_LIBRARY_FOR_DISTRIBUTION 2021-09-08 21:10:44 +03:00
Vlad Suhomlinov 762b5352bc refactor: replace class with AnyObject 2021-09-08 21:09:36 +03:00
Vlad Suhomlinov 596a549595 chore: bumo version 2021-09-08 20:37:20 +03:00
Vlad Suhomlinov 4e667225bb fix: podpsec dependecies 2021-09-08 20:37:09 +03:00
Vlad Suhomlinov 91c15c21ed refactor: rename IInvalidatable -> Invalidatable 2021-08-16 13:51:39 +03:00
Vlad Suhomlinov 97e3b15ad8 refactor: correct links 2021-08-16 13:10:06 +03:00
Vlad Suhomlinov 518d8e7f41 refactor: correct PR comments 2021-08-16 13:08:14 +03:00
Vlad Suhomlinov 1c97cb629e refactor: PR comments 2021-08-16 13:06:03 +03:00
Vlad Suhomlinov 58bd8b1eb9 chore: bump version 1.6.0 2021-08-15 19:14:46 +03:00
Vlad Suhomlinov c7a3c46b47 feat: add pretty timer 2021-08-15 19:12:40 +03:00
Ilya 252e00198f
Merge pull request #280 from TouchInstinct/feature/recovery_files_and_remove_unnecessary
recovery files and remove unnecessary
2021-08-06 17:37:35 +03:00
Ilya Salatyuk b1cc752af3 recovery files and remove unnecessary 2021-08-06 17:35:25 +03:00
Ilya 4e7d4356fe
Merge pull request #279 from TouchInstinct/fix/fix_headers
fix incorrect header handling
2021-08-06 17:26:55 +03:00
Ilya Salatyuk ffcec6e4de replace type and remove unnecessary 2021-08-06 00:02:32 +03:00
Ilya Salatyuk 4b908f1e32 fix incorrect header handling 2021-08-05 08:20:16 +03:00
Ivan Smolin 88bc9798b1
Merge pull request #273 from TouchInstinct/feature/header_transition_helper
feat: Add HeaderTransitionDelegate - Helper for transition of TableVi…
2021-07-15 10:04:51 +03:00
Ivan Smolin fb0e1090e5 add TINetworking 2021-07-02 00:56:13 +03:00
Boyko Mihail 8b32bcd89e docs: Update Readme file 2021-06-29 17:55:56 +03:00
Boyko Mihail eea8727d98 fix: pr issue 2021-06-29 17:54:48 +03:00
Boyko Mihail 17f9a79e0a docs: Update Readme file 2021-06-29 17:36:22 +03:00
Boyko Mihail 033335a0b5 docs: Update Readme file 2021-06-29 17:15:32 +03:00
Boyko Mihail 225605b0ac docs: Update Readme file 2021-06-29 17:14:21 +03:00
Boyko Mihail fa7c06fcc9 docs: Update Readme file 2021-06-29 17:13:17 +03:00
Boyko Mihail 5720af623a docs: Update Readme file 2021-06-29 17:11:56 +03:00
Boyko Mihail 564b4e23d8 docs: Update Readme file 2021-06-29 17:10:09 +03:00
Boyko Mihail 28c3808f1a fix: pr issue 2021-06-29 16:47:39 +03:00
Boyko Mihail ce426d1f49 fix: pr issue 2021-06-28 17:28:09 +03:00
Boyko Mihail 6acf0f0b0f fix: Fix pr issuue. Add new protocol and separation other protocol 2021-06-24 20:19:18 +03:00
Boyko Mihail 5e06035236 fix: pr issue. fix conflict 2021-06-22 14:53:46 +03:00
Boyko Mihail c327047bc4 fix: pr issue. fix conflict 2021-06-22 13:43:06 +03:00
Boyko Mihail c873366e7f fix: pr issue. Add animators 2021-06-22 13:39:27 +03:00
Loupehope 67488af9c6
Merge pull request #278 from TouchInstinct/feature/update_deps_and_scripts
Feature: update deps and scripts
2021-06-21 11:10:46 +03:00
Vlad Suhomlinov c68f6fa69f refactor: remove TIPagination podspec 2021-06-21 10:33:54 +03:00
Vlad Suhomlinov 6580042f03 chore: update 2021-06-21 00:12:40 +03:00
Vlad Suhomlinov 80199ccce0 feat: update minor dependencies 2021-06-21 00:10:00 +03:00
Vlad Suhomlinov 71f516af98 docs: add Build dependencies for LeadKit.xcodeproj snippet 2021-06-20 23:35:53 +03:00
Vlad Suhomlinov b39c217c30 fix: podspec’s scripts 2021-06-20 22:48:34 +03:00
Loupehope cd91364c75
Merge pull request #276 from TouchInstinct/feature/tipagination
feat: add realisation of paginating items from a data source
2021-06-19 23:08:30 +03:00
Boyko Mihail 1b58ff5aaa fix: pr issue 2021-06-18 14:07:13 +03:00
Boyko Mihail effad1e51c fix: pr issue 2021-06-18 11:12:55 +03:00
Boyko Mihail a9c7bd6abd fix: pr issue 2021-06-18 11:09:59 +03:00
Boyko Mihail e38ee51275 fix: pr issue 2021-06-18 10:56:55 +03:00
Boyko Mihail ba3211e613 fix: pr issue 2021-06-18 10:35:13 +03:00
Vlad Suhomlinov 68914f0ddc chore: bump 1.3.0 2021-06-17 18:36:04 +03:00
Vlad Suhomlinov 49e6172edf feat: add realisation of paginating items from a data source 2021-06-17 18:04:17 +03:00
Boyko Mihail e20a4b8924 fix: revert readme file 2021-06-17 13:17:52 +03:00
Boyko Mihail 2da25414da fix: change gif 2021-06-05 12:32:06 +03:00
Boyko Mihail 4f02836cf4 fix: align of image in doc 2021-06-05 12:10:06 +03:00
Boyko Mihail fc679c5020 docs: Add gif and change readme file 2021-06-05 12:08:10 +03:00
Boyko Mihail 0810070370 fix: small 2021-06-04 20:38:21 +03:00
Boyko Mihail 2bd3144922 fix: protocols 2021-06-04 20:35:28 +03:00
Boyko Mihail cf81e228ef fix: naming 2021-06-04 20:28:59 +03:00
Boyko Mihail 0095829e31 fix: Fix pr issue and add animation 2021-06-04 20:26:59 +03:00
Boyko Mihail 44b41a6618 fix: remove delegate code 2021-06-03 19:03:56 +03:00
Boyko Mihail 2de6bc2df3 feat: Add HeaderTransitionDelegate - Helper for transition of TableView header and navigationBar title view 2021-06-03 18:41:27 +03:00
Mihail 954080891d
Merge pull request #270 from TouchInstinct/feature/TIKeychainUtils
feat: Add TIKeychainUtils
2021-04-30 19:17:08 +03:00
Boyko Mihail d27c8ab97c feat: Fix dependency (pr issue) 2021-04-30 18:56:50 +03:00
Boyko Mihail 77ca9d05fa feat: fix pr issue 2021-04-30 17:47:58 +03:00
Boyko Mihail ee78f008eb feat: Fix pr issue 2021-04-30 14:55:32 +03:00
Boyko Mihail 529dd9edf8 feat: Fix pr issue 2021-04-30 13:18:27 +03:00
Boyko Mihail d560c035c6 feat: Add TIKeychainUtils 2021-04-29 15:05:38 +03:00
Loupehope 1ad5eed642
Merge pull request #269 from TouchInstinct/feature/useful_scripts
Add useful scripts
2021-04-29 09:49:11 +03:00
Vlad Suhomlinov a6461bd77e feat: add useful scripts 2021-04-29 09:43:26 +03:00
Vlad Suhomlinov e3a6897083 chore: bump version 1.1.2 2021-04-29 09:20:25 +03:00
Vlad Suhomlinov 67d31b108b Merge branch 'master' into feature/useful_scripts 2021-04-29 09:19:05 +03:00
Loupehope 4a1e84773e
docs: add folder filter 2021-04-29 09:11:13 +03:00
Vlad Suhomlinov ea00667963 fix: add .DS_Store to .gitignore 2021-04-29 09:00:20 +03:00
Shalashnikov Maxim e86dcc81f9
Merge pull request #267 from TouchInstinct/fix/fix-StatefullButton-propagation
fix: fix StatefullButton propagation
2021-03-16 16:38:54 +03:00
b0sya e87173b166 docs: update CHANGELOG 2021-03-16 16:32:33 +03:00
b0sya 6c8ff3e682 build: bump version number 2021-03-16 16:29:55 +03:00
b0sya 8655c90f1d refactor: change disabling propagation condition 2021-03-16 16:11:02 +03:00
b0sya 3e64b5cc4e fix: fix StatefullButton propagation 2021-03-16 15:31:31 +03:00
Ivan Smolin 2e1cf62e6f
Merge pull request #266 from TouchInstinct/feature/base_controllers
Feature/base controllers
2021-03-09 11:59:35 +03:00
Vlad 614e574596 docs: add snippets docs 2021-03-05 21:27:14 +03:00
Ivan Smolin 5d449ce92f docs: update CHANGELOG 2021-03-05 13:01:11 +03:00
Ivan Smolin 6472f8a3bb fix: make TableDirectorHolder public 2021-03-05 12:52:33 +03:00
Ivan Smolin 0a195e3309 build: fix TIFoundationUtils build 2021-03-05 12:52:33 +03:00
Ivan Smolin 616a6ec9ed build: bump version number 2021-03-05 12:52:33 +03:00
Ivan Smolin 1225b190f0 feat: add TableDirector extensions for BaseCustomViewController, add TableKitTableView with tableDirector property 2021-03-05 12:08:15 +03:00
Ivan Smolin 44306a5863 feat: add BaseControllers 2021-03-05 12:08:15 +03:00
Ivan Smolin d3e07fd550 feat: add UserDefaultsBackingStore 2021-03-05 12:08:15 +03:00
Ivan Smolin f3dcef4690 fix: creation of bottomSeparator for BaseSeparatorCell 2021-03-05 12:08:15 +03:00
Ivan Smolin 1efc4c2bf0 fix: set allHostsMustBeEvaluated of SessionManager to false, so we can download 2021-03-05 12:08:15 +03:00
Loupehope 7b5b6ed945
Merge pull request #264 from TouchInstinct/feature/update_dependecies
Update dependencies
2021-01-29 13:02:40 +03:00
Vlad 5fd69cae98 refactor: update info.plist 2021-01-29 09:54:17 +03:00
Vlad 3d683f44bc Merge branch 'master' into feature/update_dependecies
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	TIFoundationUtils/TIFoundationUtils.podspec
#	TISwiftUtils/TISwiftUtils.podspec
#	TITableKitUtils/TITableKitUtils.podspec
#	TITransitions/TITransitions.podspec
#	TIUIElements/TIUIElements.podspec
#	TIUIKitCore/TIUIKitCore.podspec
2021-01-29 09:27:24 +03:00
Loupehope 151022a22c
Merge pull request #265 from TouchInstinct/fix/podspec_file
Fix podspec file
2021-01-26 10:46:42 +03:00
Vlad 50ca1c7d79 chore: bump version 2021-01-25 17:43:02 +03:00
Vlad 9b115370f7 fix: LeadKit.podspec file 2021-01-25 17:40:15 +03:00
Loupehope f9af35e558
Merge pull request #263 from TouchInstinct/feature/git_hooks
Add prepare-commit-msg to check commit style
2021-01-25 16:31:09 +03:00
Vlad d264b23089 refactor: update to RxSwift 6 2021-01-23 21:53:08 +03:00
Vlad c5403c0ba9 refactor: up version 2021-01-23 21:47:24 +03:00
Vlad 1ab3993c7e docs: add page for semantic commit messages 2021-01-23 15:32:25 +03:00
Vlad 31c0f2c93a refactor: @petropavel13 comments 2021-01-17 21:37:27 +03:00
Vlad 5afbc2ffa5 refactor: update swiftlint to 39.1 2021-01-11 23:26:23 +03:00
Vlad 7ffac03d73 feat!: update dependencies and project settings 2021-01-11 23:18:29 +03:00
Loupehope 6700252ef6
docs: correct typo 2021-01-11 12:50:31 +03:00
Vlad ffe4098937 fix: carthage install 2021-01-09 20:17:12 +03:00
Vlad e2c9c937b8 refactor: correct githook and setup script 2021-01-07 18:51:21 +03:00
Vlad 57c010f4f2 refactor: correct setup script 2021-01-07 18:46:47 +03:00
Vlad 9113f0b7df feat: add prepare-commit-msgto check commit style 2021-01-07 18:15:43 +03:00
Ivan Smolin 7408446b18
Merge pull request #258 from TouchInstinct/feature/stateful_button_and_more
StatefulButton and more
2020-12-24 14:22:03 +03:00
Ivan Smolin 2e84f5cb2d fix code review notes 2020-12-24 11:37:00 +03:00
Ivan Smolin 102c15ca07 added StatefulButton & RoundedStatefulButton.
added CACornerMask rounding extension.
added UIControl.State dictionary extensions.
added UIFont registration.
reworked BaseTextAttributes & ViewText.
removed ViewTextConfigurable.
2020-12-23 16:45:36 +03:00
MRSorokinMaxim f3fe5e08d8
Merge pull request #256 from TouchInstinct/titransaction_podspec
fix autor for validate spec
2020-11-11 16:39:31 +03:00
Maxim Sorokin 4f8d0b537d fix author name 2020-11-11 16:39:04 +03:00
Maxim Sorokin 6fbf1e0eff Revert "fix name author"
This reverts commit c997eb2bfb.
2020-11-11 16:38:32 +03:00
Maxim Sorokin c997eb2bfb fix name author 2020-11-11 16:35:13 +03:00
Maxim Sorokin 7102cccc60 fix autor for validate spec 2020-11-11 16:18:11 +03:00
MRSorokinMaxim 0f518c3c88
Merge pull request #255 from TouchInstinct/titransaction_podspec
[Add] TiTransactios podspec
2020-11-11 16:04:59 +03:00
Maxim Sorokin 00c72992ca [Add] TiTransactios podspec 2020-11-11 15:47:25 +03:00
Ivan Smolin 6b3bf25f0a
Merge pull request #254 from TouchInstinct/feature/separators
Feature/separators
2020-10-23 18:34:00 +03:00
Ivan Smolin 8b1e20d790 update README 2020-10-23 17:25:50 +03:00
Ivan Smolin 53e25e3a13 update podspec version to 0.11.0 2020-10-23 17:25:30 +03:00
Ivan Smolin 18d7e5c5b6 update BaseSeparatorCell layout configuration 2020-10-23 17:24:26 +03:00
Ivan Smolin 6433233d9e rename configure(with:) -> configureSeparators(with:) 2020-10-23 15:57:24 +03:00
Ivan Smolin 0a7f68bd23 update podspec and add missing extensions for TableKit 2020-10-23 12:44:49 +03:00
Ivan Smolin 281f0b72b9 add podspecs 2020-10-23 11:31:48 +03:00
Ivan Smolin dfbe18fedd update READMEs 2020-10-23 10:24:22 +03:00
Ivan Smolin e92b46eb51 rename InitializableView -> InitializableViewProtocol, move base elements to TIUIElements 2020-10-22 12:44:42 +03:00
Ivan Smolin 64cb766542 add missing ViewTextConfigurable extensions 2020-10-21 22:55:14 +03:00
Ivan Smolin 4af10aa107 add ConfigurableView 2020-10-21 22:37:20 +03:00
Ivan Smolin 6fdb6af732 add ViewText and BaseTextAttributes 2020-10-21 22:34:45 +03:00
Ivan Smolin 55977e39f5 add TableKitUtils with BaseSeparatorCell 2020-10-21 22:06:54 +03:00
Mihail 7b7fcab29e
Merge pull request #251 from TouchInstinct/fix/protection_to_open
Change protection level to open
2020-10-05 12:44:44 +03:00
Boyko Mihail 22ba1fb8f7 Change protection level to open 2020-10-05 12:41:12 +03:00
Mihail e1ba5e5788
Merge pull request #250 from TouchInstinct/fix/Add_presentedOrTopViewController
Add presentedOrTopViewController
2020-10-05 10:42:25 +03:00
Boyko Mihail f797ea2d8f Update version 2020-10-05 10:34:25 +03:00
Boyko Mihail 4c81de418f Add presentedOrTopViewController 2020-10-05 10:31:48 +03:00
Mihail 1e903ad14d
Merge pull request #249 from TouchInstinct/fix/Add_BaseOrientationController_and_videoOrientation_extension
Fix/add base orientation controller and video orientation extension
2020-10-05 08:38:00 +03:00
Boyko Mihail 76b6822ce5 Fix indent 2020-10-02 15:55:11 +03:00
Boyko Mihail 6b735be733 Fix PR issue 2020-10-02 15:53:47 +03:00
Boyko Mihail 5427b0f1be Fix indent 2020-10-01 20:59:45 +03:00
Boyko Mihail c3827fcaf2 Update version and changelog 2020-10-01 20:55:41 +03:00
Boyko Mihail 702f518e50 fix public extension 2020-10-01 20:44:33 +03:00
Boyko Mihail 451d99d36d cherry-pick 28c51ae7f6 2020-10-01 20:40:55 +03:00
Boyko Mihail 9ae23ecef4 Add videoOrientation extension 2020-10-01 20:38:12 +03:00
Boyko Mihail 7011435905 cherry-pick ccc085df3c 2020-10-01 20:38:01 +03:00
Mihail 0a1b2547b5
Merge pull request #248 from TouchInstinct/fix/tv_exclude
Add exlude files
2020-10-01 12:54:47 +03:00
Boyko Mihail 9991408b32 Add new exlude 2020-10-01 11:14:54 +03:00
Boyko Mihail 16a6f63ba2 Add new exlude 2020-10-01 10:59:59 +03:00
Boyko Mihail 6d8ba62316 Update version 2020-09-30 15:55:36 +03:00
Boyko Mihail 12a47d8b24 add comma 2020-09-30 11:46:42 +03:00
Boyko Mihail f7b9732d9d Add exlude files 2020-09-30 11:42:25 +03:00
Mihail a56ecd5f79
Merge pull request #247 from TouchInstinct/feature/Add_orientation_viewController_logic
Add orientation viewController Extension
2020-09-25 12:37:54 +03:00
Boyko Mihail ab09d9e289 Fix PR issue 2020-09-24 18:36:35 +03:00
Boyko Mihail 98e0e34afe Fix podspec 2020-09-23 15:40:57 +03:00
Boyko Mihail cf92942e61 Fix PR issue from Ivan Smolin 2020-09-23 14:48:08 +03:00
Boyko Mihail 45d88b4cf6 Add orientation viewController Extension 2020-09-23 13:42:38 +03:00
Loupehope 690c5f8a8c
Merge pull request #246 from TouchInstinct/fix/no_connection_error
No connection error
2020-09-10 14:03:06 +03:00
Vlad 9a9ebcb627 Code correction 2020-09-10 12:44:23 +03:00
Vlad 32c711823b Up version 2020-09-10 12:42:01 +03:00
Vlad 7d4c8e5294 Fix no connection error handling 2020-09-10 12:37:35 +03:00
Loupehope 5be71ec033
Merge pull request #245 from TouchInstinct/fix/SessionManager
Fix mappingQueue in SessionManager
2020-09-04 19:26:18 +03:00
Vlad 6c75bbe884 Remove custom requestQueue and serializationQueue 2020-09-04 19:19:25 +03:00
Vlad bd8542fe85 Code correction 2020-09-04 19:05:13 +03:00
Vlad 4ba7e639e8 Fix naming 2020-09-04 19:02:08 +03:00
Vlad 2f8c5aa7b3 Update changelog 2020-09-04 18:54:43 +03:00
Vlad e45b566ed2 Fix mappingQueue in SessionManager 2020-09-04 18:46:15 +03:00
Loupehope 5e15fbc857
Merge pull request #240 from TouchInstinct/feature/refresh_control
Rrefresh control
2020-09-04 12:26:09 +03:00
Vlad 79d8f2a05e Merge branch 'master' into feature/refresh_control
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
2020-09-04 12:25:23 +03:00
Loupehope d884203af5
Merge pull request #244 from TouchInstinct/feature/update_dep
Up version of Alamofire and RxAlamofire
2020-09-04 09:25:42 +03:00
Vlad f9f62b3fe7 Update changelog 2020-09-03 21:20:17 +03:00
Vlad ec02ac7be6 Up version of Alamofire and RxAlamofire 2020-09-03 21:19:30 +03:00
Vlad 80f26d932c Up version of Alamofire and RxAlamofire 2020-09-03 21:08:54 +03:00
Nikita 3c90aff617
Merge pull request #243 from TouchInstinct/feature/podspecs_update
Feature/podspecs update
2020-09-02 10:20:49 +03:00
krasich74 445ff1a644 completly remove files 2020-09-01 17:21:37 +03:00
krasich74 419685ae2d Fixed pr comments pt.2 2020-09-01 16:53:09 +03:00
krasich74 75796c0ade Fixed pr comments pt.1 2020-09-01 16:12:35 +03:00
krasich74 fd4402b368 Merge remote-tracking branch 'origin/master' into feature/podspecs_update 2020-09-01 14:40:32 +03:00
krasich74 94bd1e9c36 Updated changelog 2020-09-01 14:38:44 +03:00
krasich74 52b7485755 Updated podspecs 2020-09-01 14:24:36 +03:00
krasich74 bba6c965c7 Updated server trust policies configuration 2020-09-01 14:15:10 +03:00
Vlad 7f05a98894 Fix self capture 2020-09-01 10:19:47 +03:00
Vlad a3d0131197 Fix git 2020-08-31 23:11:38 +03:00
Vlad 942ad1fb57 Fix typo 2020-08-31 23:11:15 +03:00
Vlad eaea21c27b Merge branch 'master' into feature/refresh_control
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
2020-08-31 23:10:34 +03:00
Vlad 86581223ea Add comments 2020-08-31 23:09:34 +03:00
Ivan Smolin 4ac04996ac
Merge pull request #242 from TouchInstinct/feature/tifoundationutils_codable_key_value_storage
Feature/tifoundationutils codable key value storage
2020-08-31 12:07:20 +03:00
krasich74 d595e0331f Merge remote-tracking branch 'origin/master' into feature/podspecs_update 2020-08-31 09:30:36 +03:00
Ivan Smolin bb6633f3ce Add changelogs and up version 2020-08-28 19:31:54 +03:00
Ivan Smolin 24898b5ed1 add CodableKeyValueStorage with propertyWrapper 2020-08-28 19:18:09 +03:00
Loupehope 4d590e9bc5
Merge pull request #241 from TouchInstinct/fix/OTPView_dependecy
Fix OTPSwiftView’s dependencies
2020-08-28 18:14:43 +03:00
Vlad a51e31ec96 Fix OTPSwiftView’s dependencies 2020-08-28 18:09:58 +03:00
Vlad d29903d51b Code correction 2020-08-26 23:51:46 +03:00
Vlad b2b386185c Up version 2020-08-26 23:41:44 +03:00
Vlad af43eb9da7 Add Refresh Control 2020-08-26 23:40:46 +03:00
MRSorokinMaxim 0aacf470cb
Merge pull request #239 from TouchInstinct/paginator
Paginator
2020-08-26 13:37:15 +03:00
Maxim Sorokin a4dfe52351 remove unused code 2020-08-26 13:21:42 +03:00
Maxim Sorokin fba3271181 fix behavior of refresh with UIRefresControll 2020-08-26 12:15:32 +03:00
Maxim Sorokin 3db011bb3f fix naming 2020-08-26 00:25:17 +03:00
Maxim Sorokin ab2f849d2b up version 2020-08-26 00:21:46 +03:00
Maxim Sorokin 95923bc9ca fix pagination wrapper 2020-08-26 00:18:56 +03:00
Loupehope ebd8ab23a3
Merge pull request #237 from TouchInstinct/feature/otpview
OTPview and BaseInitializableControl
2020-08-25 21:35:40 +03:00
Vlad cda62768dd Add closures 2020-08-25 20:49:25 +03:00
Vlad 5b644e1ed3 Merge branch 'master' into feature/otpview
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
2020-08-24 23:31:29 +03:00
Vlad 11942c327b Up version 2020-08-24 23:30:35 +03:00
Vlad a4ddd994cd Add TISwiftUtils and code correction 2020-08-24 23:28:32 +03:00
Loupehope 46be8249d8
Merge pull request #238 from TouchInstinct/fix/pagination_wrapper_multy_requests
Fix load more request repetion in PaginationWrapper
2020-08-24 20:44:27 +03:00
Vlad 2164d4fb3b Up version 2020-08-24 19:16:53 +03:00
Vlad 61e796f278 Replace rx bindings with target 2020-08-24 19:00:58 +03:00
Vlad 6a8f050e0c Fix PaginationWrapper multy requests 2020-08-24 18:54:09 +03:00
Vlad cba4833d46 Code corresction 2020-08-23 23:47:56 +03:00
Vlad bb125aa428 Up version 2020-08-22 20:43:46 +03:00
Vlad 285383e595 Add OTPSwiftView and BaseInitializableControl 2020-08-22 20:40:24 +03:00
Loupehope be8ba32d46
Merge pull request #235 from TouchInstinct/feature/TIActivityIndicators
Add Animatable to TIUIKitCore and add TIUIElements
2020-08-21 11:14:29 +03:00
Vlad 265860c966 Merge branch 'master' into feature/TIAnimatable
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
2020-08-21 11:13:28 +03:00
MRSorokinMaxim 37c8d736db
Merge pull request #233 from TouchInstinct/feature/container_cell_and_base_tap_view_model
Feature/container cell and base tap view model
2020-08-20 09:55:16 +03:00
Vlad 90b82edd64 Code correction 2020-08-19 23:50:50 +03:00
Vlad eabfa7ec01 Code correction, fix iOS version 2020-08-19 23:48:00 +03:00
Vlad eaf58da24a Update readme 2020-08-19 23:38:47 +03:00
Vlad ec29cb0e27 Update Readme 2020-08-19 23:37:25 +03:00
Vlad 00c60e6fb0 Update changelog 2020-08-19 23:35:57 +03:00
Vlad 2808d015bf Fix PR 2020-08-19 23:35:02 +03:00
Loupehope b6ef182d56
Update README.md 2020-08-19 16:04:10 +03:00
Vlad 16e2d222a4 Up version 2020-08-19 15:54:51 +03:00
Vlad 91e6906145 Add Animatable to TIUIKitCore and add TIActivityIndicators 2020-08-19 15:47:31 +03:00
Maxim Sorokin a227d74db5 added disposeBag 2020-08-19 11:04:27 +03:00
Maxim Sorokin 058f50215d fix public –> private for wrappedView 2020-08-18 18:01:13 +03:00
Maxim Sorokin dd4e8e2c7b up version 2020-08-18 17:44:58 +03:00
Maxim Sorokin fc82a8539c Merge branch 'master' into feature/container_cell_and_base_tap_view_model
# Conflicts:
#	CHANGELOG.md
2020-08-18 17:44:08 +03:00
Loupehope 2032c17556
Merge pull request #234 from TouchInstinct/fix/pagination_animation
ScrollView content offset and load more crash
2020-08-18 15:53:05 +03:00
Vlad eb86d72394 Code correction 2020-08-18 15:50:27 +03:00
Vlad f84a5a643d Up version 2020-08-18 15:21:53 +03:00
Vlad ae76c806ae Fix scrollview offset and request crash 2020-08-18 15:19:19 +03:00
Maxim Sorokin dfa4a7c0f9 fix naming 2020-08-18 13:48:17 +03:00
Maxim Sorokin b273d2c118 Up version and update change log 2020-08-18 12:42:23 +03:00
Maxim Sorokin f3cd8bec97 Refactoring BaseTableViewCell 2020-08-18 12:12:48 +03:00
Maxim Sorokin 7924886b70 [Add] BaseTappableViewModel and VoidTappableViewModel 2020-08-18 12:06:33 +03:00
Maxim Sorokin f51d26311d [Add] BaseTableViewCell and ContainerTableCell 2020-08-18 11:55:48 +03:00
Loupehope 6b2230b4ce
Merge pull request #232 from TouchInstinct/feature/spm
Add SPM with TITransitions and TIUIKitCore
2020-08-10 13:01:52 +03:00
Vlad e21ba045fa Fix PR from petropavel13 2020-08-10 13:01:18 +03:00
Vlad 59367c1eb0 Update readme of frameworks 2020-08-09 15:31:43 +03:00
Loupehope 153a404d5f
Update README.md 2020-08-09 15:29:10 +03:00
Vlad b6079e175c Up version and update changelog 2020-08-09 15:17:21 +03:00
Vlad d99f926222 Add SPM with TITransitions and TIUIKitCore 2020-08-09 13:05:08 +03:00
Loupehope 6c94eb8948
Merge pull request #231 from TouchInstinct/feature/runloop_for_refreshing
Add runloop for refreshing
2020-08-08 15:19:39 +03:00
Vlad fa7260cab2 Remove available check 2020-08-08 02:10:37 +03:00
Vlad 174885d172 Update changelog and version 2020-08-08 00:57:36 +03:00
Vlad a962e3f9f2 Add available check 2020-08-08 00:51:31 +03:00
Vlad 1c98858aa9 Add runloop block execution for refresh control 2020-08-08 00:51:01 +03:00
Loupehope d1d5f46378
Merge pull request #230 from TouchInstinct/feature/footerView
Replace retry button with retry view and add ButtonHolder protocol
2020-08-07 18:16:37 +03:00
Vlad b4b8785cdc Fix typo 2020-08-07 14:43:37 +03:00
Vlad 0f5a0d165c Add ButtonHolderView conformance 2020-08-07 14:36:24 +03:00
Vlad a524ff7855 Fix naming 2020-08-07 14:34:21 +03:00
Vlad 5fc4625bf6 Fix conforming 2020-08-07 14:23:26 +03:00
Vlad 2c7c2c4ba5 Fix visibility 2020-08-07 14:21:52 +03:00
Vlad f0387692bc Update changelog 2020-08-07 14:14:16 +03:00
Vlad fcc5805dac Replace retry button with retry view 2020-08-07 14:10:21 +03:00
MRSorokinMaxim e9ad33785a
Merge pull request #229 from TouchInstinct/update/customizableButtonView
Update/customizable button view
2020-07-23 17:14:05 +03:00
Maxim Sorokin ab52e832dc up version to 0.9.33 and chenged log 2020-07-23 16:57:24 +03:00
Maxim Sorokin 5fb90f6069 fix compile error 2020-07-23 16:22:43 +03:00
Maxim Sorokin 622c722a59 add buttonTitle property in CustomizableButtonView 2020-07-23 15:57:22 +03:00
Maxim Sorokin 74d4be3635 fix button appearance, remove buttonStateTitles from Appearance, add buttonTitle into CustomizableButtonViewModel 2020-07-23 15:48:10 +03:00
Maxim Sorokin f0e25196a2 added public access specifier and alpha property 2020-07-22 16:51:59 +03:00
MRSorokinMaxim 0172b222b0
Merge pull request #226 from TouchInstinct/update/customizableButtonView
Update/customizable button view
2020-07-22 11:49:15 +03:00
Maxim Sorokin e2ab9d817c fix open -> public private(set) disposeBag and fix typo 2020-07-22 10:43:41 +03:00
Maxim Sorokin f7a66a5dba remove unnecessary 2020-07-22 10:10:12 +03:00
Maxim Sorokin 3430344703 up version to 0.9.32 and chenged log 2020-07-22 10:07:22 +03:00
Maxim Sorokin 80544b342a update CustomizableButtonView 2020-07-20 23:02:10 +03:00
Maxim Sorokin b668f76c4a added layoutIfNeeded and setNeedsDisplay to customButtomView and fixed enable button is enabled 2020-07-20 22:24:38 +03:00
Maxim Sorokin 3d0b1af04f fixed button state the enabled 2020-07-20 21:13:01 +03:00
Maxim Sorokin 34d75efff8 make disposeBag is open 2020-07-20 20:44:34 +03:00
Maxim Sorokin d4d8c3b9b1 update CustomizableButtonView 2020-07-20 13:56:25 +03:00
Maxim Sorokin b6d4a5f7ad update CustomizableButtonView 2020-07-20 13:31:20 +03:00
krasich74 1850366709 Fixed Alamofire precondition 2020-06-29 12:57:53 +03:00
krasich74 5a39fe10cd Performed migration from Alamofire 4 to Alamofire 5
Removed .swift-version in favour of swift version in podspecs
2020-06-09 16:28:50 +03:00
krasich74 1bd8941af4 podspecs 2020-05-07 20:05:35 +03:00
krasich74 b6be1163b3 Updated Podspecs to use RxSwift 5.1.0 2020-05-07 19:57:30 +03:00
Loupehope dc102b1f73
Merge pull request #225 from TouchInstinct/feature/add_@discardableResult_to_replace_section
[Add] @discardable result to replace section
2020-04-15 15:55:54 +03:00
Vlad 0833982bf0 [Update] changelog 2020-04-15 15:34:18 +03:00
Vlad a516deef00 [Update] indent 2020-04-15 15:32:55 +03:00
Vlad d8868d1aca Update changelog and podspec 2020-04-15 15:32:00 +03:00
Vlad 7cffa29c7f [Add] @discardableResult to func replace(with section…) 2020-04-15 15:25:04 +03:00
Mihail f7410622b1
Merge pull request #224 from TouchInstinct/Change_String_TelpromptUR
Change String+TelpromptURL. Add * to removable characterSet
2020-02-21 12:22:23 +03:00
Boyko Mihail 8f26e41b52 Update changelog and podspec files 2020-02-21 11:56:19 +03:00
Boyko Mihail 5da96e5a8f Change String+TelpromptURL. Add * to removable characterSet 2020-02-20 18:05:24 +03:00
Vitaliy Salnikov 9e82df42c5
Merge pull request #223 from TouchInstinct/feature/comparable_in_range
Overload 'in' method for Comparable to use ClosedRange
2020-01-30 18:36:18 +03:00
Vitaliy Salnikov 44ccb8dd28 Overload 'in' method for Comparable to use ClosedRange 2020-01-23 16:33:20 +03:00
Ivan Smolin 16b209cd59
Merge pull request #222 from TouchInstinct/carthage_without_binary
Carthage without binary
2020-01-22 19:42:59 +03:00
Ivan Smolin a023e164e1 update changelog 2020-01-22 19:13:54 +03:00
Ivan Smolin 54bb41e5ff fix swiftlint issues 2020-01-22 14:10:15 +03:00
Ivan Smolin 1f6370fd73 update dependencies, pin swiftlint version 2020-01-14 12:50:58 +03:00
Ivan Smolin 986e2d0cf1 carthage without binaries 2020-01-13 17:22:48 +03:00
Sergey 0de81939ad
Merge pull request #219 from TouchInstinct/feature/fullScreenPresent
add UIViewController extension with full screen present
2019-10-04 15:39:44 +03:00
Sergey Kopytov 6de58594ea fix typo 2019-10-04 14:21:50 +03:00
Sergey Kopytov cdebfbebdd add presentation mode property 2019-10-04 14:19:05 +03:00
Sergey Kopytov 090c066a75 add comment 2019-10-03 23:15:18 +03:00
Sergey Kopytov 8b13419da4 fix version 2019-10-03 23:08:39 +03:00
Sergey Kopytov 87d5f91d2d add UIViewController extension with full screen present 2019-10-03 21:01:12 +03:00
Ivan Babkin 7237efe641
Merge pull request #218 from TouchInstinct/feature/date_formats_support
Add method for DateFormattingService that parses date from string in …
2019-09-09 15:14:43 +03:00
Ivan Babkin 31ea1157ce Fix version in changelog 2019-09-09 14:56:15 +03:00
Ivan Babkin 21563377f4 Add method for DateFormattingService that parses date from string in one of the given formats with current region. 2019-09-09 14:54:43 +03:00
Ivan Babkin 1832520a6d
Merge pull request #217 from TouchInstinct/fix/cursor
Add method for processing cursor response
2019-08-30 14:08:21 +03:00
Ivan Babkin f01730d4c8 Fix changelog 2019-08-30 14:06:52 +03:00
Ivan Babkin 31bb4d9798 Add method for processing cursor response 2019-08-30 14:06:23 +03:00
Ivan Babkin 6e1e4de0b1
Merge pull request #216 from TouchInstinct/fix/open_cursor
Make TotalCountCursor open and not final
2019-08-30 12:45:00 +03:00
Ivan Babkin dac88ebe98 Make TotalCountCursor open and not final 2019-08-30 12:22:15 +03:00
Ivan Babkin 97ec620d8c
Merge pull request #214 from TouchInstinct/feature/query_params
Add possibility to pass query items to ApiRequestParameters
2019-08-30 12:13:43 +03:00
Ivan Babkin bd0bc54fdd Merge branch 'master' into feature/query_params 2019-07-25 16:30:16 +03:00
Ivan Babkin 0ff4c685d4
Merge pull request #215 from TouchInstinct/fix/api_parameters
Make url and parameters public for ApiRequestParameters
2019-07-10 12:24:50 +03:00
Ivan Babkin 44bc8dfefc Make ApiRequestParameters properties public 2019-07-10 12:23:04 +03:00
Ivan Babkin 4d21fe0577 Refactoring 2019-07-01 15:38:10 +03:00
Ivan Babkin b3dcd06298 Remove Alamofire importing 2019-06-28 15:45:07 +03:00
Ivan Babkin 3ed9d949a4 Add possibility to pass query items to ApiRequestParameters 2019-06-28 15:42:51 +03:00
Ivan Babkin 6f1d4a0dba
Merge pull request #211 from TouchInstinct/fix/rounding
Fix/rounding
2019-06-10 16:18:00 +03:00
Ivan Babkin c12131de39 Version up, updated changelog 2019-06-10 14:55:17 +03:00
Ivan Babkin 2700848d50 Fixed double rounding, added decimal rounding 2019-06-07 20:13:30 +03:00
Aliona 67a02f62e6
Merge pull request #210 from TouchInstinct/upd_initializable
Update initializable and podspec description
2019-05-22 18:42:06 +03:00
Aliona 9f96b934de Fix markup 2019-05-21 15:07:42 +03:00
Aliona 5a053b410e Fix comments to changelog 2019-05-21 15:04:48 +03:00
Aliona 8cfaede34e Make extension public as well 2019-05-21 14:51:07 +03:00
Aliona 0332d9848e Update initializable and podspec description 2019-05-21 14:49:22 +03:00
Aliona d0b2cbac5a
Merge pull request #209 from TouchInstinct/initializable
Initializable
2019-05-20 20:40:21 +03:00
Aliona b2016fa157 Fix Artur's comments 2019-05-20 20:18:16 +03:00
Aliona cdc9668b1a Merge remote-tracking branch 'origin/initializable' into initializable 2019-05-20 20:02:43 +03:00
Aliona 970f8c6987 Initializable protocol 2019-05-20 20:02:35 +03:00
Aliona 419ce202a8 Initializable protocol 2019-05-20 20:01:14 +03:00
ArturAzarau 4ca23cf3d0
Merge pull request #208 from TouchInstinct/fix/tableViewInsetBinding
Table view content offset when keyboard appears fixed
2019-05-20 16:48:37 +03:00
Artur d4fa2d1109 Realization updated 2019-05-20 16:20:45 +03:00
Artur 2c2935c892 Comment about how to use bottomInsetBinder added 2019-05-20 15:46:50 +03:00
Artur 683d3e9797 Podspec and changelog update 2019-05-20 15:30:02 +03:00
Artur d05b1b00c9 TableView Content offset binding was fixed 2019-05-20 15:27:37 +03:00
Ivan Babkin 971e495b6d
Merge pull request #206 from TouchInstinct/feature/hex_string
Added hexString property for UIColor
2019-04-25 19:33:02 +03:00
Ivan Babkin f8026bcd66 Merge branch 'master' into feature/hex_string 2019-04-25 19:30:30 +03:00
ArturAzarau 6449f99e89
Merge pull request #204 from TouchInstinct/feature/CustomizableButton
Feature/customizable button
2019-04-25 19:17:43 +03:00
Artur ef2e351eae Some fix 2019-04-25 19:16:06 +03:00
Ivan Babkin 88a7fbee81 Refactoring 2019-04-25 17:14:42 +03:00
Ivan Babkin ddd008398f Added hexString property for UIColor 2019-04-25 15:18:19 +03:00
Artur Azarau 09e97500fc Build scripts were updated 2019-04-23 16:34:14 +03:00
Artur Azarau cfb8259dbe Version in podspec updated 2019-04-23 14:39:47 +03:00
Artur Azarau b99870183b Dead code deleted 2019-04-23 14:31:33 +03:00
Artur Azarau 469073a5e9 Documentation for classes added 2019-04-23 13:11:20 +03:00
Artur Azarau c4798d6015 Issues resolved 2019-04-23 13:04:51 +03:00
Artur Azarau a3f43a93e8 Issues resolved 2019-04-23 11:30:27 +03:00
Artur d8bb1ce6b0 New line deleted 2019-04-22 20:07:46 +03:00
Artur f1aefb5378 BigBossButtonState was renamed to CustomizableButtonState 2019-04-22 20:04:54 +03:00
Artur b82c547f06 Podspec updated 2019-04-22 19:52:10 +03:00
Artur b594edfc13 Changelog updated 2019-04-22 19:50:50 +03:00
Artur ac291e4ef1 Merge branch 'master' into feature/CustomizableButton 2019-04-22 19:43:53 +03:00
Artur 08b87a0782 Self code review 2019-04-22 19:42:18 +03:00
Artur 223ca6a67e Typo fixed 2019-04-19 18:00:36 +03:00
Artur 544d5bc248 Issues resolved 2019-04-19 17:58:39 +03:00
Pavel Lukandiy 0df9be960f
Merge pull request #203 from TouchInstinct/fix/spinner_fix
Fix/spinner fix
2019-04-19 17:46:43 +03:00
Pavel Lukandiy 39aa498072 Version update 2019-04-19 17:42:15 +03:00
Pavel Lukandiy fb465b99cb Spinner fix 2019-04-19 17:39:26 +03:00
ArturAzarau 9337532f6f
Merge pull request #202 from TouchInstinct/fix/podspec
Pod spec updated
2019-04-17 19:54:02 +03:00
Artur 185d5a838d Pod spec updated 2019-04-17 19:48:24 +03:00
Pavel Lukandiy 8271ffc4b9
Merge pull request #201 from TouchInstinct/fix/podspec
Podspec fix
2019-04-17 17:07:07 +03:00
Pavel Lukandiy 8c61bfab9e Podspec fix 2019-04-17 17:05:31 +03:00
Pavel Lukandiy 6366f70b9a
Merge pull request #200 from TouchInstinct/fix/podspec
Added dependency
2019-04-17 16:40:14 +03:00
Pavel Lukandiy 35a39c149c Added dependency 2019-04-17 16:23:24 +03:00
Pavel Lukandiy 42feb7471f
Merge pull request #199 from TouchInstinct/feature/label_table_cell
Feature/label table cell
2019-04-17 15:34:45 +03:00
Pavel Lukandiy fc3708c793 Removed estimated height 2019-04-17 15:26:17 +03:00
Pavel Lukandiy 8ad7e7162f Podspec update 2019-04-16 20:26:43 +03:00
Artur 05ce8e9d4f Class was renamed 2019-04-16 18:10:26 +03:00
Artur 8d0320fe26 Customizable button was added 2019-04-16 18:08:28 +03:00
Artur 00383039b7 Build scripts were updated 2019-04-16 18:08:10 +03:00
Pavel Lukandiy abca33e3ae Version up 2019-04-15 16:41:44 +03:00
Pavel Lukandiy 12f49c9b51 Added snapkit dependency
Added label table cell
2019-04-15 16:38:32 +03:00
ArturAzarau 8e589a695b
Merge pull request #182 from TouchInstinct/feature/baseSearchViewController
BaseSearchViewController
2019-04-10 15:10:54 +03:00
Artur Azarau 9297de6171 Optional type deleted 2019-04-10 13:33:23 +03:00
Artur Azarau 9cce0b095f Changelog fixed 2019-04-10 13:33:13 +03:00
Artur 2ba53e3366 Build scripts update 2019-04-09 16:24:35 +03:00
Artur 87b23ff8da Addition to previous commit 2019-04-09 16:16:04 +03:00
Artur 3c745e5058 Pod file and podfile.lock were deleted 2019-04-09 16:15:42 +03:00
Artur 23a2cfb113 Merge branch 'baseSearchViewController' into feature/baseSearchViewController 2019-04-09 16:06:58 +03:00
Artur 2d4beef7f0 Changelog and podspec fixed 2019-04-09 16:01:18 +03:00
Artur 0447c53709 Merge branch 'master' into baseSearchViewController 2019-04-09 15:59:06 +03:00
Ivan Smolin c25c92e9da
Merge pull request #197 from TouchInstinct/feature/update_swift_date
update SwiftDate (~> 6)
2019-04-01 11:59:16 +03:00
Ivan Smolin 287955fdba update SwiftDate (~> 6) 2019-04-01 11:55:50 +03:00
Ivan Babkin c50a171030
Merge pull request #196 from TouchInstinct/fix/xcodeproj
Fixed LeadKit.xcodeproj
2019-03-29 20:03:55 +03:00
Ivan Babkin e4e5dcfa30 Fixed LeadKit.xcodeproj 2019-03-29 20:01:48 +03:00
Ivan Babkin 79bca12a00
Merge pull request #193 from TouchInstinct/feature/network_layer
Network features
2019-03-28 16:42:05 +03:00
Ivan Babkin aa00c43f57 Merge branch 'master' into feature/network_layer 2019-03-28 12:19:25 +03:00
Ivan Smolin 0a490b243d
Merge pull request #194 from TouchInstinct/swift_5
Swift 5
2019-03-28 12:14:03 +03:00
Ivan Smolin 3be1d793a2 update changelog and podspec version 2019-03-28 12:02:43 +03:00
Ivan Smolin 223be00d74 migrate to Swift 5; remove test targets 2019-03-28 12:00:16 +03:00
Artur 6f8c4e2b67 More fixes 2019-03-27 14:32:15 +03:00
Artur 053c08443b Fixes 2019-03-27 13:52:38 +03:00
Artur 1dfa0b4f48 Issues fixed 2019-03-27 13:50:33 +03:00
Artur 7d96e07b4e Merge master 2019-03-26 20:22:04 +03:00
Ivan Babkin 23058232ce Fixed changelog 2019-03-26 13:03:05 +03:00
Ivan Babkin b30447e877 Refactoring 2019-03-25 19:56:25 +03:00
Ivan Babkin 5088c20e82 Updated changelog, version up 2019-03-25 15:08:17 +03:00
Ivan Babkin d3d8136477 Refactoring 2019-03-25 14:51:12 +03:00
Ivan Babkin f82158959f Refactoring 2019-03-25 14:32:19 +03:00
Ivan Babkin c713e786f0 Added mapping error handling 2019-03-25 13:20:18 +03:00
Ivan Babkin 9938f1fc49 Refactoring 2019-03-22 16:33:40 +03:00
Ivan Babkin df2f969dc4 Added upload file request 2019-03-20 14:58:01 +03:00
Pavel Lukandiy 29de30cd37
Merge pull request #192 from TouchInstinct/fix/blocks
Fix/blocks
2019-03-19 16:23:00 +05:00
Pavel Lukandiy d9f7da35f1 Added missing function 2019-03-19 16:05:57 +05:00
Pavel Lukandiy 2df2979d8f Naming fix 2019-03-19 15:33:49 +05:00
Pavel Lukandiy 6556d8368b PR fixes 2019-03-19 15:05:41 +05:00
Pavel Lukandiy 517e9619ae Naming fix 2019-03-18 14:49:32 +05:00
Pavel Lukandiy a73baf8253 Version up 2019-03-17 14:17:21 +05:00
Pavel Lukandiy a21847b7ff Added case iterable 2019-03-17 14:09:08 +05:00
Pavel Lukandiy 1b6d77f34c Blocks fix 2019-03-17 13:57:31 +05:00
Pavel Lukandiy 49445a4056
Merge pull request #190 from TouchInstinct/feature/misc_updates
Feature/misc updates
2019-03-14 01:21:23 +05:00
Pavel Lukandiy c529ae736e Podspec version update 2019-03-14 00:38:30 +05:00
Pavel Lukandiy e9eac22a3b Cocoapods removed 2019-03-13 21:33:08 +05:00
Pavel Lukandiy c71626ba92 Changelog update 2019-03-13 21:30:08 +05:00
Pavel Lukandiy 512f854296 Pods deintegrated 2019-03-13 21:28:53 +05:00
Pavel Lukandiy 192a30bfa6 Throwable block fix 2019-03-13 21:28:42 +05:00
Pavel Lukandiy f6b39ddcf7 Plist updates 2019-03-13 14:58:17 +05:00
Pavel Lukandiy f2519ad3fc Changelog update 2019-03-13 14:54:25 +05:00
Pavel Lukandiy cb6d5c4232 New closure typealiases 2019-03-13 14:28:58 +05:00
Pavel Lukandiy caa93bfd1f Scheme updated. Libs updated 2019-03-13 14:11:00 +05:00
Ivan Babkin fb7063e870
Merge pull request #189 from TouchInstinct/fix/carthage
Fixed cartfile
2019-03-06 18:51:55 +03:00
Ivan Babkin 1c97a22a6f Fixed cartfile 2019-03-06 18:49:10 +03:00
Ivan Babkin 09da4ce234
Merge pull request #188 from TouchInstinct/feature/validStatusCodes
Add validStatusCodes pararmeter to request methods
2019-03-06 18:19:11 +03:00
Ivan Babkin 95202a4ff1 Fixed documentation 2019-03-06 16:42:26 +03:00
Ivan Babkin a53dad9777 Add validStatusCodes pararmeter to request methods 2019-03-06 14:39:37 +03:00
Artur 5389eda15d New line at the end of SearchResultsViewController removed 2019-03-06 13:46:02 +03:00
Artur 72fd83785a Typo fixed 2019-03-05 17:49:47 +03:00
Artur 73e2f0743a Issues resolved
- Documentation added
- Changelog fixed
2019-03-05 17:18:13 +03:00
Artur dee27dd0a9 Issue resolved 2019-03-05 15:36:11 +03:00
Artur 5970fd2f69 Issues resolved 2019-03-05 15:18:21 +03:00
Artur 8cb1771e5d Issues resolved 2019-03-05 14:23:58 +03:00
Artur 470247f6d8 Podspec and changelog were updated 2019-03-05 11:33:55 +03:00
Artur f6569fc395 Issues resolved 2019-03-05 11:27:53 +03:00
Artur 8f3b75297a Cartfile updated 2019-03-05 11:00:55 +03:00
Artur 482d8b10e8 Merge branch 'master' into feature/baseSearchViewController 2019-03-05 10:51:44 +03:00
Ivan Babkin fd9d50b8de
Merge pull request #187 from TouchInstinct/refactoring
Refactoring
2019-02-22 13:52:39 +03:00
Ivan Babkin dbc5bf9a17 Refactoring 2019-02-22 13:24:49 +03:00
Ivan Babkin 7cb1ea350b
Merge pull request #186 from TouchInstinct/feature/data_request
Add request method that returns data
2019-02-21 17:46:58 +03:00
Ivan Babkin 3c1ee70ff3 Add request method that returns data 2019-02-21 16:39:15 +03:00
Ivan Smolin d9a4cf03c9
Merge pull request #185 from TouchInstinct/feature/carthage
Feature/carthage
2019-02-11 14:35:32 +03:00
Ivan Smolin 299fbe37e2 fix linting issues 2019-02-11 13:53:48 +03:00
Artur Azarau 6950b1746b issues resolved 2019-02-07 19:37:25 +03:00
Artur Azarau 8e295d701c issues resolved 2019-02-07 19:02:14 +03:00
Artur Azarau 71ef4d6895 task done 2019-02-04 19:50:18 +03:00
Ivan Smolin fb813b259f remove extension target and extension subspec 2019-01-30 13:52:21 +03:00
Ivan Smolin 8c4eda2f66 add binary dependencies 2019-01-29 16:59:04 +03:00
Ivan Smolin 71115f3651 change extension target product name 2019-01-29 16:49:34 +03:00
Ivan Smolin 0c37bea3cf update dependencies, add cartfile 2019-01-29 15:52:33 +03:00
SoriUR c1c0f470d2
Merge pull request #181 from SoriUR/fix/configureSeparators_new_method
configureSeparators method
2019-01-28 15:45:44 +03:00
Iurii 6b04a0fb32 Rename the vars 2019-01-27 07:35:57 +03:00
Iurii 9517e1151e Add a method 2019-01-27 07:27:10 +03:00
ArturAzarau 15ed08d7cf
Merge pull request #178 from TouchInstinct/feature/titleType
TitleType was added
2019-01-10 22:00:34 +03:00
Artur Azarau 727162f58d documentation for large title display mode was fixed 2019-01-10 21:54:31 +03:00
Artur Azarau 4402cd963c version in podspec was fixed 2019-01-10 21:47:07 +03:00
Artur Azarau 2b4259d08e podspec and changelog were updated 2019-01-10 15:16:15 +03:00
Artur Azarau 26efa9a4d8 issues resolved 2019-01-10 14:54:20 +03:00
Artur Azarau e3ddab4f19 issues resolved 2019-01-10 14:16:45 +03:00
Artur Azarau 35131cc336 file renamed 2019-01-10 10:04:58 +03:00
Artur Azarau 707d4bc6f4 Several things were added:
- TitleType enum
- Large title display mode variable
- Function for updating title in view controllers
2019-01-10 10:01:22 +03:00
Ivan Babkin c19f56ecf6
Merge pull request #173 from TouchInstinct/feature/array_request_parameter
Added ability to pass an array parameter in request
2018-12-19 13:39:49 +03:00
Ivan Babkin e1f79f3304 Using guard instead of if 2018-12-19 13:14:28 +03:00
Ivan Babkin 373fee95d0 Changelog fix 2018-12-19 13:06:12 +03:00
Ivan Babkin 598ae1adad Throwing error when using invalid parameters 2018-12-19 12:58:05 +03:00
Ivan Babkin e01861af80 Documentation fix 2018-12-18 17:21:26 +03:00
Ivan Babkin bd47336cfb Refactoring 2018-12-18 16:10:46 +03:00
Ivan Babkin c49fce14a2 Changelog fix 2018-12-17 18:29:23 +03:00
Ivan Babkin a27ec284ac Added ability to pass an array parameter in request 2018-12-17 18:00:45 +03:00
Sergey fed7d6c9d4
Merge pull request #172 from TouchInstinct/tableKitSections
add functions to sections
2018-12-17 15:44:20 +03:00
Sergey Kopytov f5ec3ca438 fix mistakes 2018-12-17 15:39:40 +03:00
Sergey Kopytov 0987c40e25 complete changelog 2018-12-17 15:38:02 +03:00
Sergey Kopytov c76b2e1745 update podspecs 2018-12-17 15:29:30 +03:00
Sergey Kopytov 445600534d add functions to sections 2018-12-17 15:16:30 +03:00
Ivan Babkin 1a5e37f527
Merge pull request #171 from TouchInstinct/feature/request_error_response
Feature/request error response
2018-10-26 18:01:42 +03:00
Ivan Babkin 65c3fba12a Refactoring 2018-10-26 16:59:51 +03:00
Ivan Babkin 7fe405539e Refactoring 2018-10-26 16:48:37 +03:00
Ivan Babkin 50f24e82b6 Podspec version and changelog update 2018-10-26 16:36:55 +03:00
Anton Popkov acf7876f71 Network error handling fix vol. 3 2018-10-26 16:10:21 +03:00
Anton Popkov 615ca1d4b4 Network error handling fix vol. 2 2018-10-26 16:03:34 +03:00
Anton Popkov 652c55a936 Network error handling fix 2018-10-26 15:20:47 +03:00
Ivan Babkin ad8fe894bf Added response to RequestError 2018-10-26 14:07:36 +03:00
Alexey Gerasimov 22ce7654db
Merge pull request #170 from TouchInstinct/feature/travis-ci
Feature/travis ci
2018-10-24 13:04:37 +03:00
Alexey Gerasimov 9bc73b8a3e Pods reverted 2018-10-24 12:49:56 +03:00
Alexey Gerasimov 15043bff09 Travis config fixed 2018-10-24 12:44:51 +03:00
Alexey Gerasimov 2df0dd1a6b Travis config fixed 2018-10-24 12:44:46 +03:00
Alexey Gerasimov 8ff37b175b Submodule link fixed 2018-10-24 12:44:37 +03:00
Alexey Gerasimov 7c6acd5c58 Schemes shared, travis config added 2018-10-24 12:44:29 +03:00
Ivan Babkin 729bbaa8ef
Merge pull request #168 from TouchInstinct/feature/time_out_request_error
Added timeOut error
2018-10-19 17:53:56 +03:00
Ivan Babkin c087a11c3a Changelog update 2018-10-19 17:50:59 +03:00
Ivan Babkin 2bccc006d7 Time out as network error 2018-10-19 17:37:45 +03:00
iON1k 83454dfef4
Merge pull request #167 from TouchInstinct/feature/podspec_version_update
Podspec version update
2018-10-15 15:07:04 +03:00
Anton Popkov 937b43d887 Changelog update 2018-10-15 15:05:45 +03:00
Anton Popkov 4425ff6f21 Podspec version update 2018-10-15 14:56:31 +03:00
Ivan Smolin 7bf65f0b36
Merge pull request #166 from TouchInstinct/feature/general_data_loading_swift4.2
Add: configureLayout method to InitializeableView protocol and all implementations.
2018-10-15 13:13:32 +03:00
Ivan Smolin e7eb1bd51f 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: 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.
2018-10-12 17:22:49 +03:00
Ivan Smolin 03701b4d82
Merge pull request #163 from TouchInstinct/feature/user_defaults_codable
Feature/user defaults codable
2018-09-18 12:46:04 +03:00
Ivan Smolin ecbf71aec0 remove unused typealias 2018-09-14 20:31:24 +03:00
Ivan Smolin b796702b4e simplify 2018-09-14 19:45:01 +03:00
Ivan Smolin 2e70e8248d Add: UserDefaults+Codable is back. Now with generic subscript support. 2018-09-14 19:40:51 +03:00
Ivan Smolin 48dd620542
Merge pull request #162 from TouchInstinct/fix/number_formatting_fix
NumberFormattingService.computedFormatters computed var reverted to static.
2018-09-14 18:13:54 +03:00
Ivan Smolin e0d13abe6c update podspec version 2018-09-14 17:57:56 +03:00
Ivan Smolin 857c747bfa Change: NumberFormattingService.computedFormatters computed var reverted to static. 2018-09-14 17:44:01 +03:00
Ivan Smolin 233db5c048
Merge pull request #161 from TouchInstinct/feature/placeholder_configurable_and_stuff
PlaceholderConfigurable & stuff
2018-09-14 12:29:26 +03:00
Ivan Smolin b27bd9d0e2 code review notes 2018-09-14 12:22:07 +03:00
Ivan Smolin 30bec8e9c7 Add: NSNumberConvertible protocol for NumberFormattingService use cases.
Add: TableDirector methods for rows insertion and removal without reload a whole table.
Add: UIImageView binder for disclosure indicator rotation.
Add: UIView.addSubviews(:) methods with variable number of arguments and array of views.
Add: PlaceholderConfigurable that defines attributes and methods for view with placeholder and regular state.
Add: ContentLoadingViewModel enum that describes possible PlaceholderConfigurable view states.
2018-09-13 20:07:04 +03:00
Ivan Smolin f2d523a9d2
Merge pull request #159 from TouchInstinct/feature/base_placeholder_view
BasePlaceholderView, ViewTextConfigurable, TableKitViewModel, Rx extensions
2018-08-15 17:55:00 +03:00
Ivan Smolin 079a723ef1 forgotten public 2018-08-07 16:32:49 +03:00
Ivan Smolin c1fe170c51 Add: Methods replace(with:), asVoid(), asOptional() to ObservableType, SharedSequence (aka Driver) and Single.
Add: Completable.deferredJust(:) static method.
Add: ViewTextConfigurable protocol. Conform UILabel, UITextField and UIButton to this protocol.
Add: BaseTextAttributes with base text appearance attributes.
Update: ViewText.string now uses BaseTextAttributes instead of separate properties.
Add: BasePlaceholderView and BasePlaceholderViewModel classes used to create your own placeholder.
Add: TableKitViewModel protocol that adds convenient extensions to cell view models that implements it.
2018-08-07 16:26:43 +03:00
Ivan Smolin acd5b39c33
Merge pull request #158 from TouchInstinct/feature/swift_date_migration_part_2
Feature/swift date migration part 2
2018-07-31 19:38:46 +03:00
Ivan Smolin a90586c46a add notes to changelog 2018-07-31 13:33:04 +03:00
Ivan Smolin 1d1ef692b9 fix default arguments 2018-07-31 13:15:10 +03:00
Ivan Smolin d1fab2c69c swift date 5.0 migration part 2 2018-07-31 12:59:55 +03:00
Ivan Smolin e804b92765
Merge pull request #157 from TouchInstinct/feature/base_controllers
Feature/base controllers
2018-07-30 19:13:25 +03:00
Ivan Smolin 0f0b37cd06 fix protocol signature 2018-07-30 18:49:09 +03:00
Ivan Smolin d8a258f05d add comment 2018-07-30 17:48:15 +03:00
Ivan Smolin 0b5b6e8358 code review notes 2018-07-30 15:48:51 +03:00
Ivan Smolin 667c15aa33 code review notes 2018-07-30 14:43:59 +03:00
Ivan Smolin 8b9f3edace final changes 2018-07-27 16:08:37 +03:00
Ivan Smolin e11b129a75 add base table & collection view controllers 2018-07-27 15:46:17 +03:00
Ivan Smolin a20aa5affa update dependencies & migrate to SwiftDate 5.0 2018-07-23 12:53:04 +03:00
Ivan Smolin 2cebfd7ffd base controllers 2018-07-23 11:45:17 +03:00
Ivan Smolin 3a8be1bc8c add compile time debug & reduce type check time 2018-07-23 11:42:37 +03:00
Ivan Smolin 27806bc9e9 fix date migration to region before formatting 2018-07-23 11:42:37 +03:00
Ivan Smolin 922b14dfcb update cocoapods 2018-07-23 11:42:37 +03:00
Aliona 2013fb7d59
Merge pull request #156 from TouchInstinct/server_trust_policies
Update key string
2018-07-17 15:26:19 +03:00
Aliona 7c553ed516 Merge remote-tracking branch 'origin/server_trust_policies' into server_trust_policies 2018-07-12 14:39:24 +03:00
Aliona 662010fc0f Update pull according to comments 2018-07-12 14:39:04 +03:00
Aliona 3901e38f81
Update CHANGELOG.md 2018-07-11 19:43:50 +03:00
Aliona 25c2edda35 Merge remote-tracking branch 'origin/server_trust_policies' into server_trust_policies 2018-07-11 19:42:15 +03:00
Aliona f69f4a12ae Move string extension to a separate file 2018-07-11 19:42:08 +03:00
Aliona cf017b7cda
Update CHANGELOG.md 2018-07-11 19:06:28 +03:00
Aliona f90bf4a77a Map incoming policies to hosts 2018-07-11 18:17:58 +03:00
Aliona 41db20ffac Pass serverTrustPolicies to init 2018-07-11 18:02:23 +03:00
Aliona 3d2a53c906 Update version 2018-07-11 17:29:38 +03:00
Aliona 0d0ae71916 Add assertion failure 2018-07-11 17:23:05 +03:00
Aliona b856a2f65e Update key string 2018-07-11 17:13:43 +03:00
Ivan Smolin d31ec29152
Merge pull request #154 from TouchInstinct/feature/RxNetworkOperationModel_update
small changes in RxNetworkOperationModel
2018-07-03 16:27:48 +03:00
Ivan Smolin 12b4efe6a9 Add: replaceDataSource method to RxNetworkOperationModel.
Add: customErrorHandler constructor parameter to RxNetworkOperationModel and it heirs.
2018-07-03 14:31:25 +03:00
Aliona 14f6a9e6cc
Merge pull request #153 from TouchInstinct/separators
Separators
2018-06-26 15:11:41 +03:00
Aliona f7ab9718c4 Update project file 2018-06-26 14:36:05 +03:00
Aliona 5e311a01a4 Add separatorRowBox to extensions 2018-06-26 14:21:26 +03:00
Aliona e06af6d4ee Update pods 2018-06-26 14:05:15 +03:00
Aliona f9691a6716 Add tablekit extensions 2018-06-26 14:01:44 +03:00
Aliona 0eba1a5865
Update CHANGELOG.md 2018-06-26 13:46:19 +03:00
Aliona 7db711c209 Update podfile 2018-06-26 13:44:40 +03:00
Aliona 65dbcb6ad3 Add files to project 2018-06-26 13:40:51 +03:00
Aliona 037bb831d4
Update CHANGELOG.md 2018-06-26 13:37:16 +03:00
Aliona bd3753fa62
Update LeadKit.podspec 2018-06-25 23:33:33 +03:00
Aliona f85910e5fe
Update CHANGELOG.md 2018-06-25 21:56:40 +03:00
Aliona 249dd03e51
Update LeadKit.podspec 2018-06-25 18:13:04 +03:00
Aliona 10755d2d22 Exclude stuff 2018-06-25 17:52:52 +03:00
Aliona f076b4aac1 Add one more destination 2018-06-25 17:43:35 +03:00
Aliona 387fb83e63 Add cells 2018-06-25 17:37:47 +03:00
iON1k 492d375aea
Merge pull request #151 from TouchInstinct/feature/spinner_view_fix
SpinnerView animation freezing fix
2018-06-07 17:40:38 +03:00
Anton Popkov f20f355a42 SpinnerView animation freezing fix 2018-06-07 16:49:17 +03:00
Andrey Ovsyannikov 37b4c72374
Merge pull request #150 from TouchInstinct/fix/status_codes
Fix/status codes
2018-05-31 18:16:50 +03:00
Madhas 690c0532d1 update changelog and podspec 2018-05-31 17:51:33 +03:00
Madhas 03f61ead48 add acceptable status codes property to network configuration 2018-05-31 17:48:52 +03:00
Igor Kislyuk 2c2386b555
Merge pull request #149 from TouchInstinct/fix/localized-component
Update localized component
2018-05-30 14:20:05 +03:00
Igor Kislyuk 06f4121b69 Update localized component 2018-05-29 22:55:10 +03:00
Andrey Ovsyannikov 87abcde0ef
Merge pull request #139 from TouchInstinct/feature/codable
Feature/codable
2018-05-28 14:56:55 +03:00
Madhas 754875556d encodable extension renamed
init from json added to Decodable
2018-05-25 19:45:23 +03:00
Madhas d408d1eed6 codable extension public 2018-05-25 18:26:02 +03:00
Madhas 3ef5795bfc rename AlamofireRequest file 2018-05-25 16:26:36 +03:00
Madhas ca9b2eaca3 rename AlamofireManager extension file 2018-05-25 16:23:59 +03:00
Madhas bba41c7122 add init to SessionManager 2018-05-25 16:22:43 +03:00
Madhas 996081f65e corrections 2018-05-25 15:55:48 +03:00
Madhas 6db6258db5 changelog corrected 2018-05-25 15:22:17 +03:00
Madhas 0996d94814 Merge branch 'master' into feature/codable 2018-05-25 15:21:04 +03:00
Madhas 85e58127b7 add SesionManager class
some refactor
2018-05-25 15:19:30 +03:00
Madhas e647052af2 correct response processing 2018-05-25 14:47:16 +03:00
Madhas 475e801393 correct ObservableMappable 2018-05-25 14:36:12 +03:00
Ivan Smolin 840f15b47e
Merge pull request #148 from TouchInstinct/fix/pagination_wrapper_retry_button
fix PaginationWrapper retry button showing
2018-05-24 18:09:11 +03:00
Madhas dcc095a233 remove ObjectMapper dependancy from podspec, change version 2018-05-24 17:27:58 +03:00
Madhas 3dc7b4fe9d add Encodable extension to all targets 2018-05-24 17:27:15 +03:00
Ivan Smolin 1d21338de9 fix PaginationWrapper retry button showing 2018-05-24 17:22:19 +03:00
Madhas 69e28eaf5b update changelog 2018-05-24 17:02:38 +03:00
Madhas 20bf8bf861 remove ObjectMapper from podfile
update podspec
2018-05-24 16:56:21 +03:00
Madhas 6cc6971d60 Merge branch 'master' into feature/codable 2018-05-24 13:05:31 +03:00
Madhas 32eefa77a6 rename observable mappable method 2018-05-24 13:02:13 +03:00
Madhas 3edfe50164 code review corrections 2018-05-24 12:57:30 +03:00
Madhas f88c8efb14 correction in network service configuration 2018-05-24 12:57:11 +03:00
Madhas 0ee26e3e8c remove occurrences of ObjectMapper 2018-05-24 12:37:36 +03:00
Madhas 35270f2b32 LeadKitError case added 2018-05-24 12:27:42 +03:00
Ivan Smolin 3931c09d7f
Merge pull request #147 from TouchInstinct/fix/one_more
one more fix
2018-05-22 18:58:46 +03:00
Ivan Smolin 2a98ba3d47 one more fix 2018-05-22 18:56:38 +03:00
Ivan Smolin 4c0579bfef
Merge pull request #146 from TouchInstinct/fix/default_implementation_of_pagination_ui_delegate
fix default implementation of PaginationWrapperUIDelegate
2018-05-22 18:45:43 +03:00
Ivan Smolin 3c99b31da0 fix default implementation of PaginationWrapperUIDelegate 2018-05-22 18:40:31 +03:00
Ivan Smolin 61962b58bd
Merge pull request #142 from TouchInstinct/feature/request_network_operation_state
NetworkOperationState and RequestNetworkOperationModel for tracking network request state
2018-05-22 17:50:11 +03:00
Ivan Smolin 34c0ce78db fix extension build 2018-05-22 17:22:49 +03:00
Ivan Smolin 1e366ba95d fix tvOS build 2018-05-22 17:22:02 +03:00
Ivan Smolin 609e1a9ff4 Merge branch 'master' into feature/request_network_operation_state
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
#	LeadKit.xcodeproj/project.pbxproj
2018-05-22 17:17:13 +03:00
Igor Kislyuk a65a55bd35
Merge pull request #144 from TouchInstinct/feature/newflow
Feature/newflow
2018-05-22 17:08:50 +03:00
Igor Kislyuk 36f059da06 Merge branch 'master' into feature/newflow
# Conflicts:
#	CHANGELOG.md
2018-05-22 16:59:07 +03:00
Igor Kislyuk 0d81d07700
Merge pull request #145 from TouchInstinct/feature/fix-typos
Feature/fix-typos (updated from #134)
2018-05-22 16:49:38 +03:00
Igor Kislyuk 53c9e23b21 Remove empty lines 2018-05-22 16:47:17 +03:00
Igor Kislyuk 9004c5f553 Update empty characters 2018-05-22 16:41:19 +03:00
Igor Kislyuk 87e4789625 Merge branch 'master' into fix-typos
# Conflicts:
#	CHANGELOG.md
#	LeadKit.podspec
2018-05-22 16:37:40 +03:00
Ivan Smolin 116f6b6836 Merge branch 'feature/newflow' of github.com:TouchInstinct/LeadKit into feature/newflow
# Conflicts:
#	CHANGELOG.md
2018-05-22 16:24:15 +03:00
Ivan Smolin cbb647afe7 Merge branch 'feature/newflow' of github.com:TouchInstinct/LeadKit into feature/newflow
# Conflicts:
#	CHANGELOG.md
#	LeadKit.xcodeproj/project.pbxproj
2018-05-22 16:23:10 +03:00
Igor Kislyuk 62240cc591 Fix typo 2018-05-22 16:20:46 +03:00
Ivan Smolin 11d529f4c0 InitializableView 2018-05-22 16:20:04 +03:00
Igor Kislyuk f71c882738 Merge branch 'master' into feature/newflow
# Conflicts:
#	CHANGELOG.md
2018-05-22 16:19:31 +03:00
Pavel 787fc3a935
Merge pull request #143 from TouchInstinct/feature/wrapper_optional_states
Feature/wrapper optional states
2018-05-22 16:09:04 +03:00
Pavel Lukandiy a9536d3cff Targets fix 2018-05-22 14:54:18 +03:00
Igor Kislyuk d7d62a6ee8 Update changelog 2018-05-22 14:46:12 +03:00
Ivan Smolin 3e73ff7658 remove NetworkOperationModel and NetworkOperationState 2018-05-22 14:26:30 +03:00
Igor Kislyuk e3e060eda4 Update for new flow 2018-05-22 14:18:32 +03:00
Pavel Lukandiy 360bcde3f9 Review notes 2018-05-22 14:03:40 +03:00
Pavel Lukandiy c49a05270f UI delegate default parameter 2018-05-21 21:18:10 +03:00
Pavel Lukandiy f71237c032 Version up 2018-05-21 21:16:49 +03:00
Pavel Lukandiy 6982b95659 Refactor and fix annotations 2018-05-21 21:09:56 +03:00
Pavel Lukandiy a6ce1288ba Removed UI delegate generic 2018-05-21 20:44:51 +03:00
Pavel Lukandiy f890fb58e0 Implemented wrapper ui delegate 2018-05-21 20:32:52 +03:00
Ivan Smolin 15a77a8bdb pagination exhausted fix 2018-05-21 20:07:11 +03:00
Ivan Smolin 6417b43df8 fix code review note 2018-05-21 19:16:12 +03:00
Madhas a28805846b toJSON method on encodable 2018-05-20 18:53:31 +03:00
Ivan Smolin 8c3c3cb7f5 hide execute from RxDataLoadingModel and it children 2018-05-18 18:56:26 +03:00
Ivan Smolin d600d57260 NetworkOperationState and RequestNetworkOperationModel for tracking network request state. Just like GeneralDataLoadingState but without empty state. 2018-05-18 18:52:40 +03:00
Madhas 2733a159ed update observable model array request test 2018-05-16 16:01:35 +03:00
Madhas b181344f28 tests updated 2018-05-16 15:52:40 +03:00
Madhas 98ef4ca86e all methods moved to Decodable 2018-05-16 15:52:32 +03:00
Madhas 91b49d8f94 observable mappable protocol updated 2018-05-16 15:52:01 +03:00
Madhas 83b8f40e93 test models updated 2018-05-16 15:51:49 +03:00
Madhas 9d3b3bd223 move normal request to Decodable 2018-05-15 22:10:24 +03:00
Madhas 5a60cf68a5 corrections 2018-05-15 12:34:41 +03:00
Madhas 2046be1282 Merge branch 'master' into feature/codable 2018-05-14 21:32:42 +03:00
Ivan Smolin 4c6990883a
Merge pull request #136 from TouchInstinct/fix/versions_in_podspec
fix version in podspec
2018-05-14 10:26:49 +03:00
Ivan Smolin fd775f4e78 fix version in podspec 2018-05-14 09:31:32 +03:00
Ivan Smolin 2a190e810e
Merge pull request #135 from TouchInstinct/fixes_0.7.13
fixes and migration to BehaviorRelay
2018-05-14 09:20:48 +03:00
Madhas fb34bdb51b test observable mappable model request 2018-05-12 16:04:49 +03:00
Madhas 0209e1b021 add ObservableMappable model 2018-05-12 16:04:30 +03:00
Madhas 17cd260d1e test request 2018-05-12 15:46:42 +03:00
Madhas d53db408fd remove some files from tests target 2018-05-12 15:34:56 +03:00
Madhas bc4f9835a5 update Post model 2018-05-12 14:44:45 +03:00
Sasha Malina 7afa723ed5 Undo useless changes 2018-05-12 00:31:26 +03:00
Ivan Smolin 7249bf66dc - Update: Migrate from Variable to BehaviorRelay.
- Fix: PaginationWrapper retry load more after fail.
- Fix: safeClear method of TableDirector now creates section without header and footer.
- Add: TableSection convenience initializer.
2018-05-11 20:16:28 +03:00
Sasha Malina ad0ed37095 Increment version 2018-05-10 12:39:24 +03:00
Sasha Malina 538138ada5 Add tests for Double extension
Add `roundValue` test case
2018-05-10 12:34:59 +03:00
Sasha Malina f0fb443f6b Fix double rounding issue & typo
In current state "normal" rounding type produce error:
`3.14.roundValue(withPrecision: 0) == 4`,
`3.14.roundValue(withPrecision: 1) == 3.2`, looks strange.
Fix typo in word `precision`, improve description
2018-05-10 12:28:48 +03:00
Sasha Malina aff508ed44 Remove some source files from 'iosTests' target
Update project structure for building ios tests
2018-05-10 12:21:32 +03:00
Alexey Gerasimov bb7fe4f7f2
Merge pull request #133 from TouchInstinct/feature/universalMapping
UniversalMappable added
2018-04-18 12:39:09 +03:00
Alexey Gerasimov d06ede6a19 Float80 extension removed 2018-04-17 21:32:41 +03:00
Alexey Gerasimov 588b097d42 Fixed: pullRequest comments 2018-04-17 19:32:36 +03:00
Alexey Gerasimov aba4068fb5 UniversalMappable added 2018-04-17 19:08:07 +03:00
Ivan Smolin e9521f1e0f
Merge pull request #132 from TouchInstinct/fix/table_view_content_overlapping
Fix: addHeaderBackground cells overlapping.
2018-04-16 17:47:59 +03:00
Ivan Smolin 9473e46d1b Fix: addHeaderBackground cells overlapping. 2018-04-13 14:00:38 +03:00
Igor Kislyuk d87890c83e
Merge pull request #131 from TouchInstinct/fix/version
Fix version
2018-04-11 21:08:13 +03:00
Igor Kislyuk 7a329feeca Fix version 2018-04-11 21:03:43 +03:00
Igor Kislyuk 828eb56560
Merge pull request #130 from TouchInstinct/fix/typo
Fix typo
2018-04-11 20:53:36 +03:00
Igor Kislyuk 03b4dc6ac5 Fix typo 2018-04-11 20:51:43 +03:00
Igor Kislyuk c168b64d2c Fix typo 2018-04-11 20:49:53 +03:00
Andrey Ovsyannikov 9275ef6b6b
Merge pull request #129 from TouchInstinct/fix/timeout
fix timeout
2018-04-11 19:22:06 +03:00
Madhas 8f9588d791 change log correction 2018-04-11 19:20:02 +03:00
Madhas 83e1eae7d0 fix timeout 2018-04-11 19:13:57 +03:00
Ivan Smolin f408af05e1
Merge pull request #127 from TouchInstinct/feature/small_changes
Small changes
2018-04-11 17:08:04 +03:00
Alexey Gerasimov 5f513b5bad
Merge pull request #128 from TouchInstinct/fix/view+autolayout
Fix/view+autolayout
2018-04-11 17:00:26 +03:00
Madhas 3fdbc72324 Merge branch 'feature/small_changes' into fix/view+autolayout 2018-04-11 16:56:53 +03:00
Madhas fc591bba0c change log + comments 2018-04-11 16:51:32 +03:00
Madhas 0c27f3b81d pleasuring swiftlint 2018-04-11 16:45:25 +03:00
Madhas f1da683009 Merge branch 'master' into fix/view+autolayout 2018-04-11 16:40:01 +03:00
Madhas d15a10857f setToCenter with insets updated 2018-04-11 16:39:03 +03:00
Ivan Smolin a9be9b8f13 Merge branch 'master' into feature/small_changes 2018-04-11 16:35:25 +03:00
Alexey Gerasimov f8e9fd8973
Merge pull request #126 from ef1rspb/separator_rowbox_ext
Fix doubling separator line issue
2018-04-11 16:32:35 +03:00
Ivan Smolin b320615154 remove copy-paste 2018-04-11 16:24:17 +03:00
sasha malina 74b61cc921 Update changelog & pod version 2018-04-11 16:17:41 +03:00
Ivan Smolin e51a585ed9 apiRequestParameters method to NetworkServiceConfiguration extension 2018-04-11 15:24:22 +03:00
Ivan Smolin 46d0fd57da code review comment 2018-04-11 13:40:10 +03:00
Ivan Smolin b364ac6d61 Remove: App, Log and LogFormatter.
Remove: CocoaLumberjack dependency.
Add: Rotate operation for image drawing.
Add: mapViewEvents overload with closure that returns array of disposables.
Update: Update ObjectMapper to 3.1.
2018-04-11 11:41:23 +03:00
sasha malina d267f0a58e Fix doubling separator line issue
The problem is that for 2..last-1 rows performs .full(middleSeparatorConfiguration, middleSeparatorConfiguration) action, that leads to doubling separator line height
2018-04-10 20:00:27 +03:00
Alexey Gerasimov 14e793cf09
Merge pull request #125 from TouchInstinct/feature/networkConfiguration
NetworkServiceConfiguration added
2018-04-06 09:46:05 +03:00
Alexey Gerasimov 66567bb811 Fixed: pullRequest comments 2018-04-06 01:11:07 +03:00
Alexey Gerasimov ed13f119f3 NetworkServiceConfiguration added 2018-04-05 22:02:44 +03:00
Ivan Smolin 85f568fd44
Merge pull request #124 from TouchInstinct/feature/separator_cell_extensions
Separator cell related stuff
2018-04-05 11:40:28 +03:00
Ivan Smolin c450d2c143 empty group 2018-04-03 17:54:26 +03:00
Ivan Smolin 79c2cf70b9 update podspec version 2018-04-02 20:28:21 +03:00
Ivan Smolin 437ab6ff6b topConfiguration and bottomConfiguration properties, methods to configure top and bottom separators in CellSeparatorType extension.
totalHeight property in SeparatorConfiguration extension.
2018-04-02 20:21:14 +03:00
Ivan Smolin ea829b7418
Merge pull request #123 from TouchInstinct/feature/fix_podspec
Exclude UIApplication extensions from iOS-Extension subspec
2018-04-02 15:44:29 +03:00
Ivan Smolin b6ddf8c285 Exclude UIApplication extensions from iOS-Extension subspec 2018-04-02 15:26:36 +03:00
Ivan Smolin 596ef59d1a
Merge pull request #120 from TouchInstinct/feature/date_formatting_view_background_and_text
DateFormattingService class replaced with protocol.
2018-04-02 13:44:27 +03:00
Alexey Gerasimov 35d81b0e2e Podspec version fix 2018-03-30 19:44:19 +03:00
Alexey Gerasimov e5d50f11d5 Merge branch 'master' into feature/date_formatting_view_background_and_text
# Conflicts:
#	CHANGELOG.md
2018-03-30 19:43:40 +03:00
Alexey Gerasimov 0706e36860
Merge pull request #122 from TouchInstinct/fix/changeRoot
Change root controller fixed
2018-03-30 19:33:23 +03:00
Alexey Gerasimov 63324eec7e Ooops 2018-03-30 19:32:23 +03:00
Alexey Gerasimov 828d63838c Refactored 2018-03-30 19:27:30 +03:00
Alexey Gerasimov 11cce2d920 Change root controller fixed 2018-03-30 19:22:19 +03:00
Ivan Smolin e6bdba0504 Xcode 9.3 migration 2018-03-30 12:19:27 +03:00
Alexey Gerasimov ee15a4f1a6
Merge pull request #121 from TouchInstinct/feature/pinView11
Safe area support added
2018-03-29 20:02:07 +03:00
Alexey Gerasimov 9a4d934cc2 Refactored 2018-03-29 20:00:13 +03:00
Alexey Gerasimov e296f5ee77 Comment added 2018-03-29 19:52:30 +03:00
Alexey Gerasimov 934121690d Safe area support added 2018-03-29 19:22:46 +03:00
Ivan Smolin 8f16df4e91 fix watchOS build 2018-03-29 17:42:54 +03:00
Ivan Smolin 03301eec3d DateFormattingService class replaced with protocol.
Add SwiftDate dependency for DateFormattingService.
Add ViewBackground enum that describes possible view backgrounds.
Add ViewText enum that describes text with appearance options.
Removed String+SizeCalculation extension.
2018-03-29 17:17:24 +03:00
Ivan Smolin b0cc7c5dfd
Merge pull request #119 from TouchInstinct/feature/equatable_additional_http_headers
Equatable optional arrays, additionalHttpHeaders and convenient initializer for NetworkService
2018-03-28 20:52:41 +03:00
Ivan Smolin 74d3e69b37 update podspec version 2018-03-28 19:33:32 +03:00
Ivan Smolin ba596d826b rename and public 2018-03-28 19:26:44 +03:00
Ivan Smolin 5c88f34333 Extension for comparing optional arrays (`[T]?`) with `Equatable` elements.
`additionalHttpHeaders` static field in `ConfigurableNetworkService` protocol.
Default initializer for Network service that conforms to `ConfigurableNetworkService` protocol.
2018-03-28 19:03:35 +03:00
Ivan Smolin 1a34bebe69
Merge pull request #118 from TouchInstinct/feature/text_field_view_model
text field view model with bindings
2018-03-27 19:35:36 +03:00
Ivan Smolin 3fe60ef94a add safeClear for TableDirector 2018-03-27 16:42:45 +03:00
Ivan Smolin 78f82e51d2 Update version and podspec 2018-03-27 14:41:14 +03:00
Ivan Smolin 7c548954fb neatify 2018-03-27 14:36:29 +03:00
Ivan Smolin 3d3736bf8f NSAttributedString extensions 2018-03-27 14:33:55 +03:00
Ivan Smolin 4d938fc384 fix iOS-extension build 2018-03-27 14:32:59 +03:00
Ivan Smolin 5d206ea718 convenient typealias PaginationWrapperType 2018-03-27 13:17:16 +03:00
Ivan Smolin 4507f8cd5d SingleLoadCursor replacement and deprecation 2018-03-27 11:59:23 +03:00
Ivan Smolin 384180443b fix network service background thread crash 2018-03-27 11:43:15 +03:00
Ivan Smolin c7e5687f0b UIApplication+openUrl and telprompt 2018-03-27 11:42:55 +03:00
Ivan Smolin 01e93cc4e3 Move VoidBlock from LeadKitAdditions. closes https://github.com/TouchInstinct/LeadKit/issues/104 2018-03-27 11:12:50 +03:00
Ivan Smolin 463279d286 text field view model with bindings 2018-03-26 23:37:10 +03:00
Ivan Smolin f875207362
Merge pull request #117 from TouchInstinct/feature/additions_merge
Feature/additions merge
2018-03-26 18:07:08 +03:00
Ivan Smolin 9dec8a651e code review note 2018-03-26 18:05:11 +03:00
Ivan Smolin 409b614e98 remove empty group 2018-03-26 16:13:35 +03:00
Ivan Smolin 26a5b9dd96 fix watch os build 2018-03-26 16:13:35 +03:00
Ivan Smolin c3337fdf22 forgot to commit 2018-03-26 16:13:35 +03:00
Ivan Smolin e3e424bdf7 naming 2018-03-26 16:13:35 +03:00
Ivan Smolin 7a905bb9b6 fix array safe subscript 2018-03-26 16:13:35 +03:00
Ivan Smolin 751e0a51d8 number formatting service 2018-03-26 16:13:35 +03:00
Ivan Smolin 24695b1dae replace DefaultNetworkService with protocol and default implementation 2018-03-26 16:13:35 +03:00
Ivan Smolin a1a8ed5366
Merge pull request #116 from TouchInstinct/feature/base_loading_view_model_and_controller
Feature/base loading view model and controller
2018-03-23 14:33:25 +03:00
Ivan Smolin cb694dcbcf code review notes 2018-03-23 11:29:24 +03:00
Ivan Smolin bfaf668191 fixes 2018-03-22 18:27:47 +03:00
Ivan Smolin 368b90ddc8 remove unused symlinks 2018-03-22 17:52:27 +03:00
Ivan Smolin 6b74825fd4 add GeneralDataLoadingViewModel and GeneralDataLoadingController protocol 2018-03-22 17:50:29 +03:00
Ivan Smolin cd00fb2f2f
Merge pull request #115 from TouchInstinct/feature/data_loading_and_pagination_wrapper
Feature/data loading and pagination wrapper
2018-03-22 15:09:29 +03:00
Ivan Smolin b0cee2dee6 code review notes 2018-03-22 14:55:43 +03:00
Ivan Smolin 7c012db927 code review notes 2018-03-22 13:17:15 +03:00
Ivan Smolin 8ab4000b95 make some types 2018-03-22 13:15:23 +03:00
Ivan Smolin a503259eb1 fix runtime crash 2018-03-21 19:22:51 +03:00
Ivan Smolin 96a59c809e add conformance to RxDataSource 2018-03-21 18:55:53 +03:00
Ivan Smolin 323300be63 fix .retry 2018-03-21 18:28:21 +03:00
Ivan Smolin 435fbace1d remove unused code 2018-03-21 17:46:10 +03:00
Ivan Smolin 0b472a1cdc code review notes 2018-03-21 16:51:34 +03:00
Ivan Smolin d20f795db7 code style 2018-03-21 16:51:25 +03:00
Ivan Smolin 03619df2f1 add data loading classes; update PaginationWrapper (collection view support) 2018-03-21 16:24:43 +03:00
Ivan Smolin 8839d029a9
Merge pull request #114 from TouchInstinct/feature/total_count_cursor
add TotalCountCursor and related stuff
2018-03-21 16:23:05 +03:00
Ivan Smolin 1477043499 add TotalCountCursor and related stuff 2018-03-20 19:25:03 +03:00
Ivan Smolin 3118fc84c8
Merge pull request #113 from TouchInstinct/libs_and_linters_update
Libs and linters update
2018-03-20 19:07:00 +03:00
Ivan Smolin c99881288a fix liner warnings 2018-03-20 18:24:52 +03:00
Ivan Smolin 3799d11e0a update linters, libs and scripts 2018-03-20 17:49:09 +03:00
1042 changed files with 69656 additions and 3794 deletions

2
.bundle/config Normal file
View File

@ -0,0 +1,2 @@
---
BUNDLE_PATH: ".gem"

96
.githooks/prepare-commit-msg Executable file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env python3
import sys, re, os
from subprocess import check_output
from sys import getdefaultencoding
getdefaultencoding() # utf-8
valid_commit_style = '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style)(\(\S+\))?\!?: .+'
merge_commit_style = '^(m|M)erge .+'
success_title = 'SUCCESS'
success_color = '92m'
error_title = 'ERROR'
error_message = 'Incorrect commit message style!\nThe commit pattern:'
error_commit_pattern = ' type(scope): message | type: message \n'
error_color = '91m'
breaking_changes_message = 'If commit include Breaking changes use ! after type or scope:'
colored_breaking_changes_message = 'If commit include \033[91mBreaking changes\033[00m use \033[91m!\033[00m after type or scope:'
breaking_changes_commit_pattern = ' type(scope)!: message | type!: message \n'
available_types_message = 'Available commit types:'
available_commit_types = ['build: Changes that affect the build system or external dependencies',
'ci: Changes to our CI configuration files and scripts',
'docs: Documentation only changes',
'feat: A new feature. Correlates with MINOR in SemVer',
'fix: A bug fix. Correlates with PATCH in SemVer',
'perf: A code change that improves performance',
'refactor: A code change that neither fixes',
'revert: A revert to previous commit',
'style: Changes that do not affect the meaning of the code (white-space, formatting, etc)']
is_GUI_client = False
def print_result_header(result_title, color):
if not is_GUI_client:
print("[\033[96mcommit lint\033[00m] [\033[{}{}\033[00m]\n".format(color, result_title))
def print_pattern(pattern):
if is_GUI_client:
print(pattern)
else:
print("\033[96m{}\033[00m".format(pattern))
def print_error_message():
print_result_header(error_title, error_color)
print(error_message)
print_pattern(error_commit_pattern)
if is_GUI_client:
print(breaking_changes_message)
else:
print(colored_breaking_changes_message)
print_pattern(breaking_changes_commit_pattern)
print_available_commit_types()
def print_available_commit_types():
print(available_types_message)
for commit_type in available_commit_types:
print(" - %s" %commit_type)
def write_commit_message(fh, commit_msg):
fh.seek(0, 0)
fh.write(commit_msg)
def lint_commit_message(fh, commit_msg):
is_merge_commit = re.findall(merge_commit_style, commit_msg)
is_valid_commit = re.findall(valid_commit_style, commit_msg)
if is_valid_commit or is_merge_commit:
print_result_header(success_title, success_color)
write_commit_message(fh, commit_msg)
sys.exit(os.EX_OK)
else:
print_error_message()
sys.exit(os.EX_DATAERR)
def run_script():
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'r+') as fh:
commit_msg = fh.read()
lint_commit_message(fh, commit_msg)
try:
sys.stdin = open("/dev/tty", "r")
is_GUI_client = False
except:
is_GUI_client = True
run_script()

81
.gitignore vendored
View File

@ -1,16 +1,18 @@
# ================
# Swift.gitignore
# ================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData
## User settings
xcuserdata/
## Various settings
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
@ -19,17 +21,14 @@ DerivedData
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
@ -39,6 +38,14 @@ playground.xcworkspace
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
.swiftpm
.build/
# CocoaPods
@ -48,30 +55,56 @@ playground.xcworkspace
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Carthage/Checkouts
Carthage/Build
Carthage/Build/
# Accio dependency management
Dependencies/
.accio/
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/screenshots
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
# AppCode
# https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems
iOSInjectionProject/
.idea/workspace.xml
.idea/tasks.xml
# homebrew-bundle
Brewfile.lock.json
# Node.js
# Dependency directories
node_modules/
# Touch Instinct custom
Downloads/
fastlane/README.md
Templates/
cpd-output.xml
*.swp
*IDEWorkspaceChecks.plist
# Gem
.gem/
.DS_Store

6
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "code-quality"]
path = code-quality
url = https://github.com/TouchInstinct/code-quality-ios
[submodule "build-scripts"]
path = build-scripts
url = https://git.svc.touchin.ru/TouchInstinct/BuildScripts.git

View File

@ -1 +0,0 @@
4.0

View File

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

View File

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

View File

@ -1,5 +1,850 @@
# Changelog
### 1.56.0
- **Update**: `ViewSkeletonsConfiguration`. It's possible to enable or disable animation for specific skeletons now.
- **Added**: `HolderViewSkeletonsConfiguration` for skeleton root view configuration
- **Added**: `DashedBoundsLayer` can now be applied to `CALayer`
### 1.55.1
- **Update**: revert `TextSkeletonsConfiguration` line height calculation
### 1.55.0
- **Update**: use TouchInstinct `TableKit` fork instead of original one
- **Update**: remove default value from `BoolValueDefaultsStorage`
### 1.54.6
- **Added**: `xcprivacy` files
- **Update**: Correctly detect app reinstall in `AppInstallLifetimeSingleValueStorage`
- **Update**: use `xHeight` instead of `pointSize` for default skeleton line height calculation
- **Update**: update `linkTextAttributes` in `UITextView` when setting interactive url parts
### 1.54.5
- **Update**: Сhange `StatefulButton` event propogation avoidance method.
### 1.54.4
- **Update**: Fix `StatefulButton` state configuration for iOS 15+.
### 1.54.3
- **Update**: Set reasonable defaults for `SkeletonConfiguration`.
### 1.54.2
- **Update**: Changed access level from internal to public of title and subtitle view in `BaseTitleSubtitleView`.
### 1.54.1
- **Added**: `BaseTitleSubtitleView` which can be inherited for fine-tuning skeletons and other behavior.
- **Update**: Changed lines number calculation method in `TextSkeletonsConfiguration`.
### 1.54.0
- **Added**: `maxWidth` parameter to `BaseViewSkeletonsConfiguration`.
- **Added**: custom `SkeletonConfigurations` for nested `SkeletonPresenters`.
- **Update**: Many fixes and improvenments to `TextSkeletonsConfiguration`.
### 1.53.3
- **Update**: `Skeletonable` can now control custom geometry change notification.
- **Update**: Filter hidden views from skeletonable views by default.
### 1.53.2
- **Update**: `DefaultTitleSubtitleView` support for separated configuration of title and subtitle labels layout.
- **Update**: `BaseListItemView` fixed trailing insets when trailing view is hidden.
### 1.53.1
- **Update**: Insets layout heuristics for `WrappedViewHodler` implementations
### 1.53.0
- **Added**: Custom string attributes to `BaseTextAttributes`
- **Added**: Customizeable `UIViewBackground` and `UIViewBorder` for `UIView.Appearance`
- **Added**: Keychain single value storage for codable models -`CodableSingleValueKeychainStorage`
- **Update**: Renamed methods `startAnimation` and `stopAnimation` of `SkeletonPresenter`, so it won't conflict with `Animatable` protocol anymore
### 1.52.0
- **Added**: `TIApplication` module with core dependencies of main application and its extension targets
- **Added**: `DefaultHomogeneousItemsCollectionView` default collection view implementation with configurable identical-type cells
- **Update**: Changed implementation of `AppInstallLifetimeSingleValueStorage`. Now it uses `SingleValueStorage<Bool>` to be able to migrate stored UserDefaults values
- **Added**: `UserLocationFetcher.OnLocationFetchFailureCallback` and `ItemDistanceTo` in `TIMapUtils`
- **Added**: Tap handler closure to `DefaultConfigurableStatefulButton.ViewModel`
- **Added**: Universal DSL
### 1.51.0
- **Added**: `BaseModalViewController` implementing `PanModalPresentable` with additional functionality
- **Added**: `BaseModalWrapperViewController` for wrapping `UIViewController`s with `BaseModalViewController` functionality
### 1.50.0
- **Updated**: Fix activity indicator positioning for `StatefulButton` on iOS 15+ and disabled state touch handling
- **Added**: iOS 15+ activity indicator placement support in `StatefulButton`
- **Added**: `TICoreGraphicsUtils` module for drawing operations and other CoreGraphics related functionality
- **Update**: `MarkerIconFactory` can now return optional `UIImage`. In this case MapManagers will show the default marker icon.
### 1.49.0
- **Added**: `BaseMigratingSingleValueKeychainStorage` and `BaseMigratingSingleValueDefaultsStorage` implementations for migrating keys from one storage to another.
### 1.48.0
- **Added**: `BaseStackView` with configurable items appearance
- **Fixed**: `CollectionTableViewCell` self-sizing
- **Added**: `ViewAppearance.WrappedViewLayout` support for all `WrappedViewHolders`
- **Added**: `ViewCallbacks` support for all `BaseInitializeableViews`
### 1.47.0
- **Added**: `flatMap` operator for `AsyncOperation`
- **Update**: `CodableKeyValueStorage` now returns `Swift.Result` with typed errors.
- **Added**: `SingleValueExpirationStorage` for time aware entries (expirable api tokens, etc.)
- **Added**: `AsyncOperation` variants of process methods in NetworkServices.
### 1.46.0
- **Added**: `AsyncSingleValueStorage` and `SingleValueStorageAsyncWrapper<SingleValueStorage>` for async access to SingleValue storages wtih swift concurrency support
- **Added**: `BaseMapUISettings` used to configure map view of different providers + user location icon rendering for yandex maps
- **Added**: `UserLocationFetcher` helper that requests authorization and subscribes to user location updates
- **Update**: add `DEVELOPMENT_INSTALL` support for all podspecs and fix playground compilation issues
### 1.45.0
- **Added**: `SingleValueStorage` implementations + `AppInstallLifetimeSingleValueStorage` for automatically removing keychain items on app reinstall.
- **Added**: `TILogging` with error logging types
- **Update**: `DefaultRecoverableJsonNetworkService` supports iOS 12.
- **Update**: `DefaultFingerprintsProvider` now uses `SingleValueStorage`
### 1.44.0
- **Added**: HTTP status codes to `EndpointErrorResult.apiError` responses
- **Added**: SwiftLint pre-build SPM step to TINetworking module
### 1.43.1
- **Fixed**: build scripts submodule url
### 1.43.0
- **Added**: `TITextProcessing` for regex and text formatting added
### 1.42.1
- **Fixed**: Podspecs source and homepage urls
### 1.42.0
- **Added**: TIDeeplink to support deeplink API
### 1.41.0
- **Update**: added callbacks for views while skeletons change status to presented or hidden
### 1.40.0
- **Added**: `PlaceholderFactory` for creating `DefaultPlaceholderView` views
- **Added**: `DefaultPlaceholderImageView`
### 1.39.0
- **Added**: UIButton Appearance model
- **Added**: `SpacedWrappedViewLayout` for spacing configurations
- **Update**: UIView appearance model with border configurations
### 1.38.0
- **Added**: Placemarks states for icon updating
- **Added**: Selecting / deselecting markers through cluster manager
### 1.37.0
- **Added**: API for converting view hierarchy to skeletons
### 1.36.1
- **Update**: `YandexMapsMobile` version updated
- **Fix**: Map manager memory leak removed
### 1.36.0
- **Removed**: `TILogger`module
- **Updated**: moved `LoggingPresenter` to `TIDeveloperUtils` module.
### 1.35.1
- **Added**: Auto documentation generation for `TIFoundationUtils` playground and compile checks for playground before release
- **Updated**: `AsyncOperation` fixed ordering of chain operations execution
### 1.35.0
- **Added**: `TIDeveloperUtils` framework, that contains different utils for development
- **Added**: `UIView` and `UIViewController` extensions for showing SwiftUI previews
- **Added**: `DashedBoundsLayer` for debugging views' frames visually
### 1.34.0
- **Added**: `BaseListItemView` for displaying three views horizontally
- **Added**: `DefaultTitleSubtitleView` for displaying one or two labels vertically
- **Update**: `StatefulButton` now can be configured with `ViewAppearance` model for each state
### 1.33.0
- **Added**: `ViewAppearance` and `ViewLayout` models for setting up Views' appearance and layout
- **Added**: `TableKit.Row` extension for configuration inner View's appearance and layout
- **Added**: `WrappableView` with typealiases for creating wrapped in the container views
- **Added**: `CollectionTableViewCell` and `ContainerView`
- **Update**: Separator appearance configureation for table views
### 1.32.0
- **Added**: `BaseInitializableWebView` with navigation and error handling api.
### 1.31.0
- **Added**: `URLInteractiveTextView` for terms and conditions hints in login flow
### 1.30.0
- **Added**: Base classes for encryption and decryption user token with pin code or biometry
- **Added**: Pin code validators
### 1.29.1
- **Updated**: `BaseTextAttributes` correct detection of the necessity of using attributed string
### 1.29.0
- **Added**: `BaseTextAttributes`can now measure text size and provides paragraph style configuration API.
- **Removed**: `ViewText`. Was fully replaced with `BaseTextAttributes`
- **Fixed**: `Operation.flattenDependencies` used in `Operation.add(to:waitUntilFinished:)` now works correctly.
- **Added**: Now it's possible to add dependent operation to start of the queue.
### 1.28.0
- **Add**: `LoggingPresenter`to present list of logs with ability of sharing it
- **Add**: `TILogger` wrapper object to log events.
### 1.27.1
- **Fix**: Weak target reference in `RefreshControl`
### 1.27.0
- **Add**: Tag like filter collection view
- **Add**: List like filter table view
- **Add**: Range like filter view
### 1.26.3
- **Update**: Add @escaping in `RequestExecutor.ExecutionClosure`
### 1.26.2
- **Update**: Add failureCompletion in `RequestExecutor`
### 1.26.1
- **Fix**: Use OperationQueue instead of NSLock in `DefaultTokenInterceptor`
- **Update**: AsyncOperation refactoring
### 1.26.0
- **Add**: `TIEcommerce` module with Cart, products, promocodes, bonuses and other related actions.
### 1.25.0
- **Update**: `RequestError` cases now contain additional url assotiated value
- **Update**: Network requests error catching now throws `RequestError` with url
### 1.24.0
- **Add**: `AlertFactory` for presenting alerts in SwiftUI and UIKit.
### 1.23.0
- **Update**: `UITextView` now support configuration with `BaseTextAttributes`
- **Add**: `ReconfigurableView` & `ChangeableViewModel` for non-destructing state update
- **Add**: `WrappedViewHolder` protocol with table/collection view cell implementations
- **Add**: `UIViewPresenter` and `ReusableUIViewPresenter` protocols with default implementation for proper handling view/cells reuse
### 1.22.0
- **Update**: Asynchronous request preprocessing
### 1.21.0
- **Update**: `AsyncEventHandler` was replaced with `EndpointRequestRetrier`
- **Add**: `FingerprintsTrustEvaluator` and `FingerprintsProvider` for fingerprint-based host trust evaluation
- **Add**: `DefaultTokenInterceptor` for queue-based token refresh across all requests of single api interactor (network service).
- **Update**: `DefaultRecoverableJsonNetworkService` now returns collection of errors in result
- **Update**: `CancellableTask` was renamed to `Cancellable`. Cancellable implementations has been moved from `TIMoyaNetworking` to `TIFoundationUtils`.
- **Add**: `ApiInteractor` protocol with basic request/response methods
### 1.20.0
- **Add**: OpenAPI security schemes support for EndpointRequest's.
- **Update**: Replace `AdditionalHeadersPlugin` with `SecuritySchemePreprocessor` and `EndpointRequestPreprocessor` (with default implementations)
### 1.19.0
- **Add**: Add presenter protocols to `TISwiftUICore` and `TIUIKitCore` modules
- **Add**: `CodeConfirmPresenter` protocol and `DefaultCodeConfirmPresenter` implementation in `TIAuth` module
### 1.18.0
- **Add**: add MapManagers for routine maps configuration
### 1.17.0
- **Add**: add smooth CameraUpdate actions for supported maps
### 1.16.2
- **Update**: `DefaultRecoverableJsonNetworkService` now supports error forwarding from its error handlers to initial requests.
### 1.16.1
- **Update**: `DateFormattersReusePool` and `ISO8601DateFormattersReusePool` are now thread safe.
### 1.16.0
- **Add**: `TIMapUtils`, `TIAppleMapUtils`, `TIGoogleMapUtils` and `TIYandexMapUtils` modules for map items clustering and interacting with them.
### 1.15.0
- **Update**: Network services in TIMoyaNetworking now passes MoyaError in result of EnpointRequest execution.
- **Add**: `TINetworkingCache` module - caching results of EndpointRequests.
- **Important Note**: `TINetworkingCache` added via SPM may require you to add `DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC=YES` flag to build settings of project target (see [probably related problem](https://forums.swift.org/t/adding-a-package-to-two-targets-in-one-projects-results-in-an-error/35007/18))
### 1.14.3
- **Fix**: Creating headerView and footerView when initializing a section with rows in `TITableKitUtils`.
- **Add**: Empty table section initialization method in `TITableKitUtils`.
### 1.14.2
- **Update**: DateFormatters properties preset in reuse pools
### 1.14.1
- **Fix**: Array encoding for `QueryStringParameterEncoding`
### 1.14.0
- **Add**: [Date] coding methods
### 1.13.0
- **Update**: Change access modifiers in `DefaultJsonNetworkService` from `public` to `open`, added additional Moya plugins processing
- **Add**: `DisplayDecodingErrorPlugin` for showing developer-frendly decoding error messages
- **Add**: Gemfile for cocoapods versioning
### 1.12.3
- **Fix**: Try parse date in ISO8601 format appending `.withFractionalSeconds` if `.withInternetDateTime` fails
### 1.12.2
- **Fix**: HeaderParameterEncoding encodes array correctly
### 1.12.1
- **Update**: DefaultRecoverableNetworkService `request` parameter was renamed to prevent ambgious reference
### 1.12.0
- **Update**: EndpointRequest Body can take a nil value
- **Update**: Parameter value can be nil as well
- **Update**: observe operator of AsyncOperation now accepts callback queue parameter
### 1.11.1
- **Fix**: `timeoutIntervalForRequest` parameter for `URLSessionConfiguration` in `NetworkServiceConfiguration` added.
### 1.11.0
- **Breaking changes**: many method signatures was changes in `TIMoyaNetworking`.
- **Add**: `ISO8601DateFormattersReusePool` and codable helpers for ISO8601 date (de)coding.
- **Add**: Moya plugin protocol for adding HTTP headers with default implementation.
### 1.10.0
- **Add**: `DefaultRecoverableJsonNetworkService` with error handling chain.
### 1.9.0
- **Add**: `TIMoyaNetworking` target - Moya + Swagger network service.
- **Update**: `TISwiftUtils` - added async closure typealiases.
- **Update**: `TIFoundationUtils` - added date formatting & decoding helpers.
### 1.8.0
- **Add**: `TIFoundationUtils.AsyncOperation` - generic subclass of Operation with chaining and result observation support
### 1.7.0
- **Add**: `TINetworking` - Swagger-frendly networking layer helpers
### 1.6.0
- **Add**: the pretty timer - TITimer.
### 1.5.0
- **Add**: `HeaderTransitionDelegate` - Helper for transition of TableView header and navigationBar title view
### 1.4.0
- **Update**: update minor dependencies.
- **Fix**: project's scripts.
### 1.3.0
- **Add**: `TIPaginator` - realisation of paginating items from a data source.
### 1.2.0
- **Add**: `TIKeychainUtils` - Set of helpers for Keychain classes.
### 1.1.1
- **Fix**: `StatefullButton` propagation
### 1.1.0
- **Add**: `BaseInitializeableViewController`, `BaseCustomViewController` and `BaseViewController` to TIUIKitCore.
- **Add**: `TableKitTableView` and `TableDirectorHolder` to TITableKitUtils.
### 1.0.0
- **API BreakingChanges**: up swift version to 5.1.
- **Update**: build scripts.
- **Update**: code with new swiftlint rules.
- **Update**: RxSwift to 6.0.0.
### 0.13.1
- **Fix**: LeadKit.podspec file.
### 0.13.0
- **Add**: Githook `prepare-commit-msg` to check commit's style.
- **Add**: Setup script.
### 0.12.0
- **Add**: StatefulButton & RoundedStatefulButton to TIUIElements.
- **Add**: added CACornerMask rounding extension to TIUIElements.
- **Add**: UIControl.State dictionary extensions to TIUIKitCore.
- **Add**: UIFont and CTFont extensions to TIUIKitCore.
- **Breaking change**: reworked BaseTextAttributes & ViewText. Removed ViewTextConfigurable protocol & conformances.
### 0.11.0
- **Add**: Cocoapods support for TI-family libraries.
- **Add**: `SeparatorConfigurable` and all helper types for separator configuration.
- **Add**: `BaseSeparatorCell` - `BaseInitializeableCell` subclass with separators support.
- **Add**: `TITableKitUtils` - set of helpers for TableKit classes.
- **Add**: `BaseTextAttributes` and `ViewText` implementation form LeadKit.
- **Update**: `BaseInitializableView` and `BaseInitializableControl` are moved to `TIUIElements` from `TIUIKitCore`.
### 0.10.9
- **Fix**: `change presentedOrTopViewController to open`.
### 0.10.8
- **Fix**: `Add presentedOrTopViewController`.
### 0.10.7
- **Fix**: `Add BaseOrientationController`.
- **Fix**: `Add videoOrientation extension`.
### 0.10.6
- **Fix**: `Add tvos exclude files`.
### 0.10.5
- **Add**: `OrientationNavigationController` .
- **Add**: `Forced Interface Orientation logic to BaseConfigurableController` .
- **Fix**: `Exclude files to watchos and tvos`.
### 0.10.4
- **Fix**: `noConnection` error.
### 0.10.3
- **Fix**: `mappingQueue` of `SessionManager`.
### 0.10.2
- **Add**: `RefreshControl` - a basic UIRefreshControl with fixed refresh action.
### 0.10.1
- **Update**: Third party dependencies: `Alamofire` 5.2.2, `RxAlamofire` 5.6.1
### 0.10.0
- **Update**: Third party dependencies: `RxSwift` (and all sub-dependencies) to 5.1.0, `Alamofire` 5.0, `SnapKit` 5.0
- **Refactored**: NetworkManager to use new Alamofire API
- **API BreakingChanges**: NetworkServiceConfiguration no longer accepts `ServerTrustPolicy`, it is now replaced by an instance of a `ServerTrustEvaluating` protocol. Full description and default implementations can be found at Alamofire [sources](https://github.com/Alamofire/Alamofire/blob/master/Source/ServerTrustEvaluation.swift). Since new evaluation is used, evaluation against self-signed certificates will now throw an AfError and abort any outcoming request. To support self-signed certificates use `DisabledTrustEvaluator` for specified host in configuration.
- **Removed**: UIImage+SupportExtensions, UIScrollView+Support
### 0.9.44
- **Add**: `TIFoundationUtils` - set of helpers for Foundation framework classes.
#### TISwiftUtils
- **Add**: `BackingStore` - a property wrapper that wraps storage and defines getter and setter for accessing value from it.
#### TIFoundationUtils
- **Add**: `CodableKeyValueStorage` - storage that can get and set codable objects by the key.
### 0.9.43
- **Fix**: `OTPSwiftView`'s dependencies.
### 0.9.42
- **Fix**: Logic bugs of `PaginationWrapper`.
### 0.9.41
- **Add**: `OTPSwiftView` - a fully customizable OTP view.
- **Add**: `BaseInitializableControl` UIControl conformance to InitializableView.
- **Add**: `TISwiftUtils` a bunch of useful helpers for development.
### 0.9.40
- **Fix**: Load more request repetion in `PaginationWrapper`.
### 0.9.39
- **Add**: `Animatable` protocol to TIUIKitCore.
- **Add**: `ActivityIndicator` protocol to TIUIKitCore.
- **Add**: `ActivityIndicatorHolder` protocol to TIUIKitCore.
- **Add**: `TIUIElements` for ui elements.
### 0.9.38
- **Add**: `BaseRxTableViewCell` is subclass of `UITableViewCell` class with support `InitializableView` and `DisposeBagHolder` protocols.
- **Add**: `ContainerTableCell` is container class that provides wrapping any `UIView` into `UITableViewCell`.
- **Add**: `BaseTappableViewModel` is simplifies interaction between view and viewModel for events of tapping.
- **Add**: `VoidTappableViewModel` is subclass of `BaseTappableViewModel` class with void payload type.
### 0.9.37
- **Fix**: ScrollView content offset of `PaginationWrapper` for iOS 13.
- **Fix**: Load more request crash of `PaginationWrapper`.
### 0.9.36
- **Add**: SPM Package.swift.
- **Add**: TITransitions via SPM.
- **Add**: TIUIKitCore via SPM.
- **Update**: Readme.
### 0.9.35
- **Add**: Selector `refreshAction()` for refresh control of `PaginationWrapper`.
### 0.9.34
- **Add**: `ButtonHolder` - protocol that contains button property.
- **Add**: `ButtonHolderView` - view which contains button.
- **Add**: Conformance `UIButton` to `ButtonHolder`.
- **Add**: Conformance `BasePlaceholderView` to `ButtonHolderView`.
- **[Breaking change]**: Replace functions `footerRetryButton() -> UIButton?` to `footerRetryView() -> ButtonHolderView?` and `footerRetryButtonHeight() -> CGFloat` to `footerRetryViewHeight() -> CGFloat` for `PaginationWrapperUIDelegate`.
- **[Breaking change]**: Replace functions `footerRetryButtonWillAppear()` to `footerRetryViewWillAppear()` and `footerRetryButtonWillDisappear()` to `footerRetryViewWillDisappear()` for `PaginationWrapperUIDelegate`.
### 0.9.33
- **Fix**: `CustomizableButtonView` container class that provides great customization.
- **Fix**: `CustomizableButtonViewModel` viewModel class for `CustomizableButtonView` configuration.
### 0.9.32
- **Fix**: `CustomizableButtonView` container class that provides great customization.
### 0.9.31
- **Add**: `@discardableResult` to function - `replace(with:at:with:manualBeginEndUpdates)` in `TableDirector`.
### 0.9.30
- **Add**: character `*` into a valid set of characters in the extension `telpromptURL` of String.
### 0.9.29
- **Update**: remove Carthage binary dependencies, update build scripts.
### 0.9.28
- **Add**: method `presentFullScreen(_ viewController:presentationStyle:animated:completion:)` for `UIViewController` that present any `viewController` modally in full screen mode by default (avoid problems with *iOS13* default presentation mode changed to `.automatic` stork)
### 0.9.27
- **Add**: method `date(from string:formats:parsedIn:)` method for `DateFormattingService` that parses date from string in one of the given formats with current region.
### 0.9.26
- **Add**: method `processResultFromConfigurationSingle` for `TotalCountCursor` that allows to get server response.
- **Add**: possibility to inherit from `TotalCountCursor`.
### 0.9.25
- **Add**: `queryItems` parameter for `ApiRequestParameters`.
- **Add**: `asQueryItems` method for `Encodable` that converts model to query items array.
### 0.9.24
- **Fix**: Make `ApiRequestParameters` properties public.
### 0.9.23
- **Add**: Rounding for `Decimal`.
- **Add**: `doubleValue` property for `Decimal`.
- **Add**: `intValue` property for `Decimal`.
- **Fix**: Rounding for `Double`.
### 0.9.22
- **Fix**: Make `Initializable` protocol public.
### 0.9.21
- **Add**: `Initializable` - common protocol for object types that can be initialized without params.
- **Add**: `instantiateArray(count:)` function in `Initializable` extension to initialize an array of instances.
### 0.9.20
- **Fix**: `bindBottomInsetBinding(from bottomInsetDriver:)` in `BaseScrollContentController` works correctly now.
### 0.9.19
- **Add**: `hexString` property for `UIColor` that returns hex representation of color as string.
### 0.9.18
- **Add**: `CustomizableButtonView` container class that provides great customization.
- **Add**: `CustomizableButtonViewModel` viewModel class for `CustomizableButtonView` configuration.
- **Add**: `CustomizableButton` class that is a `CustomizableButtonView` subview and gives it a button functionality.
### 0.9.17
- **Fix**: SpinnerView infinity animation.
### 0.9.16
- **Add**: `LabelTableViewCell` moved from `LeadKitAdditions`.
- **Add**: `SnapKit` dependency.
### 0.9.15
- **Add**: `BaseSearchViewController` class 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.
### 0.9.7
- **Add**: Carthage support.
### 0.9.6
- **Add**: Add new `configureSeparators` method to `SeparatorRowBox` array.
### 0.9.5
- **Add**: `TitleType` enum, that defines `UIViewController`'s title type.
- **Add**: `UINavigationItem.largeTitleDisplayMode` property, that defines `UINavigationItem`'s large title display mode.
- **Add**: `UIViewController.updateNavigationItemTitle` method, that takes `TitleType` as a parameter and updates `UIViewController`'s title.
### 0.9.4
- **Add**: initialization of `ApiRequestParameters`, that takes an array as a request parameter.
- **Add**: `NetworkServiceConfiguration.apiRequestParameters` method, that creates `ApiRequestParameters` with array request parameter.
- **Add**: `SessionManager.request` method, that takes an array as a request parameter.
- **Add**: `RequestUsageError` error, that represents wrong usage of requset parameters.
### 0.9.3
- **Add**: `Insert`/`Remove` section with animation functions to `TableKit`. Also make new function `Replace` that uses new `Insert`/`Remove` to animate section replace.
### 0.9.2
- **Update**: Add response to `RequestError`.
- **Fix**: Update `SessionManager+Extensions` to catch network connection error.
### 0.9.1
- **Update**: `DataRequest+Extensions` time out as network error
### 0.9.0
- **Update**: version update.
### 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**: `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.
### 0.8.12
- **Add**: `UserDefaults+Codable` is back. Now with generic subscript support.
### 0.8.11
- **Change**: `NumberFormattingService.computedFormatters` computed var reverted to static.
### 0.8.10
- **[Breaking change]**: `NumberFormattingService` methods is not static anymore.
- **Add**: `NSNumberConvertible` protocol for `NumberFormattingService` use cases.
- **Add**: `TableDirector` methods for rows insertion and removal without reload a whole table.
- **Add**: `UIImageView` binder for disclosure indicator rotation.
- **Add**: `UIView.addSubviews(:)` methods with variable number of arguments and array of views.
- **Add**: `PlaceholderConfigurable` that defines attributes and methods for view with placeholder and regular state.
- **Add**: `ContentLoadingViewModel` enum that describes possible `PlaceholderConfigurable` view states.
### 0.8.9
- **Add**: Methods `replace(with:)`, `asVoid()`, `asOptional()` to `ObservableType`, `SharedSequence` (aka `Driver`) and `Single`.
- **Add**: `Completable.deferredJust(:)` static method.
- **Add**: `ViewTextConfigurable` protocol. Conform `UILabel`, `UITextField` and `UIButton` to this protocol.
- **Add**: `BaseTextAttributes` with base text appearance attributes.
- **Update**: `ViewText.string` now uses `BaseTextAttributes` instead of separate properties.
- **Add**: `BasePlaceholderView` and `BasePlaceholderViewModel` classes used to create your own placeholder.
- **Add**: `TableKitViewModel` protocol that adds convenient extensions to cell view models that implements it.
### 0.8.8
- **Update**: Update `DateFormat` protocol. Add `dateToStringFormat` and `stringToDateFormat` according to SwiftDate 5.0.
- **Update**: Replace `String` with `DateFormat` in `DataFormattingService` date parsing methods.
- **Update**: Replace `DateInRegion` with `DateRepresentable` in `DataFormattingService` string formatting methods.
- **Add**: `parsedIn` optional parameter to date parsing method in `DataFormattingService`.
### 0.8.7
- **Add**: Base configurable controllers hierarchy with generic custom view argument (`BaseConfigurableController`, `BaseCustomViewController`, `BaseScrollContentController`, `BaseTableContentController` and `BaseCollectionContentController`).
- **Add**: `ScrollViewHolder`, `TableViewHolder` and `CollectionViewHolder` protocols.
- **Update**: Update dependencies.
- **[Breaking change]**: Update `SwiftDate` to 5.0.x.
- **[Breaking change]**: Update `DateFormattingService`. Change `format` argument from `DateFormatType` to `String`.
- **Update**: Add compile time debug messages. Improve compile time for some pieces of code.
### 0.8.6
- **Fix**: Add `trustPolicies` param to `NetworkServiceConfiguration` initialization.
- **Fix**: Update `serverTrustPolicies` to save host instead of the whole URL as a key.
- **Add**: String extension that extracts host.
### 0.8.5
- **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**: `UIApplication` extensions path for `Core-iOS-Extension` exclusions.
### 0.8.3
- **Fix**: `SpinnerView` animation freezing
### 0.8.2
- **Add**: `acceptableStatusCodes` property to `NetworkServiceConfiguration`
### 0.8.1
- **Add**: Support for `localizedComponent` for `FixedWidthInteger`
### 0.8.0
- **Add**: tests for `NetworkService`
- **Add**: `toJSON(with encoder: JSONEncoder)` method to `Encodable`
- **Add**: `failedToDecode` error case to `LeadKitError`
- **Add**: `SessionManager` class
- **Remove**: occurrences `ObjectMapper` pod and its occurrences in code
- **Update**: replace `ObjectMapper` mapping with `Decodable`
### 0.7.19
- **Fix**: `PaginationWrapper` retry button showing.
### 0.7.18
- **Update**: default implementation of `PaginationWrapperUIDelegate`.
### 0.7.17
- **Add**: `RxNetworkOperationModel` base class, `NetworkOperationState` and `NetworkOperationStateType` protocols.
### 0.7.16
- **[Breaking Change]**: Remove `ModuleConfigurator`, change type of `ConfigurableController.viewModel` property from `IUO` to plain `ViewModelT`.
- **Add**: `InitializableView` protocol with default implementation.
- **Update**: `ConfigurableController` protocol now inherit `InitializableView`.
- **[Breaking Change]**: `setAppearance` of `ConfigurableController` replaced with `configureAppearance` of `InitializableView`.
### 0.7.15
- **Fix**: `Double.roundValue(withPrecision:)` rounding issue
- **Add**: `Double+Rounding` test case
### 0.7.14
- **[Breaking Change]**: `PaginationWrapper` separating state views from data loading.
### 0.7.13
- **Update**: Migrate from `Variable` to `BehaviorRelay`.
- **Fix**: `PaginationWrapper` retry load more after fail.
- **Fix**: `safeClear` method of `TableDirector` now creates section without header and footer.
- **Add**: `TableSection` convenience initializer for section without footer and header.
### 0.7.12
- **Add**: `UniversalMappable` protocol to have ability generate generic mapping models
### 0.7.11
- **Fix**: `addHeaderBackground` cells overlapping.
### 0.7.10
- **Fix**: `wtihInsets` renamed to `with insets`
### 0.7.9
- **Fix**: timeoutInterval is set to another URLSessionConfiguration property in NetworkServiceConfiguration
### 0.7.8
- **Remove**: `App`, `Log` and `LogFormatter`.
- **Remove**: `CocoaLumberjack` dependency.
- **Add**: Rotate operation for image drawing.
- **Add**: `mapViewEvents` overload with closure that returns array of disposables.
- **Update**: Update `ObjectMapper` to 3.1.
- **Add**: `apiRequestParameters` method to `NetworkServiceConfiguration` extension.
- **Update**: Rename setToCenter(withInsets:) to pintToSuperview(withInsets:excluding:)
- **Update**: Added parameter "edges" with label "excluding" to aforementioned method
### 0.7.7
- **Fix**: Fix doubling separator line issue
### 0.7.6
- **Add**: `NetworkServiceConfiguration` to configure NetworkService instance
- **Remove**: `ConfigurableNetworkSevice` protocol
- **Update**: Acceptable status codes in SessionManager become `Set<Int>`
### 0.7.5
- **Add**: `topConfiguration` and `bottomConfiguration` properties, methods to configure top and bottom separators in `CellSeparatorType` extension.
- **Add**: `totalHeight` property in `SeparatorConfiguration` extension.
### 0.7.4
- **Update**: Exclude UIApplication extensions from iOS-Extension subspec.
### 0.7.3
- **Update**: Xcode 9.3 migration.
- **Remove**: Default initializer for Network service that conforms to `ConfigurableNetworkService` protocol.
- **[Breaking Change]**: `DateFormattingService` class replaced with protocol.
- **Add**: `SwiftDate` dependency for `DateFormattingService`.
- **Add**: `ViewBackground` enum that describes possible view backgrounds.
- **Add**: `ViewText` enum that describes text with appearance options.
- **Removed**: `String+SizeCalculation` extension.
### 0.7.2
- **Fixed**: Change root controller for window
### 0.7.1
- **Add**: Extension for comparing optional arrays (`[T]?`) with `Equatable` elements.
- **Add**: `additionalHttpHeaders` static field in `ConfigurableNetworkService` protocol.
- **Add**: Default initializer for Network service that conforms to `ConfigurableNetworkService` protocol.
## 0.7.0
- **Add**: `TotalCountCursor` for total count based pagination and related stuff.
- **[Breaking Change]**: `PaginationTableViewWrapper` and `PaginationTableViewWrapperDelegate` was renamed to `PaginationWrapper` and `PaginationWrapperDelegate `. Also there is significant changes in api
- **Add**: `GeneralDataLoadingModel` and `PaginationDataLoadingModel` for regular and paginated data loading with state handling.
- **Add**: `GeneralDataLoadingViewModel` and `GeneralDataLoadingController` for regular data loading and state handling in UI.
- **Add**: `ConfigurableNetworkService` - replacement of `DefaultNetworkService` from LeadKitAdditions.
- **Add**: `NumberFormattingService` and `NumberFormat` protocols with default implementation for creating per-project number formatters.
- **Add**: Very flexible in configuration `TextFieldViewModel` with build-in two-side data model binding.
- **Add**: `SingleLoadCursorConfiguration` as a replacement of `SingleLoadCursor`.
- **Add**: `UIApplication` extensions for making phone calls.
- **Add**: `NSAttributedString` extensions for appending attributed strings using `+` operator.
- **Change**: Lots of fixes and enhancements.
- **Update**: Update dependecies versions.
### 0.6.7
- **Add**: UITableView extension to add colored background for tableview bounce area.
@ -16,13 +861,13 @@
- **Fix**: SpinnerView bug(no animation) in Swift 4.
## 0.6.3
### 0.6.3
- **Fix**: SeparatorCell updates constraints after setting separator insets
## 0.6.2
### 0.6.2
- **Fix**: AlamofireManager extension no longer performs requests with default manager
## 0.6.1
### 0.6.1
- **New**: `RequestError`. Represents general api request errors
- **Change**: All api methods now throws `RequestError` when fails.
@ -38,62 +883,59 @@
- **Remove**: `Observable` creation for `ImmutableMappable`
- **Remove**: `UIView` and `UsedDefaults` extensions, `EstimatedViewHeightProtocol`, `StaticEstimatedViewHeightProtocol`, `StoryboardIdentifierProtocol`
## 0.5.18
### 0.5.18
- **Fix**: EmptyCell first appearance setup fix
## 0.5.17
### 0.5.17
- **Fix**: EmptyCell reusing appearance fix
- **Fix**: SeparatorCell reusing separators fix
## 0.5.16
### 0.5.16
- **Change**: Rename `AppearanceProtocol` to `AppearanceConfigurable`
- **Add**: `subscript(safe:)` subscript to `Array` extension for safe access to element by index
## 0.5.15
### 0.5.15
- **Add**: `AppearanceProtocol` which ensures that specific type can apply appearance to itself
- **Add**: `with(appearance:)`, `set(appearance:)` methods to TableRow extension
- **Add**: `Appearance` to `EmptyCell`
- **Remove**: `SeparatorCellViewModel`.
## 0.5.13
### 0.5.13
- **Change**: Remove type erasure behavior from `AnyBaseTableRow`
- **Change**: Rename `AnyBaseTableRow` class to `SeparatorRowBox`
- **Change**: Move `anyRow` property from `EmptyCellRow` to `TableRow` extension and rename it to `separatorRowBox`.
- **Change**: Move `configure(extreme: middle:)` method from `TableDirector` extension to `Array` extension and rename it to `configureSeparators(extreme: middle:)`
## 0.5.12
### 0.5.12
- **Fix**: Update type of `viewModel` in `ConfigurableController` to `ImplicitlyUwrappedOptional<ViewModelT>` instead of `ViewModelT`
## 0.5.11
### 0.5.11
- **[Breaking Change]**: rename initializer from `init(initialFrom:)` to `init(resetFrom:)` in `ResettableType`
- **Add**: `SeparatorCell` with `SeparatorCellViewModel`
- **Add**: `AnyBaseTableRow` for type-erasure
- **Add**: `EmptyCellRow` for empty cell with static height
## 0.5.10
### 0.5.10
- **Fix**: `Public` modifier for `SpinnerView`
- **Fix**: `Public` modifier for `SpinnerView`
## 0.5.9
### 0.5.9
- **Fix**: One-two-many fixed for values more than 99
## 0.5.8
### 0.5.8
- **Fix**: Synchronization over `NSRecursiveLock` for request count tracker in NetworkService
## 0.5.7
### 0.5.7
- **Add**: String extension `localizedComponent(value:stringOne:stringTwo:stringMany:)`
## 0.5.6
### 0.5.6
- **Fix**: Clear tableview if placeholder is shown

7
Cartfile Normal file
View File

@ -0,0 +1,7 @@
github "malcommac/SwiftDate"
github "Alamofire/Alamofire"
github "RxSwiftCommunity/RxAlamofire" ~> 6.1
github "TouchInstinct/TableKit"
github "ReactiveX/RxSwift" ~> 6.2
github "pronebird/UIScrollView-InfiniteScroll" "1.1.0"
github "SnapKit/SnapKit" ~> 5.0

7
Cartfile.resolved Normal file
View File

@ -0,0 +1,7 @@
github "Alamofire/Alamofire" "5.4.3"
github "ReactiveX/RxSwift" "6.2.0"
github "RxSwiftCommunity/RxAlamofire" "v6.1.2"
github "SnapKit/SnapKit" "5.0.1"
github "TouchInstinct/TableKit" "2.10008.1"
github "malcommac/SwiftDate" "6.3.1"
github "pronebird/UIScrollView-InfiniteScroll" "1.1.0"

5
Gemfile Normal file
View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem "cocoapods", "~> 1.11"

98
Gemfile.lock Normal file
View File

@ -0,0 +1,98 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
rexml
activesupport (6.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
claide (1.1.0)
cocoapods (1.11.3)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.11.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.3)
activesupport (>= 5.0, < 7)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.6.2)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.1.10)
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
ffi (1.15.5)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (1.10.0)
concurrent-ruby (~> 1.0)
json (2.6.1)
minitest (5.15.0)
molinillo (0.8.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.6)
rexml (3.2.5)
ruby-macho (2.5.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
zeitwerk (2.5.4)
PLATFORMS
x86_64-darwin-20
x86_64-darwin-21
DEPENDENCIES
cocoapods (~> 1.11)
BUNDLED WITH
2.3.26

View File

@ -1,118 +1,120 @@
Pod::Spec.new do |s|
s.name = "LeadKit"
s.version = "0.6.7"
s.version = "1.35.0"
s.summary = "iOS framework with a bunch of tools for rapid development"
s.homepage = "https://github.com/TouchInstinct/LeadKit"
s.homepage = "https://git.svc.touchin.ru/TouchInstinct/LeadKit"
s.license = "Apache License, Version 2.0"
s.author = "Touch Instinct"
s.source = { :git => "https://github.com/TouchInstinct/LeadKit.git", :tag => s.version }
s.platform = :ios, '9.0'
s.source = { :git => "https://git.svc.touchin.ru/TouchInstinct/LeadKit.git", :tag => s.version }
s.platform = :ios, '10.0'
s.swift_versions = ['5.1']
s.subspec 'UIColorHex' do |ss|
ss.ios.deployment_target = '8.0'
ss.tvos.deployment_target = '9.0'
ss.watchos.deployment_target = '2.0'
ss.ios.deployment_target = '10.0'
ss.tvos.deployment_target = '10.0'
ss.watchos.deployment_target = '3.0'
ss.source_files = "Sources/Extensions/UIColor/UIColor+Hex.swift"
end
s.subspec 'Drawing' do |ss|
# ss.ios.deployment_target = '8.0' # can't get it work: DrawingOperation.swift:29:17: note: did you mean 'DrawingOperation'?
# ss.tvos.deployment_target = '9.0' # can't get it work: DrawingOperation.swift:29:17: note: did you mean 'DrawingOperation'?
ss.watchos.deployment_target = '2.0'
ss.source_files = [
"Sources/Enums/ResizeMode.swift",
"Sources/Extensions/{CGContext,CGImage,CGSize,UIImage}/*",
"Sources/Protocols/{DrawingOperation,SupportProtocol}.swift",
"Sources/Structures/Drawing/*",
]
ss.watchos.exclude_files = [
"Sources/Structures/Drawing/CALayerDrawingOperation.swift",
"Sources/Extensions/UIImage/*",
]
end
s.subspec 'Core' do |ss|
ss.ios.deployment_target = '9.0'
ss.tvos.deployment_target = '9.0'
ss.watchos.deployment_target = '2.0'
ss.ios.deployment_target = '10.0'
ss.tvos.deployment_target = '10.0'
ss.watchos.deployment_target = '3.0'
ss.source_files = "Sources/**/*.swift"
ss.watchos.exclude_files = [
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Classes/Controllers/**/*",
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/BaseRxTableViewCell/*",
"Sources/Classes/Views/ContainerTableCell/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/Views/LabelTableViewCell/*",
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Classes/Views/XibView/*",
"Sources/Classes/Views/SpinnerView/*",
"Sources/Classes/Views/DefaultPlaceholders/*",
"Sources/Classes/Views/CollectionViewWrapperView/*",
"Sources/Classes/Views/TableViewWrapperView/*",
"Sources/Classes/Views/BasePlaceholderView/*",
"Sources/Classes/Views/CustomizableButton/*",
"Sources/Classes/Search/*",
"Sources/Enums/Search/*",
"Sources/Extensions/CABasicAnimation/*",
"Sources/Extensions/CGFloat/CGFloat+Pixels.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/NetworkService/NetworkService+RxLoadImage.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/Support/UIScrollView+Support.swift",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/Support/UINavigationItem+Support.swift",
"Sources/Extensions/TableKit/**/*.swift",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift",
"Sources/Extensions/UIActivityIndicatorView/*",
"Sources/Extensions/UIAlertcontroller/*",
"Sources/Extensions/UICollectionView/*",
"Sources/Extensions/UIDevice/*",
"Sources/Extensions/UIImage/*",
"Sources/Extensions/UITableView/*",
"Sources/Extensions/UIView/*",
"Sources/Extensions/UIViewController/*",
"Sources/Extensions/UIWindow/*",
"Sources/Extensions/Array/Array+RowExtensions.swift",
"Sources/Extensions/Drawing/UIImage/*",
"Sources/Extensions/UIKit/**/*.swift",
"Sources/Extensions/Views/ViewBackground/*",
"Sources/Extensions/Views/SeparatorCell/*",
"Sources/Extensions/Views/ConfigurableView/*",
"Sources/Extensions/Views/PlaceholderConfigurable/*",
"Sources/Protocols/UIKit/**/*.swift",
"Sources/Protocols/LoadingIndicator.swift",
"Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift",
"Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift",
"Sources/Protocols/Views/SeparatorCell/*",
"Sources/Protocols/Views/PlaceholderConfigurable/*",
"Sources/Protocols/TableKit/**/*",
"Sources/Protocols/Controllers/SearchResultsViewController.swift",
"Sources/Structures/Views/AnyLoadingIndicator.swift",
"Sources/Structures/DrawingOperations/CALayerDrawingOperation.swift",
"Sources/Structures/DrawingOperations/RoundDrawingOperation.swift",
"Sources/Structures/DrawingOperations/BorderDrawingOperation.swift",
"Sources/Structures/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/UIInterfaceOrientation/*"
]
ss.tvos.exclude_files = [
"Sources/Classes/Controllers/BaseConfigurableController.swift",
"Sources/Classes/Controllers/BaseCollectionContentController.swift",
"Sources/Classes/Views/TableViewWrapperView/TableViewWrapperView.swift",
"Sources/Classes/Views/CollectionViewWrapperView/CollectionViewWrapperView.swift",
"Sources/Classes/Controllers/BaseScrollContentController.swift",
"Sources/Classes/Controllers/BaseCustomViewController.swift",
"Sources/Classes/Controllers/BaseOrientationNavigationController.swift",
"Sources/Extensions/UIKit/UIDevice/UIDevice+ScreenOrientation.swift",
"Sources/Classes/Controllers/BaseTableContentController.swift",
"Sources/Classes/Views/BaseRxTableViewCell/*",
"Sources/Classes/Views/ContainerTableCell/*",
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Classes/Views/LabelTableViewCell/*",
"Sources/Classes/Views/CustomizableButton/*",
"Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift",
"Sources/Classes/Search/*",
"Sources/Structures/Drawing/CALayerDrawingOperation.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/Support/UIScrollView+Support.swift",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift"
"Sources/Enums/Search/*",
"Sources/Extensions/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/Support/UINavigationItem+Support.swift",
"Sources/Extensions/TableKit/**/*.swift",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift",
"Sources/Extensions/Array/Array+RowExtensions.swift",
"Sources/Extensions/Views/SeparatorCell/*",
"Sources/Protocols/DataLoading/PaginationDataLoading/PaginationWrappable.swift",
"Sources/Protocols/Views/SeparatorCell/*",
"Sources/Protocols/TableKit/**/*",
"Sources/Protocols/Controllers/SearchResultsViewController.swift",
"Sources/Structures/DataLoading/PaginationDataLoading/*",
"Sources/Extensions/UIInterfaceOrientation/*",
"Sources/Classes/Controllers/BaseOrientationController.swift"
]
ss.dependency "CocoaLumberjack/Swift", '~> 3.3.0'
ss.dependency "RxSwift", '4.0.0'
ss.dependency "RxCocoa", '4.0.0'
ss.dependency "RxAlamofire", '4.0.0'
ss.dependency "ObjectMapper", '~> 3.0.0'
ss.dependency "RxSwift", '~> 6.2'
ss.dependency "RxCocoa", '~> 6.2'
ss.dependency "RxAlamofire", '~> 6.1'
ss.dependency "SwiftDate", '~> 6'
ss.ios.dependency "TableKit", '~> 2.5.0'
ss.ios.dependency "UIScrollView-InfiniteScroll", '~> 1.0.0'
end
s.subspec 'Core-iOS-Extension' do |ss|
ss.platform = :ios, '9.0'
ss.source_files = "Sources/**/*.swift"
ss.exclude_files = [
"Sources/Classes/Views/SeparatorRowBox/*",
"Sources/Classes/Views/SeparatorCell/*",
"Sources/Classes/Views/EmptyCell/*",
"Sources/Classes/Pagination/PaginationTableViewWrapper.swift",
"Sources/Extensions/NetworkService/NetworkService+ActivityIndicator.swift",
"Sources/Extensions/PaginationTableViewWrapperDelegate/PaginationTableViewWrapperDelegate+DefaultImplementation.swift",
"Sources/Extensions/TableDirector/*",
"Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift"
]
ss.dependency "CocoaLumberjack/Swift", '~> 3.3.0'
ss.dependency "RxSwift", '4.0.0'
ss.dependency "RxCocoa", '4.0.0'
ss.dependency "RxAlamofire", '4.0.0'
ss.dependency "ObjectMapper", '~> 3.0.0'
ss.ios.dependency "TableKit", '~> 2.11'
ss.ios.dependency "SnapKit", '~> 5.0.1'
ss.ios.dependency "UIScrollView-InfiniteScroll", '~> 1.1.0'
end
s.default_subspec = 'Core'

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1230"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67186B271EB248F100CFAFFB"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit iOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67186B271EB248F100CFAFFB"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit iOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67186B2F1EB248F100CFAFFB"
BuildableName = "LeadKit iOSTests.xctest"
BlueprintName = "LeadKit iOSTests"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67186B271EB248F100CFAFFB"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit iOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67186B271EB248F100CFAFFB"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit iOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1230"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6782BB9F1EB31D590086E0B8"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit tvOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6782BB9F1EB31D590086E0B8"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit tvOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6782BBA71EB31D5A0086E0B8"
BuildableName = "LeadKit tvOSTests.xctest"
BlueprintName = "LeadKit tvOSTests"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6782BB9F1EB31D590086E0B8"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit tvOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6782BB9F1EB31D590086E0B8"
BuildableName = "LeadKit.framework"
BlueprintName = "LeadKit tvOS"
ReferencedContainer = "container:LeadKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

107
Makefile Normal file
View File

@ -0,0 +1,107 @@
export SRCROOT := $(shell pwd)
push_to_podspecs: TISwiftUtils.target TIFoundationUtils.target TICoreGraphicsUtils.target TIKeychainUtils.target TIUIKitCore.target TIUIElements.target TIWebView.target TIBottomSheet.target TISwiftUICore.target TITableKitUtils.target TIDeeplink.target TIDeveloperUtils.target TILogging.target TINetworking.target TIMoyaNetworking.target TINetworkingCache.target TIMapUtils.target TIAppleMapUtils.target TIGoogleMapUtils.target TIPagination.target TIAuth.target TIEcommerce.target TITextProcessing.target TIApplication.target
$(call clean)
TISwiftUtils.target:
MODULE_NAME="TISwiftUtils" ./project-scripts/push_to_podspecs.sh
touch TISwiftUtils.target
TIFoundationUtils.target: TISwiftUtils.target TILogging.target
MODULE_NAME="TIFoundationUtils" ./project-scripts/push_to_podspecs.sh
touch TIFoundationUtils.target
TICoreGraphicsUtils.target:
MODULE_NAME="TICoreGraphicsUtils" ./project-scripts/push_to_podspecs.sh
touch TICoreGraphicsUtils.target
TIKeychainUtils.target: TIFoundationUtils.target
MODULE_NAME="TIKeychainUtils" ./project-scripts/push_to_podspecs.sh
touch TIKeychainUtils.target
TIUIKitCore.target: TISwiftUtils.target
MODULE_NAME="TIUIKitCore" ./project-scripts/push_to_podspecs.sh
touch TIUIKitCore.target
TIUIElements.target: TIUIKitCore.target TILogging.target
MODULE_NAME="TIUIElements" ./project-scripts/push_to_podspecs.sh
touch TIUIElements.target
TIWebView.target: TIUIKitCore.target
MODULE_NAME="TIWebView" ./project-scripts/push_to_podspecs.sh
touch TIWebView.target
TIBottomSheet.target: TIUIElements.target
MODULE_NAME="TIBottomSheet" ./project-scripts/push_to_podspecs.sh
touch TIBottomSheet.target
TISwiftUICore.target: TIUIKitCore.target
MODULE_NAME="TISwiftUICore" ./project-scripts/push_to_podspecs.sh
touch TISwiftUICore.target
TITableKitUtils.target: TIUIElements.target
MODULE_NAME="TITableKitUtils" ./project-scripts/push_to_podspecs.sh
touch TITableKitUtils.target
TIDeeplink.target: TIFoundationUtils.target
MODULE_NAME="TIDeeplink" ./project-scripts/push_to_podspecs.sh
touch TIDeeplink.target
TIDeveloperUtils.target: TIUIElements.target
MODULE_NAME="TIDeveloperUtils" ./project-scripts/push_to_podspecs.sh
touch TIDeveloperUtils.target
TINetworking.target: TIFoundationUtils.target
MODULE_NAME="TINetworking" ./project-scripts/push_to_podspecs.sh
touch TINetworking.target
TILogging.target:
MODULE_NAME="TILogging" ./project-scripts/push_to_podspecs.sh
touch TILogging.target
TIMoyaNetworking.target: TINetworking.target
MODULE_NAME="TIMoyaNetworking" ./project-scripts/push_to_podspecs.sh
touch TIMoyaNetworking.target
TINetworkingCache.target: TINetworking.target
MODULE_NAME="TINetworkingCache" ./project-scripts/push_to_podspecs.sh
touch TINetworkingCache.target
TIMapUtils.target: TILogging TICoreGraphicsUtils.target
MODULE_NAME="TIMapUtils" ./project-scripts/push_to_podspecs.sh
touch TIMapUtils.target
TIAppleMapUtils.target: TIMapUtils.target
MODULE_NAME="TIAppleMapUtils" ./project-scripts/push_to_podspecs.sh
touch TIAppleMapUtils.target
TIGoogleMapUtils.target: TIMapUtils.target
MODULE_NAME="TIGoogleMapUtils" ./project-scripts/push_to_podspecs.sh
touch TIGoogleMapUtils.target
TIYandexMapUtils.target: TIMapUtils.target
MODULE_NAME="TIYandexMapUtils" ./project-scripts/push_to_podspecs.sh
touch TIYandexMapUtils.target
TIPagination.target: TISwiftUtils.target
MODULE_NAME="TIPagination" ./project-scripts/push_to_podspecs.sh
touch TIPagination.target
TIAuth.target: TIUIKitCore.target TIKeychainUtils.target
MODULE_NAME="TIAuth" ./project-scripts/push_to_podspecs.sh
touch TIAuth.target
TIEcommerce.target: TINetworking.target TIUIElements.target
MODULE_NAME="TIEcommerce" ./project-scripts/push_to_podspecs.sh
touch TIEcommerce.target
TITextProcessing.target:
MODULE_NAME="TITextProcessing" ./project-scripts/push_to_podspecs.sh
touch TITextProcessing.target
TIApplication.target: TIFoundationUtils.target TILogging.target
MODULE_NAME="TIApplication" ./project-scripts/push_to_podspecs.sh
touch TIApplication.target
clean:
rm *.target

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

151
OTPSwiftView/README.md Normal file
View File

@ -0,0 +1,151 @@
# OTPSwiftView
![Platform](https://img.shields.io/badge/platform-iOS-green)
A fully customizable OTP view.
<p align="left">
<img src="Assets/preview.gif" width=300 height=533>
</p>
# Usage
```swift
class ViewController: UIViewController {
let otpView = CustomOTPSwiftView() // Custom OTP view
let config = OTPCodeConfig(codeSymbolsCount: 6, // Base configuration of OTP view
spacing: 6,
customSpacing: [2: 20])
override func viewDidLoad() {
super.viewDidLoad()
/*
Add your codeView and set layout
*/
/* Configure OTP view */
otpView.configure(with: config)
/* Bind events */
otpView.onTextEnter = { code in
// Get code from codeView
}
/* Update text */
otpView.code = "234435"
/* Update focus */
otpView.beginFirstResponder() // show keyboard
otpView.resignFirstResponder() // hide keyboard
}
}
```
# Customization
## Single OTP View
*OTPView* is a base class that describes a single OTP textfield.
To customize the appearance and layout, you must inherit from the OTPView.
*Don't forget to add UIGestureRecognizer to call closure `onTap?()`. Use UITapGestureRecognizer to avoid bugs.*
```swift
import OTPSwiftView
class CustomOTPView: OTPView {
override func addViews() {
super.addViews()
// Adding additional views to current view. The OTP textfield has already been added.
}
override func configureLayout() {
super.configureLayout()
// Confgiure layout of subviews
}
override func bindViews() {
super.bindViews()
// Binding to data or user actions
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTapAction))
addGestureRecognizer(gesture)
}
private func onTapAction() {
onTap?()
}
override func configureAppearance() {
super.configureAppearance()
// Appearance configuration method
}
}
```
*If needed to set validation for input use `validationClosure: ValidationClosure<String>?`*. For example, only numbers validation:
```swift
import OTPSwiftView
class CustomOTPView: OTPView {
override func bindViews() {
super.bindViews()
codeTextField.validationClosure = { input in
input.allSatisfy { $0.isNumber }
}
}
}
```
## OTPSwiftView
*OTPSwiftView* is a base class that is responsible for the layout of single OTP views.
As with OTPView, you should create an heir class to configure your full OTP view.
```swift
import OTPSwiftView
final class CustomOTPSwiftView: OTPSwiftView<CustomOTPView> {
override func addViews() {
super.addViews()
// Adding additional views to current code view. The single OTP views has already been added.
}
override func configureLayout() {
super.configureLayout()
// Confgiure layout of subviews
}
override func bindViews() {
super.bindViews()
// Binding to data or user actions
}
override func configureAppearance() {
super.configureAppearance()
// Appearance configuration method
}
override func configure(with config: OTPCodeConfig) {
super.configure(with: config)
// Configure you code view with configuration
}
}
```
# Installation via SPM
You can install this framework as a target of LeadKit.

View File

@ -0,0 +1,38 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Base configuration for OTPSwiftView
open class OTPCodeConfig {
public typealias Spacing = [Int: CGFloat]
public let codeSymbolsCount: Int
public let spacing: CGFloat
public let customSpacing: Spacing?
public init(codeSymbolsCount: Int, spacing: CGFloat, customSpacing: Spacing?) {
self.codeSymbolsCount = codeSymbolsCount
self.spacing = spacing
self.customSpacing = customSpacing
}
}

View File

@ -0,0 +1,149 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
import TIUIElements
import TISwiftUtils
/// Base full OTP View for entering the verification code
open class OTPSwiftView<View: OTPView>: BaseInitializableControl {
private var emptyOTPView: View? {
textFieldsCollection.first { $0.codeTextField.text.orEmpty.isEmpty } ?? textFieldsCollection.last
}
public private(set) var codeStackView = UIStackView()
public private(set) var textFieldsCollection: [View] = []
public var onTextEnter: ParameterClosure<String>?
public var code: String {
get {
textFieldsCollection.compactMap { $0.codeTextField.text }.joined()
}
set {
textFieldsCollection.first?.codeTextField.set(inputText: newValue)
}
}
public override var isFirstResponder: Bool {
!textFieldsCollection.allSatisfy { !$0.codeTextField.isFirstResponder }
}
open override func addViews() {
super.addViews()
addSubview(codeStackView)
}
open override func configureAppearance() {
super.configureAppearance()
codeStackView.contentMode = .center
codeStackView.distribution = .fillEqually
}
open func configure(with config: OTPCodeConfig) {
textFieldsCollection = createTextFields(numberOfFields: config.codeSymbolsCount)
codeStackView.addArrangedSubviews(textFieldsCollection)
codeStackView.spacing = config.spacing
configure(customSpacing: config.customSpacing, for: codeStackView)
bindTextFields(with: config)
}
@discardableResult
open override func becomeFirstResponder() -> Bool {
guard let emptyOTPView = emptyOTPView, !emptyOTPView.isFirstResponder else {
return false
}
return emptyOTPView.codeTextField.becomeFirstResponder()
}
@discardableResult
open override func resignFirstResponder() -> Bool {
guard let emptyOTPView = emptyOTPView, emptyOTPView.isFirstResponder else {
return false
}
return emptyOTPView.codeTextField.resignFirstResponder()
}
}
// MARK: - Configure textfields
private extension OTPSwiftView {
func configure(customSpacing: OTPCodeConfig.Spacing?, for stackView: UIStackView) {
guard let customSpacing = customSpacing else {
return
}
customSpacing.forEach { viewIndex, spacing in
guard viewIndex < stackView.arrangedSubviews.count, viewIndex >= .zero else {
return
}
self.set(spacing: spacing,
after: stackView.arrangedSubviews[viewIndex],
for: stackView)
}
}
func set(spacing: CGFloat, after view: UIView, for stackView: UIStackView) {
stackView.setCustomSpacing(spacing, after: view)
}
func createTextFields(numberOfFields: Int) -> [View] {
var textFieldsCollection: [View] = []
(.zero..<numberOfFields).forEach { _ in
let textField = View()
textField.codeTextField.previousTextField = textFieldsCollection.last?.codeTextField
textFieldsCollection.last?.codeTextField.nextTextField = textField.codeTextField
textFieldsCollection.append(textField)
}
return textFieldsCollection
}
func bindTextFields(with config: OTPCodeConfig) {
let onTextChangedSignal: VoidClosure = { [weak self] in
guard let code = self?.code else {
return
}
let correctedCode = code.prefix(config.codeSymbolsCount).string
self?.onTextEnter?(correctedCode)
}
let onTap: VoidClosure = { [weak self] in
self?.becomeFirstResponder()
}
textFieldsCollection.forEach {
$0.codeTextField.onTextChangedSignal = onTextChangedSignal
$0.onTap = onTap
}
}
}

View File

@ -0,0 +1,131 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
import TISwiftUtils
/// Base one symbol textfield
open class OTPTextField: UITextField {
private let maxSymbolsCount = 1
public weak var previousTextField: OTPTextField?
public weak var nextTextField: OTPTextField?
public var onTextChangedSignal: VoidClosure?
public var validationClosure: Closure<String, Bool>?
public var caretHeight: CGFloat?
public var lastNotEmpty: OTPTextField {
let isLastNotEmpty = !text.orEmpty.isEmpty && nextTextField?.text.orEmpty.isEmpty ?? true
return isLastNotEmpty ? self : nextTextField?.lastNotEmpty ?? self
}
open override var font: UIFont? {
didSet {
if caretHeight == nil, let font = font {
caretHeight = font.pointSize - font.descender
}
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
}
@available(*, unavailable)
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
open override func deleteBackward() {
guard text.orEmpty.isEmpty else {
return
}
onTextChangedSignal?()
previousTextField?.text = ""
previousTextField?.becomeFirstResponder()
}
public func set(inputText: String) {
text = inputText.prefix(maxSymbolsCount).string
let nextInputText = inputText.count >= maxSymbolsCount
? inputText.suffix(inputText.count - maxSymbolsCount).string
: ""
nextTextField?.set(inputText: nextInputText)
}
open override func caretRect(for position: UITextPosition) -> CGRect {
guard let caretHeight = caretHeight else {
return super.caretRect(for: position)
}
var superRect = super.caretRect(for: position)
superRect.size.height = caretHeight
return superRect
}
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self && isFirstResponder ? view : nil
}
}
extension OTPTextField: UITextFieldDelegate {
public func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
guard let textField = textField as? OTPTextField else {
return true
}
let isInputEmpty = textField.text.orEmpty.isEmpty && string.isEmpty
guard isInputEmpty || validationClosure?(string) ?? true else {
return false
}
switch range.length {
case 0: // set text to textfield
textField.set(inputText: string)
let currentTextField = textField.lastNotEmpty.nextTextField ?? textField.lastNotEmpty
currentTextField.becomeFirstResponder()
textField.onTextChangedSignal?()
return false
case 1: // remove character from textfield
textField.text = ""
textField.onTextChangedSignal?()
return false
default:
return true
}
}
}

View File

@ -0,0 +1,37 @@
//
// Copyright (c) 2020 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 TIUIElements
import TISwiftUtils
/// Base OTP view with textfield for entering a one symbol
open class OTPView: BaseInitializableView {
public let codeTextField = OTPTextField()
public var onTap: VoidClosure?
open override func addViews() {
super.addViews()
addSubview(codeTextField)
}
}

95
Package.resolved Normal file
View File

@ -0,0 +1,95 @@
{
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
"version" : "5.7.1"
}
},
{
"identity" : "antlr4",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antlr/antlr4",
"state" : {
"revision" : "44d87bc1d130c88aa452894aa5f7e2f710f68253",
"version" : "4.10.1"
}
},
{
"identity" : "cache",
"kind" : "remoteSourceControl",
"location" : "https://github.com/hyperoslo/Cache.git",
"state" : {
"revision" : "c7f4d633049c3bd649a353bad36f6c17e9df085f",
"version" : "6.0.0"
}
},
{
"identity" : "cursors",
"kind" : "remoteSourceControl",
"location" : "https://github.com/petropavel13/Cursors",
"state" : {
"revision" : "52f27b82cb1cbbc2b5fd09514c48b9c75e3b0300",
"version" : "0.6.0"
}
},
{
"identity" : "keychainaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
}
},
{
"identity" : "moya",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Moya/Moya.git",
"state" : {
"revision" : "c263811c1f3dbf002be9bd83107f7cdc38992b26",
"version" : "15.0.3"
}
},
{
"identity" : "panmodal",
"kind" : "remoteSourceControl",
"location" : "https://git.svc.touchin.ru/TouchInstinct/PanModal",
"state" : {
"revision" : "ced7c1703f90746df0224b6e0d33c146d9ae4284",
"version" : "1.3.1"
}
},
{
"identity" : "reactiveswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ReactiveCocoa/ReactiveSwift.git",
"state" : {
"revision" : "c43bae3dac73fdd3cb906bd5a1914686ca71ed3c",
"version" : "6.7.0"
}
},
{
"identity" : "rxswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ReactiveX/RxSwift.git",
"state" : {
"revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4",
"version" : "6.6.0"
}
},
{
"identity" : "tablekit",
"kind" : "remoteSourceControl",
"location" : "https://git.svc.touchin.ru/TouchInstinct/TableKit.git",
"state" : {
"revision" : "fec9537745799fab55df7477cb3ec2b4ea5c254d",
"version" : "2.12.0"
}
}
],
"version" : 2
}

198
Package.swift Normal file
View File

@ -0,0 +1,198 @@
// swift-tools-version:5.7
#if canImport(PackageDescription)
import PackageDescription
let package = Package(
name: "LeadKit",
platforms: [
.iOS(.v12)
],
products: [
// MARK: - Application
.library(name: "TIApplication", targets: ["TIApplication"]),
// MARK: - UIKit
.library(name: "TIUIKitCore", targets: ["TIUIKitCore"]),
.library(name: "TIUIElements", targets: ["TIUIElements"]),
.library(name: "TIWebView", targets: ["TIWebView"]),
.library(name: "TIBottomSheet", targets: ["TIBottomSheet"]),
// MARK: - SwiftUI
.library(name: "TISwiftUICore", targets: ["TISwiftUICore"]),
// MARK: - Utils
.library(name: "TISwiftUtils", targets: ["TISwiftUtils"]),
.library(name: "TIFoundationUtils", targets: ["TIFoundationUtils"]),
.library(name: "TICoreGraphicsUtils", targets: ["TICoreGraphicsUtils"]),
.library(name: "TIKeychainUtils", targets: ["TIKeychainUtils"]),
.library(name: "TITableKitUtils", targets: ["TITableKitUtils"]),
.library(name: "TIDeeplink", targets: ["TIDeeplink"]),
.library(name: "TIDeveloperUtils", targets: ["TIDeveloperUtils"]),
// MARK: - Networking
.library(name: "TINetworking", targets: ["TINetworking"]),
.library(name: "TIMoyaNetworking", targets: ["TIMoyaNetworking"]),
.library(name: "TINetworkingCache", targets: ["TINetworkingCache"]),
// MARK: - Maps
.library(name: "TIMapUtils", targets: ["TIMapUtils"]),
.library(name: "TIAppleMapUtils", targets: ["TIAppleMapUtils"]),
// MARK: - Elements
.library(name: "OTPSwiftView", targets: ["OTPSwiftView"]),
.library(name: "TITransitions", targets: ["TITransitions"]),
.library(name: "TIPagination", targets: ["TIPagination"]),
.library(name: "TIAuth", targets: ["TIAuth"]),
.library(name: "TIEcommerce", targets: ["TIEcommerce"]),
.library(name: "TITextProcessing", targets: ["TITextProcessing"])
],
dependencies: [
.package(url: "https://git.svc.touchin.ru/TouchInstinct/TableKit.git", .upToNextMinor(from: "2.12.0")),
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", .upToNextMajor(from: "4.2.2")),
.package(url: "https://github.com/petropavel13/Cursors", .upToNextMajor(from: "0.5.1")),
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.4.0")),
.package(url: "https://github.com/Moya/Moya.git", .upToNextMajor(from: "15.0.0")),
.package(url: "https://github.com/hyperoslo/Cache.git", .upToNextMajor(from: "6.0.0")),
.package(url: "https://github.com/antlr/antlr4", .upToNextMinor(from: "4.10.1")),
.package(url: "https://git.svc.touchin.ru/TouchInstinct/PanModal", .upToNextMinor(from: "1.3.0"))
],
targets: [
// MARK: - Application architecture
.target(name: "TIApplication",
dependencies: ["TILogging", "TIFoundationUtils", "KeychainAccess"],
path: "TIApplication/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
// MARK: - UIKit
.target(name: "TIUIKitCore", dependencies: ["TISwiftUtils"], path: "TIUIKitCore/Sources"),
.target(name: "TIUIElements",
dependencies: ["TIUIKitCore", "TILogging"],
path: "TIUIElements/Sources",
exclude: ["../TIUIElements.app"],
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TIWebView", dependencies: ["TIUIKitCore", "TISwiftUtils"], path: "TIWebView/Sources"),
.target(name: "TIBottomSheet",
dependencies: ["PanModal", "TIUIElements", "TIUIKitCore", "TISwiftUtils"],
path: "TIBottomSheet/Sources",
exclude: ["../TIBottomSheet.app"],
plugins: [.plugin(name: "TISwiftLintPlugin")]),
// MARK: - SwiftUI
.target(name: "TISwiftUICore",
dependencies: ["TIUIKitCore", "TISwiftUtils"],
path: "TISwiftUICore/Sources"),
// MARK: - Utils
.target(name: "TISwiftUtils",
path: "TISwiftUtils/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TIFoundationUtils",
dependencies: ["TISwiftUtils", "TILogging"],
path: "TIFoundationUtils",
exclude: ["TIFoundationUtils.app"],
resources: [
.copy("PrivacyInfo.xcprivacy"),
],
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TICoreGraphicsUtils",
dependencies: [],
path: "TICoreGraphicsUtils/Sources",
exclude: ["../TICoreGraphicsUtils.app"],
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TIKeychainUtils",
dependencies: ["TIFoundationUtils", "KeychainAccess"],
path: "TIKeychainUtils/Sources",
exclude: ["../TIKeychainUtils.app"],
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"),
.target(name: "TIDeeplink", dependencies: ["TIFoundationUtils"], path: "TIDeeplink", exclude: ["TIDeeplink.app"]),
.target(name: "TIDeveloperUtils", dependencies: ["TISwiftUtils", "TIUIKitCore", "TIUIElements"], path: "TIDeveloperUtils/Sources"),
.target(name: "TILogging", path: "TILogging/Sources", plugins: ["TISwiftLintPlugin"]),
// MARK: - Networking
.target(name: "TINetworking",
dependencies: ["TIFoundationUtils", "Alamofire", "TILogging"],
path: "TINetworking/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TIMoyaNetworking",
dependencies: ["TINetworking", "Moya"],
path: "TIMoyaNetworking/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TINetworkingCache",
dependencies: ["TINetworking", "Cache"],
path: "TINetworkingCache/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
// MARK: - Maps
.target(name: "TIMapUtils",
dependencies: ["TILogging", "TICoreGraphicsUtils"],
path: "TIMapUtils/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
.target(name: "TIAppleMapUtils",
dependencies: ["TIMapUtils"],
path: "TIAppleMapUtils/Sources",
plugins: [.plugin(name: "TISwiftLintPlugin")]),
// MARK: - Elements
.target(name: "OTPSwiftView", dependencies: ["TIUIElements"], path: "OTPSwiftView/Sources"),
.target(name: "TITransitions", path: "TITransitions/Sources"),
.target(name: "TIPagination", dependencies: ["Cursors", "TISwiftUtils"], path: "TIPagination/Sources"),
.target(name: "TIAuth", dependencies: ["TIUIKitCore", "TIKeychainUtils"], path: "TIAuth/Sources"),
.target(name: "TIEcommerce", dependencies: ["TIFoundationUtils", "TISwiftUtils", "TINetworking", "TIUIKitCore", "TIUIElements"], path: "TIEcommerce/Sources"),
.target(name: "TITextProcessing",
dependencies: [.product(name: "Antlr4", package: "antlr4")],
path: "TITextProcessing/Sources",
exclude: ["../TITextProcessing.app"]),
.binaryTarget(name: "SwiftLintBinary",
url: "https://github.com/realm/SwiftLint/releases/download/0.52.2/SwiftLintBinary-macos.artifactbundle.zip",
checksum: "89651e1c87fb62faf076ef785a5b1af7f43570b2b74c6773526e0d5114e0578e"),
.plugin(name: "TISwiftLintPlugin",
capability: .buildTool(),
dependencies: ["SwiftLintBinary"]),
// MARK: - Tests
.testTarget(
name: "TITimerTests",
dependencies: ["TIFoundationUtils"],
path: "Tests/TITimerTests"),
.testTarget(
name: "TITextProcessingTests",
dependencies: ["TITextProcessing"],
path: "Tests/TITextProcessingTests"),
.testTarget(
name: "TIFoundationUtilsTests",
dependencies: ["TIFoundationUtils", "TISwiftUtils", "TILogging"],
path: "Tests/TIFoundationUtilsTests")
]
)
#endif

View File

@ -0,0 +1,57 @@
//
// Copyright (c) 2023 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 PackagePlugin
import Foundation
@main
struct SwiftLintPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
let swiftlintScriptPath = context.package.directory.appending(["build-scripts", "xcode", "build_phases", "swiftlint.sh"])
let swiftlintExecutablePath = try context.tool(named: "swiftlint").path
let srcRoot = context.package.directory.string
let targetDir = target.directory.string
let relativeTargetDir = targetDir.replacingOccurrences(of: srcRoot, with: "")
let clearRelativeTargetDir = relativeTargetDir[relativeTargetDir.index(after: relativeTargetDir.startIndex)...] // trim leading /
return [
.prebuildCommand(displayName: "SwiftLint linting \(target.name)...",
executable: swiftlintScriptPath,
arguments: [
swiftlintExecutablePath,
context.package.directory.appending(subpath: "swiftlint_base.yml")
],
environment: [
"SCRIPT_DIR": swiftlintScriptPath.removingLastComponent().string,
"SRCROOT": srcRoot,
"SCRIPT_INPUT_FILE_COUNT": "1",
"SCRIPT_INPUT_FILE_0": clearRelativeTargetDir,
// "FORCE_LINT": "1", // Lint all files in target (not only modified)
// "AUTOCORRECT": "1"
],
outputFilesDirectory: context.package.directory)
]
}
}

59
Podfile
View File

@ -1,59 +0,0 @@
abstract_target 'LeadKit' do
pod "CocoaLumberjack/Swift", '~> 3.3.0'
pod "RxSwift", '4.0.0'
pod "RxCocoa", '4.0.0'
pod "RxAlamofire", '4.0.0'
pod "ObjectMapper", '~> 3.0.0'
inhibit_all_warnings!
target 'LeadKit iOS' do
platform :ios, '9.0'
use_frameworks!
pod "TableKit", '~> 2.5.0'
pod "UIScrollView-InfiniteScroll", '~> 1.0.0'
target 'LeadKit iOSTests' do
inherit! :search_paths
# Pods for testing
end
end
target 'LeadKit iOS Extensions' do
platform :ios, '9.0'
use_frameworks!
target 'LeadKit iOS ExtensionsTests' do
inherit! :search_paths
# Pods for testing
end
end
target 'LeadKit watchOS' do
platform :watchos, '2.0'
use_frameworks!
end
target 'LeadKit tvOS' do
platform :tvos, '9.0'
use_frameworks!
target 'LeadKit tvOSTests' do
inherit! :search_paths
# Pods for testing
end
end
end
# If you have slow HDD
ENV['COCOAPODS_DISABLE_STATS'] = "true"

View File

@ -1,39 +0,0 @@
PODS:
- Alamofire (4.5.1)
- CocoaLumberjack/Default (3.3.0)
- CocoaLumberjack/Swift (3.3.0):
- CocoaLumberjack/Default
- ObjectMapper (3.0.0)
- RxAlamofire (4.0.0):
- RxAlamofire/Core (= 4.0.0)
- RxAlamofire/Core (4.0.0):
- Alamofire (~> 4.5)
- RxSwift (~> 4.0)
- RxCocoa (4.0.0):
- RxSwift (~> 4.0)
- RxSwift (4.0.0)
- TableKit (2.5.0)
- UIScrollView-InfiniteScroll (1.0.2)
DEPENDENCIES:
- CocoaLumberjack/Swift (~> 3.3.0)
- ObjectMapper (~> 3.0.0)
- RxAlamofire (= 4.0.0)
- RxCocoa (= 4.0.0)
- RxSwift (= 4.0.0)
- TableKit (~> 2.5.0)
- UIScrollView-InfiniteScroll (~> 1.0.0)
SPEC CHECKSUMS:
Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a
CocoaLumberjack: 3c8c74683302f9012bb168e1c4b7ae3c0b558431
ObjectMapper: 92230db59bf8f341a5c3a3cf0b9fbdde3cf0d87f
RxAlamofire: 6ea579ac53bf14cb4bc7049a3866e5a769989b1d
RxCocoa: d62846ca96495d862fa4c59ea7d87e5031d7340e
RxSwift: fd680d75283beb5e2559486f3c0ff852f0d35334
TableKit: 42d4dff2944f273cdeec2ef6352064eb6a9a355b
UIScrollView-InfiniteScroll: c132d6d5851daff229ab4a1060ccf70a05a051c9
PODFILE CHECKSUM: ef8520adc4869bbbf0cf4cf70ab5757b0c95be1f
COCOAPODS: 1.3.1

139
README.md
View File

@ -1,2 +1,139 @@
# LeadKit
LeadKit it's a iOS framework with a bunch of tools for rapid app development
LeadKit is the iOS framework with a bunch of tools for rapid app development.
This repository contains the following frameworks:
- [TISwiftUtils](TISwiftUtils) - a bunch of useful helpers for Swift development.
- [TIFoundationUtils](TIFoundationUtils) - set of helpers for Foundation framework classes.
- [TIUIKitCore](TIUIKitCore) - core ui elements and protocols from LeadKit.
- [TISwiftUICore](TISwiftUICore) Core UI elements: protocols, views and helpers.
- [TIUIElements](TIUIElements) - bunch of of useful protocols and views.
- [OTPSwiftView](OTPSwiftView) - a fully customizable OTP view.
- [TITableKitUtils](TITableKitUtils) - set of helpers for TableKit classes.
- [TIKeychainUtils](TIKeychainUtils) - set of helpers for Keychain classes.
- [TIPagination](TIPagination) - realisation of paginating items from a data source.
- [TINetworking](TINetworking) - Swagger-frendly networking layer helpers.
- [TIMoyaNetworking](TIMoyaNetworking) - Moya + Swagger network service.
- [TIAppleMapUtils](TIAppleMapUtils) - set of helpers for map objects clustering and interacting using Apple MapKit.
- [TIGoogleMapUtils](TIGoogleMapUtils) - set of helpers for map objects clustering and interacting using Google Maps SDK.
- [TIYandexMapUtils](TIYandexMapUtils) - set of helpers for map objects clustering and interacting using Yandex Maps SDK.
- [TIAuth](TIAuth) - login, registration, confirmation and other related actions
## Playgrounds
### Create new Playground
```sh
$ cd TIModuleName
$ touch PlaygroundPodfile
$ echo "ENV[\"DEVELOPMENT_INSTALL\"] = \"true\"
target 'TIModuleName' do
platform :ios, IOS_VERSION_NUMBER
use_frameworks!
pod 'TIDependencyModuleName', :path => '../../../../TIDependencyModuleName/TIDependencyModuleName.podspec'
pod 'TIModuleName', :path => '../../../../TIModuleName/TIModuleName.podspec'
end" > PlaygroundPodfile
$ nef playground --name TIModuleName --cocoapods --custom-podfile PlaygroundPodfile
```
See example of `PlaygroundPodfile` in `TIFoundationUtils`
### Rename/add pages to Playground
For every new feature in module create new Playground page with documentation in comments. See [nef markdown documentation](https://github.com/bow-swift/nef#-generating-a-markdown-project).
### Create symlink to nef playground
```sh
$ cd TIModuleName
$ ln -s TIModuleName.app/Contents/MacOS/TIModuleName.playground TIModuleName.playground
```
### Add nef files to TIModuleName.app/.gitignore
```
# gitignore nef files
**/build/
**/nef/
LICENSE
```
### Exclude .app bundles from package sources
#### SPM
```swift
.target(name: "TIModuleName", dependencies: ..., path: ..., exclude: ["TIModuleName.app"]),
```
#### Podspec
```ruby
sources = 'your_sources_expression'
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
s.source_files = sources
s.exclude_files = s.name + '.app'
else
s.source_files = s.name + '/' + sources
s.exclude_files = s.name + '/*.app'
end
```
## Docs:
- [TIFoundationUtils](docs/tifoundationutils)
* [AsyncOperation](docs/tifoundationutils/asyncoperation.md)
- [TICoreGraphicsUtils](docs/ticoregraphicsutils)
* [DrawingOperations](docs/ticoregraphicsutils/drawingoperations.md)
- [TIKeychainUtils](docs/tikeychainutils)
* [SingleValueStorage](docs/tikeychainutils/singlevaluestorage.md)
- [TIUIElements](docs/tiuielements)
* [Skeletons](docs/tiuielements/skeletons.md)
* [Placeholders](docs/tiuielements/placeholder.md)
- [TITextProcessing](docs/titextprocessing)
* [TITextProcessing](docs/titextprocessing/titextprocessing.md)
- [TIDeeplink](docs/tideeplink/deeplinks.md)
- [TIBottomSheet](docs/tibottomsheet/tibottomsheet.md)
- [Semantic Commit Messages](docs/semantic-commit-messages.md) - commit message codestyle.
- [Snippets](docs/snippets.md) - useful commands and scripts for development.
## Contributing
- Run following script in framework's folder:
```
./setup
```
- If legacy [Source](https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/master/Sources) folder needed, [build dependencies for LeadKit.xcodeproj](https://git.svc.touchin.ru/TouchInstinct/LeadKit/blob/master/docs/snippets.md#build-dependencies-for-LeadKit.xcodeproj).
- Make sure the commit message codestyle is followed. More about [Semantic Commit Messages](docs/semantic-commit-messages.md).
## Installation
### SPM
```swift
dependencies: [
.package(url: "https://git.svc.touchin.ru/TouchInstinct/LeadKit.git", from: "x.y.z"),
],
```
### Cocoapods
```ruby
source 'https://git.svc.touchin.ru/TouchInstinct/Podspecs.git'
pod 'TISwiftUtils', 'x.y.z'
pod 'TIFoundationUtils', 'x.y.z'
# ...
```
## Legacy
Code located in root `Sources` folder and `LeadKit.podspec` should be treated as legacy and shouldn't be used in newly created projects. Please use TI* modules via SPM or CocoaPods.

View File

@ -0,0 +1,36 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Base collection controller configurable with view model and CollectionViewWrapperView as custom view.
open class BaseCollectionContentController<ViewModel>: BaseScrollContentController<ViewModel, CollectionViewWrapperView> {
override open func createView() -> CollectionViewWrapperView {
CollectionViewWrapperView(layout: UICollectionViewFlowLayout())
}
/// Contained UICollectionView instance.
public var collectionView: UICollectionView {
customView.collectionView
}
}

View File

@ -0,0 +1,69 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UIViewController
/// Base controller that should be configured with view model.
open class BaseConfigurableController<ViewModel>: BaseOrientationController, ConfigurableController {
/// A view model instance used by this controller.
public let viewModel: ViewModel
/// Initializer with view model parameter.
///
/// - Parameter viewModel: A view model to configure this controller.
public init(viewModel: ViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - ConfigurableController
open func addViews() {
// override in subclass
}
open func configureLayout() {
// override in subclass
}
open func bindViews() {
// override in subclass
}
open func configureAppearance() {
// override in subclass
}
open func localize() {
// override in subclass
}
open func configureBarButtons() {
// override in subclass
}
}

View File

@ -0,0 +1,54 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UIView
/// Base controller configurable by view model and custom view.
open class BaseCustomViewController<ViewModel, View: UIView>: BaseConfigurableController<ViewModel> {
/// Contained custom view.
public private(set) lazy var customView = createView()
/// Initializer with view model and custom view parameters.
///
/// - Parameters:
/// - viewModel: A view model to configure this controller.
/// - customView: UIView instance to assign in view property.
public override init(viewModel: ViewModel) {
super.init(viewModel: viewModel)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override open func loadView() {
view = customView
}
/// Creates custom view.
///
/// - Returns: Initialized custom view.
open func createView() -> View {
View()
}
}

View File

@ -0,0 +1,30 @@
import Foundation
open class BaseOrientationController: UIViewController {
/// Ability to set forced screen orientation
open var forcedInterfaceOrientation: UIInterfaceOrientation?
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
switch forcedInterfaceOrientation {
case .landscapeLeft:
return .landscapeLeft
case .landscapeRight:
return .landscapeRight
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
default:
return super.supportedInterfaceOrientations
}
}
open override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
forcedInterfaceOrientation ?? super.preferredInterfaceOrientationForPresentation
}
}

View File

@ -0,0 +1,25 @@
import UIKit
open class OrientationNavigationController: UINavigationController {
// MARK: - Public properties
open var presentedOrTopViewController: UIViewController? {
presentedViewController ?? topViewController
}
open override var shouldAutorotate: Bool {
presentedOrTopViewController?.shouldAutorotate
?? super.shouldAutorotate
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
presentedOrTopViewController?.supportedInterfaceOrientations
?? super.supportedInterfaceOrientations
}
open override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
presentedOrTopViewController?.preferredInterfaceOrientationForPresentation
?? super.preferredInterfaceOrientationForPresentation
}
}

View File

@ -0,0 +1,79 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
public typealias ScrollViewHolderView = UIView & ScrollViewHolder
/// Base controller configurable with view model and ScrollViewHolder custom view.
open class BaseScrollContentController<ViewModel, View: ScrollViewHolderView>: BaseCustomViewController<ViewModel, View> {
private var bottomInsetDisposable: Disposable?
private let defaultInsetsRelay = BehaviorRelay<UIEdgeInsets>(value: .zero)
/// Bind given driver to bottom inset of scroll view. Takes into account default bottom insets.
///
/// - Parameter bottomInsetDriver: Driver that emits CGFloat bottom inset changes.
public func bindBottomInsetBinding(from bottomInsetDriver: Driver<CGFloat>) {
bottomInsetDisposable = bottomInsetDriver
.withLatestFrom(defaultInsetsRelay.asDriver()) {
$0 + $1.bottom
}
.drive(customView.scrollView.rx.bottomInsetBinder)
}
/// Unbind scroll view from previous binding.
public func unbindBottomInsetBinding() {
bottomInsetDisposable?.dispose()
}
/// Contained UIScrollView instance.
public var scrollView: UIScrollView {
customView.scrollView
}
/// Default insets used for contained scroll view.
public var defaultInsets: UIEdgeInsets {
get {
defaultInsetsRelay.value
}
set {
defaultInsetsRelay.accept(newValue)
customView.scrollView.contentInset = newValue
customView.scrollView.scrollIndicatorInsets = newValue
}
}
}
public extension BaseScrollContentController {
/// On iOS, tvOS 11+ sets contentInsetAdjustmentBehavior to .never.
/// On earlier versions sets automaticallyAdjustsScrollViewInsets to false.
func disableAdjustsScrollViewInsets() {
if #available(iOS 11.0, tvOS 11.0, *) {
customView.scrollView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
}
}

View File

@ -0,0 +1,52 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import TableKit
/// Base table controller configurable with view model and TableViewWrapperView as custom view.
open class BaseTableContentController<ViewModel>: BaseScrollContentController<ViewModel, TableViewWrapperView> {
/// TableDirector binded to table view.
public private(set) lazy var tableDirector = createTableDirector()
/// Creates tableDirector for table view.
///
/// - Returns: Initialized TableDirector.
open func createTableDirector() -> TableDirector {
TableDirector(tableView: tableView)
}
override open func createView() -> TableViewWrapperView {
TableViewWrapperView(tableViewStyle: .plain)
}
override open func configureAppearance() {
super.configureAppearance()
tableView.separatorStyle = .none
}
/// Contained UITableView instance.
public var tableView: UITableView {
customView.tableView
}
}

View File

@ -23,7 +23,9 @@
import RxSwift
/// Paging cursor implementation with enclosed cursor for fetching results
public class FixedPageCursor<Cursor: CursorType>: CursorType {
public class FixedPageCursor<Cursor: CursorType>: CursorType, RxDataSource {
public typealias ResultType = [Element]
fileprivate let cursor: Cursor
@ -40,17 +42,17 @@ public class FixedPageCursor<Cursor: CursorType>: CursorType {
}
public var exhausted: Bool {
return cursor.exhausted && cursor.count == count
cursor.exhausted && cursor.count == count
}
public private(set) var count: Int = 0
public subscript(index: Int) -> Cursor.Element {
return cursor[index]
cursor[index]
}
public func loadNextBatch() -> Single<[Cursor.Element]> {
return Single.deferred {
Single.deferred {
if self.exhausted {
return .error(CursorError.exhausted)
}
@ -67,15 +69,16 @@ public class FixedPageCursor<Cursor: CursorType>: CursorType {
return self.cursor.loadNextBatch()
.flatMap { _ in
self.loadNextBatch()
}
}
}
}
}
/// FixedPageCursor subclass with implementation of ResettableType
public class ResettableFixedPageCursor<Cursor: ResettableCursorType>: FixedPageCursor<Cursor>, ResettableType {
public typealias ResultType = [Element]
public override init(cursor: Cursor, pageSize: Int) {
super.init(cursor: cursor, pageSize: pageSize)
}
@ -83,5 +86,4 @@ public class ResettableFixedPageCursor<Cursor: ResettableCursorType>: FixedPageC
public required init(resetFrom other: ResettableFixedPageCursor) {
super.init(cursor: other.cursor.reset(), pageSize: other.pageSize)
}
}

View File

@ -29,7 +29,7 @@ public extension CursorType {
/// - Parameter transform: closure to transform elements
/// - Returns: new MapCursor instance
func flatMap<T>(transform: @escaping MapCursor<Self, T>.Transform) -> MapCursor<Self, T> {
return MapCursor(cursor: self, transform: transform)
MapCursor(cursor: self, transform: transform)
}
/// Creates ResettableMapCursor with current cursor
@ -39,13 +39,14 @@ public extension CursorType {
func flatMap<T>(transform: @escaping ResettableMapCursor<Self, T>.Transform)
-> ResettableMapCursor<Self, T> where Self: ResettableCursorType {
return ResettableMapCursor(cursor: self, transform: transform)
ResettableMapCursor(cursor: self, transform: transform)
}
}
/// Map cursor implementation with enclosed cursor for fetching results
public class MapCursor<Cursor: CursorType, T>: CursorType {
public class MapCursor<Cursor: CursorType, T>: CursorType, RxDataSource {
public typealias ResultType = [Element]
public typealias Transform = (Cursor.Element) -> T?
@ -66,31 +67,32 @@ public class MapCursor<Cursor: CursorType, T>: CursorType {
}
public var exhausted: Bool {
return cursor.exhausted
cursor.exhausted
}
public var count: Int {
return elements.count
elements.count
}
public subscript(index: Int) -> T {
return elements[index]
elements[index]
}
public func loadNextBatch() -> Single<[T]> {
return cursor.loadNextBatch().map { newItems in
let transformedNewItems = newItems.flatMap(self.transform)
cursor.loadNextBatch().map { newItems in
let transformedNewItems = newItems.compactMap(self.transform)
self.elements += transformedNewItems
return transformedNewItems
}
}
}
/// MapCursor subclass with implementation of ResettableType
public class ResettableMapCursor<Cursor: ResettableCursorType, T>: MapCursor<Cursor, T>, ResettableType {
public typealias ResultType = [Cursor.Element]
public override init(cursor: Cursor, transform: @escaping Transform) {
super.init(cursor: cursor, transform: transform)
}
@ -98,5 +100,4 @@ public class ResettableMapCursor<Cursor: ResettableCursorType, T>: MapCursor<Cur
public required init(resetFrom other: ResettableMapCursor) {
super.init(cursor: other.cursor.reset(), transform: other.transform)
}
}

View File

@ -22,7 +22,31 @@
import RxSwift
/// Single load cursor configuration for single load operation
public final class SingleLoadCursorConfiguration<Element>: TotalCountCursorConfiguration {
public typealias ResultType = [Element]
private let loadingSingle: Single<ResultType>
/// Initializer for Single with array result type.
///
/// - Parameter loadingSingle: Single that will emit array of result type.
public init(loadingSingle: Single<ResultType>) {
self.loadingSingle = loadingSingle
}
public func resultSingle() -> Single<ResultType> {
loadingSingle
}
public init(resetFrom other: SingleLoadCursorConfiguration) {
self.loadingSingle = other.loadingSingle
}
}
/// Cursor implementation for single load operation
@available(*, deprecated, message: "Use SingleLoadCursorConfiguration with TotalCountCursor.")
public class SingleLoadCursor<Element>: ResettableCursorType {
private let loadingObservable: Single<[Element]>
@ -43,20 +67,20 @@ public class SingleLoadCursor<Element>: ResettableCursorType {
public private(set) var exhausted = false
public var count: Int {
return content.count
content.count
}
public subscript(index: Int) -> Element {
return content[index]
content[index]
}
public func loadNextBatch() -> Single<[Element]> {
return Single.deferred {
Single.deferred {
if self.exhausted {
return .error(CursorError.exhausted)
}
return self.loadingObservable.do(onNext: { [weak self] newItems in
return self.loadingObservable.do(onSuccess: { [weak self] newItems in
self?.onGot(result: newItems)
})
}
@ -66,5 +90,4 @@ public class SingleLoadCursor<Element>: ResettableCursorType {
content = result
exhausted = true
}
}

View File

@ -23,7 +23,9 @@
import RxSwift
/// Stub cursor implementation for array content type
public class StaticCursor<Element>: ResettableCursorType {
public class StaticCursor<Element>: ResettableRxDataSourceCursor {
public typealias ResultType = [Element]
private let content: [Element]
@ -43,11 +45,11 @@ public class StaticCursor<Element>: ResettableCursorType {
public private(set) var count = 0
public subscript(index: Int) -> Element {
return content[index]
content[index]
}
public func loadNextBatch() -> Single<[Element]> {
return Single.deferred {
Single.deferred {
if self.exhausted {
return .error(CursorError.exhausted)
}
@ -59,5 +61,4 @@ public class StaticCursor<Element>: ResettableCursorType {
return .just(self.content)
}
}
}

View File

@ -0,0 +1,71 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
open class TotalCountCursor<CursorConfiguration: TotalCountCursorConfiguration>: ResettableRxDataSourceCursor {
public typealias Element = CursorConfiguration.ResultType.ElementType
public typealias ResultType = [Element]
private let configuration: CursorConfiguration
private var elements: [Element] = []
public private(set) var totalCount: Int = .max
public var exhausted: Bool {
count >= totalCount
}
public var count: Int {
elements.count
}
public subscript(index: Int) -> Element {
elements[index]
}
public init(configuration: CursorConfiguration) {
self.configuration = configuration
}
public required init(resetFrom other: TotalCountCursor) {
configuration = other.configuration.reset()
}
open func processResultFromConfigurationSingle() -> Single<CursorConfiguration.ResultType> {
configuration.resultSingle()
}
public func loadNextBatch() -> Single<[Element]> {
processResultFromConfigurationSingle()
.do(onSuccess: { [weak self] listingResult in
self?.totalCount = listingResult.totalCount
self?.elements = (self?.elements ?? []) + listingResult.results
})
.map {
$0.results
}
}
}

View File

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

View File

@ -0,0 +1,131 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
/// ViewModel that loads data from given data source with loading state tracking.
open class GeneralDataLoadingViewModel<ResultType>: BaseViewModel, GeneralDataLoadingHandler, DisposeBagHolder {
public typealias LoadingModel = GeneralDataLoadingModel<ResultType>
public typealias DataSourceType = Single<ResultType>
public typealias LoadingState = GeneralDataLoadingState<DataSourceType>
private let loadingModel: LoadingModel
private let loadingStateRelay = BehaviorRelay<LoadingState>(value: .initial)
// MARK: - DisposeBagHolder
public let disposeBag = DisposeBag()
/// Initializer with single result sequence and empty result checker closure.
///
/// - Parameters:
/// - dataSource: A single element sequence.
/// - customErrorHandler: Custom error handler for state update. Pass nil for default error handling.
/// - emptyResultChecker: Closure for checking result on empty state.
public init(dataSource: DataSourceType,
customErrorHandler: LoadingModel.ErrorHandler? = nil,
emptyResultChecker: @escaping LoadingModel.EmptyResultChecker = { _ in false }) {
loadingModel = LoadingModel(dataSource: dataSource,
customErrorHandler: customErrorHandler,
emptyResultChecker: emptyResultChecker)
loadingModel.stateDriver
.drive(loadingStateRelay)
.disposed(by: disposeBag)
bindLoadingState(from: loadingStateDriver)
loadingModel.reload()
}
/// Returns observable that emits current loading state.
open var loadingStateObservable: Observable<LoadingState> {
loadingStateRelay.asObservable()
}
/// Returns driver that emits current loading state.
open var loadingStateDriver: Driver<LoadingState> {
loadingStateRelay.asDriver()
}
/// By default returns true if loading state == .result.
open var hasContent: Bool {
currentLoadingState.hasResult
}
/// Returns current result if it exists.
public var currentResult: ResultType? {
currentLoadingState.result
}
/// Current state of loading process.
private(set) public var currentLoadingState: LoadingState {
get {
loadingStateRelay.value
}
set {
loadingStateRelay.accept(newValue)
}
}
/// Manually update loading state.
/// Should be used only in specific situations on your own risk!
///
/// - Parameter newState: New loading state.
public func updateStateManually(to newState: LoadingState) {
currentLoadingState = newState
}
/// Replaces current data source of loading model with new one.
///
/// - Parameter dataSource: A single element sequence.
public func replaceDataSource(with newDataSource: DataSourceType) {
loadingModel.replaceDataSource(with: newDataSource)
}
/// Reload data.
public func reload() {
loadingModel.reload()
}
// MARK: - GeneralDataLoadingHandler
open func onLoadingState() {
// override in subclass
}
open func onResultsState(result: ResultType) {
// override in subclass
}
open func onEmptyState() {
// override in subclass
}
open func onErrorState(error: Error) {
// override in subclass
}
}

View File

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

View File

@ -0,0 +1,361 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
import UIScrollView_InfiniteScroll
/// Class that connects PaginationDataLoadingModel with UIScrollView. It handles all non-visual and visual states.
final public class PaginationWrapper<Cursor: ResettableRxDataSourceCursor, Delegate: PaginationWrapperDelegate>
where Cursor == Delegate.DataSourceType, Cursor.ResultType == [Cursor.Element] {
private typealias DataLoadingModel = PaginationDataLoadingModel<Cursor>
private typealias LoadingState = DataLoadingModel.NetworkOperationStateType
private typealias FinishInfiniteScrollCompletion = ((UIScrollView) -> Void)
private var wrappedView: AnyPaginationWrappable
private let paginationViewModel: DataLoadingModel
private weak var delegate: Delegate?
private weak var uiDelegate: PaginationWrapperUIDelegate?
/// Sets the offset between the real end of the scroll view content and the scroll position,
/// so the handler can be triggered before reaching end. Defaults to 0.0;
public var infiniteScrollTriggerOffset: CGFloat {
get {
wrappedView.scrollView.infiniteScrollTriggerOffset
}
set {
wrappedView.scrollView.infiniteScrollTriggerOffset = newValue
}
}
public var pullToRefreshEnabled: Bool = true {
didSet {
if pullToRefreshEnabled {
createRefreshControl()
} else {
removeRefreshControl()
}
}
}
private var bottom: CGFloat {
wrappedView.scrollView.contentSize.height - wrappedView.scrollView.frame.size.height
}
private let disposeBag = DisposeBag()
private var currentPlaceholderView: UIView?
private var currentPlaceholderViewTopConstraint: NSLayoutConstraint?
/// Initializer with table view, placeholders container view, cusor and delegate parameters.
///
/// - Parameters:
/// - wrappedView: UIScrollView instance to work with.
/// - cursor: Cursor object that acts as data source.
/// - delegate: Delegate object for data loading events handling.
/// - uiDelegate: Delegate object for UI customization.
public init(wrappedView: AnyPaginationWrappable,
cursor: Cursor,
delegate: Delegate,
uiDelegate: PaginationWrapperUIDelegate? = nil) {
self.wrappedView = wrappedView
self.delegate = delegate
self.uiDelegate = uiDelegate
self.paginationViewModel = PaginationDataLoadingModel(dataSource: cursor) { $0.isEmpty }
bindViewModelStates()
createRefreshControl()
}
/// Method that reload all data in internal view model.
public func reload() {
paginationViewModel.reload()
}
/// Method acts like reload, but shows initial loading view after being invoked.
public func retry() {
paginationViewModel.retry()
}
/// Method that enables placeholders animation due pull-to-refresh interaction.
///
/// - Parameter scrollObservable: Observable that emits content offset as CGPoint.
public func setScrollObservable(_ scrollObservable: Observable<CGPoint>) {
scrollObservable
.asDriver(onErrorJustReturn: .zero)
.drive(scrollOffsetChanged)
.disposed(by: disposeBag)
}
// MARK: - States handling
private func onInitialState() {
//
}
private func onLoadingState(afterState: LoadingState) {
if case .initial = afterState {
wrappedView.scrollView.isUserInteractionEnabled = false
removeAllPlaceholderView()
guard let loadingIndicator = uiDelegate?.initialLoadingIndicator() else {
return
}
let loadingIndicatorView = loadingIndicator.view
loadingIndicatorView.translatesAutoresizingMaskIntoConstraints = true
wrappedView.backgroundView = loadingIndicatorView
loadingIndicator.startAnimating()
currentPlaceholderView = loadingIndicatorView
} else {
removeInfiniteScroll()
wrappedView.footerView = nil
}
}
private func onLoadingMoreState(afterState: LoadingState) {
if case .error = afterState { // user tap retry button in table footer
uiDelegate?.footerRetryViewWillDisappear()
wrappedView.footerView = nil
addInfiniteScroll(withHandler: false)
wrappedView.scrollView.beginInfiniteScroll(true)
}
}
private func onResultsState(newItems: DataLoadingModel.ResultType,
from cursor: Cursor,
afterState: LoadingState) {
wrappedView.scrollView.isUserInteractionEnabled = true
if case .initialLoading = afterState {
delegate?.paginationWrapper(didReload: newItems, using: cursor)
removeAllPlaceholderView()
wrappedView.scrollView.refreshControl?.endRefreshing()
addInfiniteScroll(withHandler: true)
} else if case .loadingMore = afterState {
delegate?.paginationWrapper(didLoad: newItems, using: cursor)
removeAllPlaceholderView()
addInfiniteScrollWithHandler()
}
}
private func onErrorState(error: Error, afterState: LoadingState) {
if case .initialLoading = afterState {
defer {
wrappedView.scrollView.refreshControl?.endRefreshing()
}
delegate?.clearData()
let customErrorHandling = uiDelegate?.customInitialLoadingErrorHandling(for: error) ?? false
guard !customErrorHandling, let errorView = uiDelegate?.errorPlaceholder(for: error) else {
return
}
replacePlaceholderViewIfNeeded(with: errorView)
} else {
guard let retryView = uiDelegate?.footerRetryView(),
let retryViewHeight = uiDelegate?.footerRetryViewHeight() else {
removeInfiniteScroll()
return
}
retryView.frame = CGRect(x: 0, y: 0, width: wrappedView.scrollView.bounds.width, height: retryViewHeight)
retryView.button.addTarget(self, action: #selector(retryEvent), for: .touchUpInside)
uiDelegate?.footerRetryViewWillAppear()
removeInfiniteScroll { scrollView in
self.wrappedView.footerView = retryView
let shouldUpdateContentOffset = Int(scrollView.contentOffset.y + retryViewHeight) >= Int(self.bottom)
if shouldUpdateContentOffset {
let newContentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y + retryViewHeight)
scrollView.setContentOffset(newContentOffset, animated: true)
if #available(iOS 13, *) {
scrollView.setContentOffset(newContentOffset, animated: true)
}
}
}
}
}
@objc private func retryEvent() {
paginationViewModel.loadMore()
}
private func onEmptyState() {
defer {
wrappedView.scrollView.refreshControl?.endRefreshing()
}
delegate?.clearData()
guard let emptyView = uiDelegate?.emptyPlaceholder() else {
return
}
replacePlaceholderViewIfNeeded(with: emptyView)
}
private func replacePlaceholderViewIfNeeded(with placeholderView: UIView) {
wrappedView.scrollView.isUserInteractionEnabled = true
removeAllPlaceholderView()
placeholderView.translatesAutoresizingMaskIntoConstraints = false
placeholderView.isHidden = false
// I was unable to add pull-to-refresh placeholder scroll behaviour without this trick
let placeholderWrapperView = UIView()
placeholderWrapperView.addSubview(placeholderView)
let leadingConstraint = placeholderView.leadingAnchor.constraint(equalTo: placeholderWrapperView.leadingAnchor)
let trailingConstraint = placeholderView.trailingAnchor.constraint(equalTo: placeholderWrapperView.trailingAnchor)
let topConstraint = placeholderView.topAnchor.constraint(equalTo: placeholderWrapperView.topAnchor)
let bottomConstraint = placeholderView.bottomAnchor.constraint(equalTo: placeholderWrapperView.bottomAnchor)
NSLayoutConstraint.activate([
leadingConstraint,
trailingConstraint,
topConstraint,
bottomConstraint
])
currentPlaceholderViewTopConstraint = topConstraint
wrappedView.backgroundView = placeholderWrapperView
currentPlaceholderView = placeholderView
}
// MARK: - private stuff
private func onExhaustedState() {
removeInfiniteScroll()
removeAllPlaceholderView()
}
private func addInfiniteScrollWithHandler() {
removeInfiniteScroll()
addInfiniteScroll(withHandler: true)
}
private func addInfiniteScroll(withHandler: Bool) {
if withHandler {
wrappedView.scrollView.addInfiniteScroll { [weak paginationViewModel] _ in
paginationViewModel?.loadMore()
}
} else {
wrappedView.scrollView.addInfiniteScroll { _ in }
}
wrappedView.scrollView.infiniteScrollIndicatorView = uiDelegate?.loadingMoreIndicator()?.view
}
private func removeInfiniteScroll(with completion: FinishInfiniteScrollCompletion? = nil) {
wrappedView.scrollView.finishInfiniteScroll(completion: completion)
wrappedView.scrollView.removeInfiniteScroll()
}
private func createRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(refreshAction), for: .valueChanged)
wrappedView.scrollView.refreshControl = refreshControl
}
@objc private func refreshAction() {
// it is implemented the combined behavior of `touchUpInside` and `touchUpOutside` using `CFRunLoopPerformBlock`,
// which `UIRefreshControl` does not support
CFRunLoopPerformBlock(CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) { [weak self] in
self?.reload()
}
}
private func removeRefreshControl() {
wrappedView.scrollView.refreshControl = nil
}
private func bindViewModelStates() {
paginationViewModel.stateDriver
.drive(stateChanged)
.disposed(by: disposeBag)
}
private func removeAllPlaceholderView() {
wrappedView.backgroundView = nil
wrappedView.footerView = nil
}
}
private extension PaginationWrapper {
private var stateChanged: Binder<LoadingState> {
Binder(self) { base, value in
switch value {
case .initial:
base.onInitialState()
case let .initialLoading(after):
base.onLoadingState(afterState: after)
case let .loadingMore(after):
base.onLoadingMoreState(afterState: after)
case let .results(newItems, from, after):
base.onResultsState(newItems: newItems, from: from, afterState: after)
case let .error(error, after):
base.onErrorState(error: error, afterState: after)
case .empty:
base.onEmptyState()
case .exhausted:
base.onExhaustedState()
}
}
}
var scrollOffsetChanged: Binder<CGPoint> {
Binder(self) { base, value in
base.currentPlaceholderViewTopConstraint?.constant = -value.y
}
}
}

View File

@ -0,0 +1,65 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
open class RxDataLoadingModel<LoadingStateType: DataLoadingState>: RxNetworkOperationModel<LoadingStateType>
where LoadingStateType.DataSourceType: RxDataSource {
public typealias EmptyResultChecker = (ResultType) -> Bool
let emptyResultChecker: EmptyResultChecker
/// Model initializer with data source, empty result checker and custom error handler.
///
/// - Parameters:
/// - dataSource: Data source for data loading.
/// - customErrorHandler: Custom error handler for state update. Pass nil for default error handling.
/// - emptyResultChecker: Empty result checker closure.
public init(dataSource: DataSourceType,
customErrorHandler: ErrorHandler? = nil,
emptyResultChecker: @escaping EmptyResultChecker) {
self.emptyResultChecker = emptyResultChecker
super.init(dataSource: dataSource, customErrorHandler: customErrorHandler)
}
open func reload() {
execute()
}
override func onGot(result: ResultType, from dataSource: DataSourceType) {
if emptyResultChecker(result) {
state = .emptyState
} else {
super.onGot(result: result, from: dataSource)
}
updateStateAfterResult(from: dataSource)
}
func updateStateAfterResult(from dataSource: DataSourceType) {
// override in subcass if needed
}
}

View File

@ -0,0 +1,100 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
open class RxNetworkOperationModel<LoadingStateType: NetworkOperationState>: NetworkOperationModel
where LoadingStateType.DataSourceType: RxDataSource {
public typealias DataSourceType = LoadingStateType.DataSourceType
public typealias ResultType = DataSourceType.ResultType
public typealias ErrorHandler = (Error, LoadingStateType) -> LoadingStateType
private let stateRelay = BehaviorRelay<LoadingStateType>(value: .initialState)
var currentRequestDisposable: Disposable?
private(set) var dataSource: DataSourceType
private let errorHandler: ErrorHandler
open var stateDriver: Driver<LoadingStateType> {
stateRelay.asDriver()
}
/// Model initializer with data source and custom error handler.
///
/// - Parameters:
/// - dataSource: Data source for network operation.
/// - customErrorHandler: Custom error handler for state update. Pass nil for default error handling.
public init(dataSource: DataSourceType, customErrorHandler: ErrorHandler? = nil) {
self.errorHandler = customErrorHandler ?? { .errorState(error: $0, after: $1) }
self.dataSource = dataSource
}
/// Performs request to given data source
public func execute() {
currentRequestDisposable?.dispose()
state = .initialLoadingState(after: state)
requestResult(from: dataSource)
}
/// Replaces current data source with new one.
///
/// - Parameter newDataSource: A new data source to use.
public func replaceDataSource(with newDataSource: DataSourceType) {
dataSource = newDataSource
}
func onGot(error: Error) {
state = errorHandler(error, state)
}
func onGot(result: ResultType, from dataSource: DataSourceType) {
state = .resultState(result: result,
from: dataSource,
after: state)
}
func requestResult(from dataSource: DataSourceType) {
currentRequestDisposable = dataSource
.resultSingle()
.observe(on: MainScheduler.instance)
.subscribe(onSuccess: { [weak self] result in
self?.onGot(result: result, from: dataSource)
}, onFailure: { [weak self] error in
self?.onGot(error: error)
})
}
var state: LoadingStateType {
get {
stateRelay.value
}
set {
stateRelay.accept(newValue)
}
}
}

View File

@ -1,59 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
open class App {
fileprivate static let stringVendorIdentifierKey = "stringIdentifierForVendor"
/// The value of CFBundleName
open static let bundleName = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
/// The value of CFBundleShortVersionString
open static let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
/// The value of CFBundleVersion
open static let bundleVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
/**
Return app's version
- returns: shortBundleVersion.bundleVersion
*/
open static var version: String {
return App.shortVersion + "." + App.bundleVersion
}
/**
Return device identifier
- returns: UUIDString
*/
open static var deviceUniqueIdentifier: String {
if let vendorIdentifier = UserDefaults.standard.string(forKey: App.stringVendorIdentifierKey) {
return vendorIdentifier
}
let vendorIdentifier = UUID().uuidString
UserDefaults.standard.set(vendorIdentifier, forKey: App.stringVendorIdentifierKey)
UserDefaults.standard.synchronize()
return vendorIdentifier
}
}

View File

@ -1,56 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
import CocoaLumberjack
open class Log {
public init() {
DDLog.add(DDFileLogger())
DDLog.add(DDASLLogger.sharedInstance)
DDLog.add(DDTTYLogger.sharedInstance)
let logFormatter = LogFormatter()
DDASLLogger.sharedInstance.logFormatter = logFormatter
DDTTYLogger.sharedInstance.logFormatter = logFormatter
let assertionHandler = NSAssertionHandler()
Thread.current.threadDictionary.setValue(assertionHandler, forKey: NSAssertionHandlerKey)
}
/**
Add start message for your application
- returns: Return value looks like "AppName 1.0.1 session started on version 9.2 (build 13c75)"
*/
open static var startMessage: String {
let startMessage = App.bundleName + " " + App.shortVersion + "."
+ App.bundleVersion + " session started on "
+ ProcessInfo.processInfo.operatingSystemVersionString.lowercased()
return startMessage
}
}

View File

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

View File

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

View File

@ -0,0 +1,202 @@
//
// 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 TableKit
import RxSwift
import UIKit
public typealias SearchResultsController = UIViewController & SearchResultsViewController
/// Class that allows to enter text for search and then displays search results in table view
open class BaseSearchViewController < Item,
ItemViewModel,
ViewModel,
CustomView: UIView & TableViewHolder>: BaseCustomViewController<ViewModel, CustomView>
where ViewModel: BaseSearchViewModel<Item, ItemViewModel> {
// MARK: - Properties
private let disposeBag = DisposeBag()
private let searchResultsController: SearchResultsController
private let searchController: UISearchController
private var didEnterText = false
// MARK: - Initialization
public init(viewModel: ViewModel, searchResultsController: SearchResultsController) {
self.searchResultsController = searchResultsController
self.searchController = UISearchController(searchResultsController: searchResultsController)
super.init(viewModel: viewModel)
initialLoadView()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Configurable Controller
open override func bindViews() {
super.bindViews()
viewModel.itemsViewModelsDriver
.drive(with: self) { owner, viewModels in
owner.handle(itemViewModels: viewModels)
}
.disposed(by: disposeBag)
Observable.merge(searchResults, resetResults)
.subscribe(with: self) { owner, state in
owner.handle(searchResultsState: state)
}
.disposed(by: disposeBag)
let searchText = searchController.searchBar.rx.text
.changed
.do(onNext: { [weak self] text in
self?.handle(searchText: text)
})
.map { $0 ?? "" }
viewModel.bind(searchText: searchText)
.disposed(by: disposeBag)
}
open override func addViews() {
super.addViews()
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
} else {
customView.tableView.tableHeaderView = searchController.searchBar
}
searchController.view.addSubview(statusBarView)
}
open override func configureAppearance() {
super.configureAppearance()
definesPresentationContext = true
configureSearchBarAppearance(searchController.searchBar)
customView.tableView.tableHeaderView?.backgroundColor = searchBarColor
}
open override func localize() {
super.localize()
searchController.searchBar.placeholder = searchBarPlaceholder
}
// MARK: - Search Controller Functionality
open func createRows(from itemsViewModels: [ItemViewModel]) -> [Row] {
assertionFailure("createRows(from:) has not been implemented")
return []
}
open var searchBarPlaceholder: String {
""
}
open var searchBarColor: UIColor {
.gray
}
open var statusBarView: UIView {
let statusBarSize = statusBarFrame().size
let statusBarView = UIView(frame: CGRect(x: 0,
y: 0,
width: statusBarSize.width,
height: statusBarSize.height))
statusBarView.backgroundColor = statusBarColor
return statusBarView
}
open var statusBarColor: UIColor {
.black
}
open func updateContent(with viewModels: [ItemViewModel]) {
// override in subclass
}
open func stateForUpdate(with viewModels: [ItemViewModel]) -> SearchResultsViewControllerState {
let rows = createRows(from: viewModels)
return .rowsContent(rows: rows)
}
open var resetResults: Observable<SearchResultsViewControllerState> {
searchController.rx.willPresent
.map { SearchResultsViewControllerState.initial }
}
open var searchResults: Observable<SearchResultsViewControllerState> {
viewModel.searchResultsDriver
.asObservable()
.compactMap { [weak self] viewModels -> SearchResultsViewControllerState? in
self?.stateForUpdate(with: viewModels)
}
}
// MARK: - Helpers
open func handle(itemViewModels viewModels: [ItemViewModel]) {
updateContent(with: viewModels)
}
open func handle(searchResultsState state: SearchResultsViewControllerState) {
searchResultsController.update(for: state)
}
open func handle(searchText: String?) {
setTableViewInsets()
}
private func setTableViewInsets() {
guard !didEnterText else {
return
}
didEnterText = true
searchResultsController.searchResultsView.tableView.contentInset = tableViewInsets
searchResultsController.searchResultsView.tableView.scrollIndicatorInsets = tableViewInsets
}
open func statusBarFrame() -> CGRect {
/// override in subclass
return .zero
}
open func configureSearchBarAppearance(_ searchBar: UISearchBar) {
// override in subclass
}
}
extension BaseSearchViewController {
open var tableViewInsets: UIEdgeInsets {
let searchBarHeight = searchController.searchBar.frame.height
let statusBarHeight = statusBarFrame().height
return UIEdgeInsets(top: searchBarHeight + statusBarHeight,
left: 0,
bottom: 0,
right: 0)
}
}

View File

@ -0,0 +1,100 @@
//
// 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
import RxCocoa
/// ViewModel that loads data from a given data source and performs search among results
open class BaseSearchViewModel<Item, ItemViewModel>: GeneralDataLoadingViewModel<[Item]> {
public typealias ItemsList = [Item]
private let searchTextRelay = BehaviorRelay(value: "")
public init(dataSource: Single<ItemsList>) {
super.init(dataSource: dataSource, emptyResultChecker: { $0.isEmpty })
}
open var itemsViewModelsDriver: Driver<[ItemViewModel]> {
loadingResultObservable
.compactMap { [weak self] items in
self?.viewModels(from: items)
}
.flatMap { Observable.from(optional: $0) }
.share(replay: 1, scope: .forever)
.asDriver(onErrorDriveWith: .empty())
}
open var searchDebounceInterval: RxTimeInterval {
.seconds(1)
}
open var searchResultsDriver: Driver<[ItemViewModel]> {
searchTextRelay.debounce(searchDebounceInterval, scheduler: MainScheduler.instance)
.withLatestFrom(loadingResultObservable) { ($0, $1) }
.flatMapLatest { [weak self] searchText, items -> Observable<ItemsList> in
self?.search(by: searchText, from: items).asObservable() ?? .empty()
}
.compactMap { [weak self] items in
self?.viewModels(from: items)
}
.flatMap { Observable.from(optional: $0) }
.share(replay: 1, scope: .forever)
.asDriver(onErrorDriveWith: .empty())
}
open func viewModel(from item: Item) -> ItemViewModel {
fatalError("viewModel(from:) has not been implemented")
}
open func search(by searchString: String, from items: ItemsList) -> Single<ItemsList> {
fatalError("searchEngine(for:) has not been implemented")
}
open func bind(searchText: Observable<String>) -> Disposable {
searchText.bind(to: searchTextRelay)
}
private func viewModels(from items: ItemsList) -> [ItemViewModel] {
items.map { self.viewModel(from: $0) }
}
open var loadingResultObservable: Observable<ResultType> {
loadingStateDriver
.asObservable()
.map { $0.result }
.flatMap { Observable.from(optional: $0) }
}
open var loadingErrorObservable: Observable<Error> {
loadingStateDriver
.asObservable()
.map { $0.error }
.flatMap { Observable.from(optional: $0) }
}
open var firstLoadingResultObservable: Single<ResultType> {
loadingResultObservable
.take(1)
.asSingle()
}
}

View File

@ -1,72 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
/// Date formatting service. Used for date to string and string to date formatting.
/// Takes into account locale and timezone.
open class DateFormattingService {
private var dateFormatters: [DateFormattingArguments: DateFormatter] = [:]
public init(formattingArguments: [DateFormattingArguments] = []) {
for argument in formattingArguments {
register(arguments: argument)
}
}
/// DateFormatter registration method.
///
/// - Parameter arguments: A formatting arguments structure.
public func register(arguments: DateFormattingArguments) {
dateFormatters[arguments] = arguments.dateFormatter
}
/// String to date convertion method.
///
/// - Parameters:
/// - dateString: The string to parse.
/// - arguments: A formatting arguments structure.
/// - Returns: A date representation of a given string interpreted using given arguments.
public func date(from dateString: String, arguments: DateFormattingArguments) -> Date? {
guard let dateFormatter = dateFormatters[arguments] else {
fatalError("No date formatter registered for given arguments: \(arguments)")
}
return dateFormatter.date(from: dateString)
}
/// Date to string convertion method.
///
/// - Parameters:
/// - date: The date to format.
/// - arguments: A formatting arguments structure.
/// - Returns: A string representation of a given date formatted using given arguments.
public func string(from date: Date, arguments: DateFormattingArguments) -> String? {
guard let dateFormatter = dateFormatters[arguments] else {
fatalError("No date formatter registered for given arguments: \(arguments)")
}
return dateFormatter.string(from: date)
}
}

View File

@ -23,72 +23,118 @@
import RxSwift
import RxCocoa
import Alamofire
import ObjectMapper
import RxAlamofire
/// Base network service implementation build on top of LeadKit extensions for Alamofire.
/// Has an ability to automatically show / hide network activity indicator
open class NetworkService {
/// Enable synchronization for setting variable from different thread
/// Enable synchronization for setting behaviour relay from different thread
private let lock = NSRecursiveLock()
private let requestCountVariable = Variable<Int>(0)
private let requestCountRelay = BehaviorRelay(value: 0)
public let sessionManager: Alamofire.SessionManager
public let configuration: NetworkServiceConfiguration
public let sessionManager: SessionManager
private let acceptableStatusCodes: [Int]
var requestCount: Driver<Int> {
return requestCountVariable.asDriver()
/// Driver that emits true when active requests count != 0 and false otherwise.
public var isActivityIndicatorVisibleDriver: Driver<Bool> {
requestCountRelay.asDriver().map { $0 != 0 }.distinctUntilChanged()
}
/// - Parameter sessionManager: Alamofire.SessionManager to use for requests
/// Creates new instance of NetworkService with given Alamofire session manager
///
/// - Parameter sessionManager: Alamofire.SessionManager to use for requests
public init(sessionManager: Alamofire.SessionManager,
acceptableStatusCodes: [Int] = Alamofire.SessionManager.defaultAcceptableStatusCodes) {
/// - Parameters:
/// - configuration: instance of NetworkServiceConfiguration to configure network service.
public init(configuration: NetworkServiceConfiguration) {
self.sessionManager = sessionManager
self.acceptableStatusCodes = acceptableStatusCodes
self.configuration = configuration
self.sessionManager = configuration.sessionManager
}
/// Perform reactive request to get mapped ObservableMappable model 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
/// - decoder: json decoder to decode response data
/// - Returns: Observable of tuple containing (HTTPURLResponse, ObservableMappable)
public func rxRequest<T: ObservableMappable>(with parameters: ApiRequestParameters)
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
public func rxObservableRequest<T: ObservableMappable>(with parameters: ApiRequestParameters,
additionalValidStatusCodes: Set<Int> = [],
decoder: JSONDecoder = JSONDecoder())
-> Observable<SessionManager.ModelResponse<T>> {
return sessionManager.rx.responseObservableModel(requestParameters: parameters,
acceptableStatusCodes: acceptableStatusCodes)
.counterTracking(for: self)
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
/// - 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<T: ImmutableMappable>(with parameters: ApiRequestParameters)
-> Observable<(response: HTTPURLResponse, model: T)> {
public func rxRequest<T: Decodable>(with parameters: ApiRequestParameters,
additionalValidStatusCodes: Set<Int> = [],
decoder: JSONDecoder = JSONDecoder())
-> Observable<SessionManager.ModelResponse<T>> {
return sessionManager.rx.responseModel(requestParameters: parameters,
acceptableStatusCodes: acceptableStatusCodes)
.counterTracking(for: self)
sessionManager.rx.responseModel(requestParameters: parameters,
additionalValidStatusCodes: additionalValidStatusCodes,
decoder: decoder)
.counterTracking(for: self)
}
fileprivate func increaseRequestCounter() {
/// Perform reactive request to get data and http response
///
/// - 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, additionalValidStatusCodes: Set<Int> = [])
-> Observable<SessionManager.DataResponse> {
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<T: Decodable>(with parameters: ApiUploadRequestParameters,
additionalValidStatusCodes: Set<Int> = [],
decoder: JSONDecoder = JSONDecoder())
-> Observable<SessionManager.ModelResponse<T>> {
sessionManager.rx.uploadResponseModel(requestParameters: parameters,
additionalValidStatusCodes: additionalValidStatusCodes,
decoder: decoder)
.counterTracking(for: self)
}
}
private extension NetworkService {
func increaseRequestCounter() {
lock.lock()
requestCountVariable.value += 1
requestCountRelay.accept(requestCountRelay.value + 1)
lock.unlock()
}
fileprivate func decreaseRequestCounter() {
func decreaseRequestCounter() {
lock.lock()
requestCountVariable.value -= 1
requestCountRelay.accept(requestCountRelay.value - 1)
lock.unlock()
}
}
public extension Observable {
@ -98,12 +144,11 @@ public extension Observable {
///
/// - Parameter networkService: NetworkService to operate on it
/// - Returns: The source sequence with the side-effecting behavior applied.
func counterTracking(for networkService: NetworkService) -> Observable<Observable.E> {
return `do`(onSubscribe: {
func counterTracking(for networkService: NetworkService) -> Observable<Observable.Element> {
`do`(onSubscribe: {
networkService.increaseRequestCounter()
}, onDispose: {
networkService.decreaseRequestCounter()
})
}
}

View File

@ -0,0 +1,77 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
/// Session Manager stored in NetworkService
open class SessionManager: Alamofire.Session {
/// Response with HTTP URL Response and target object
public typealias ModelResponse<T> = (response: HTTPURLResponse, model: T)
/// Response with HTTP URL Response and data
public typealias DataResponse = (response: HTTPURLResponse, data: Data)
/// Acceptable status codes for validation
public let acceptableStatusCodes: Set<Int>
/// Dispatch Queue on which mapping is performed
public let mappingQueue: DispatchQueue
public init(configuration: URLSessionConfiguration,
serverTrustManager: ServerTrustManager,
acceptableStatusCodes: Set<Int>,
mappingQueue: DispatchQueue) {
self.acceptableStatusCodes = acceptableStatusCodes
self.mappingQueue = mappingQueue
let delegate = SessionDelegate()
let delegateQueue = OperationQueue()
delegateQueue.underlyingQueue = mappingQueue
let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
super.init(session: session,
delegate: delegate,
rootQueue: mappingQueue,
serverTrustManager: serverTrustManager)
}
public init(session: URLSession,
delegate: SessionDelegate,
serverTrustManager: ServerTrustManager,
acceptableStatusCodes: Set<Int>,
mappingQueue: DispatchQueue) {
self.acceptableStatusCodes = acceptableStatusCodes
self.mappingQueue = mappingQueue
session.delegateQueue.underlyingQueue = mappingQueue
super.init(session: session,
delegate: delegate,
rootQueue: mappingQueue,
serverTrustManager: serverTrustManager)
}
}

View File

@ -0,0 +1,44 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxCocoa
import RxSwift
open class BaseTappableViewModel<PayloadType> {
private let tapRelay = PublishRelay<PayloadType>()
public var tapDriver: Driver<PayloadType> {
tapRelay.asDriver(onErrorDriveWith: .empty())
}
public var tapObservable: Observable<PayloadType> {
tapRelay.asObservable()
}
public func bind(tapObservable: Observable<PayloadType>) -> Disposable {
tapObservable.bind(to: tapRelay)
}
public func tap(payload: PayloadType) {
tapRelay.accept(payload)
}
}

View File

@ -0,0 +1,27 @@
//
// Copyright (c) 2020 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.
//
open class VoidTappableViewModel: BaseTappableViewModel<Void> {
public func tap() {
tap(payload: ())
}
}

View File

@ -0,0 +1,77 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Placeholder view visual attributes without layout.
open class BasePlaceholderViewModel {
/// Title text with text attributes.
public let title: ViewText
/// Description text with text attributes.
public let description: ViewText?
/// Center image of placeholder.
public let centerImage: UIImage?
/// Button title with text attributes.
public let buttonTitle: ViewText?
/// Placeholder background.
public let background: ViewBackground
/// Memberwise initializer.
///
/// - Parameters:
/// - title: Title text with text attributes.
/// - description: Description text with text attributes.
/// - centerImage: Center image of placeholder.
/// - buttonTitle: Button title with text attributes.
/// - background: Placeholder background.
public init(title: ViewText,
description: ViewText? = nil,
centerImage: UIImage? = nil,
buttonTitle: ViewText? = nil,
background: ViewBackground = .color(.clear)) {
self.title = title
self.description = description
self.centerImage = centerImage
self.buttonTitle = buttonTitle
self.background = background
}
}
public extension BasePlaceholderViewModel {
/// Returns true if description is not nil.
var hasDescription: Bool {
description != nil
}
/// Returns true buttonTitle is not nil.
var hasButton: Bool {
buttonTitle != nil
}
/// Returns true if centerImage is not nil.
var hasCenterImage: Bool {
centerImage != nil
}
}

View File

@ -0,0 +1,104 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Layoutless placeholder view. This class is used as views holder & configurator.
/// You should inherit it and implement layout.
open class BasePlaceholderView: ButtonHolderView, InitializableView {
/// Title label of placeholder view.
public let titleLabel = UILabel()
/// Description label of placeholder view.
public let descriptionLabel = UILabel()
/// Center image view of placeholder view.
public let centerImageView = UIImageView()
/// Action button of placeholder view.
public private(set) lazy var button = createButton()
/// Background image view of placeholder view.
public let backgroundImageView = UIImageView()
public override init(frame: CGRect) {
super.init(frame: frame)
initializeView()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Subclass override
/// Override to create your own subclass button.
///
/// - Returns: UIButton (sub)class.
open func createButton() -> UIButton {
UIButton()
}
// MARK: - InitializableView
open func addViews() {
// override in subclass
}
open func bindViews() {
// override in subclass
}
open func configureAppearance() {
// override in subclass
}
open func localize() {
// override in subclass
}
open func configureLayout() {
// override in subclass
}
}
public extension BasePlaceholderView {
/// Method for base configuration BasePlaceholderView instance.
///
/// - Parameter viewModel: Placeholder view visual attributes without layout.
func baseConfigure(with viewModel: BasePlaceholderViewModel) {
titleLabel.configure(with: viewModel.title)
descriptionLabel.isHidden = !viewModel.hasDescription
viewModel.description?.configure(view: descriptionLabel)
centerImageView.isHidden = !viewModel.hasCenterImage
centerImageView.image = viewModel.centerImage
viewModel.background.configure(backgroundView: self,
backgroundImageView: backgroundImageView)
button.isHidden = !viewModel.hasButton
viewModel.buttonTitle?.configure(view: button)
}
}

View File

@ -0,0 +1,73 @@
//
// Copyright (c) 2020 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
open class BaseRxTableViewCell: UITableViewCell, InitializableView, DisposeBagHolder {
// MARK: - Properties
public var disposeBag = DisposeBag()
// MARK: - Initialization
override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
initializeView()
}
@available(*, unavailable)
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - Override
override open func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
// MARK: - InitializableView
open func addViews() {
// overriding
}
open func bindViews() {
// overriding
}
open func configureLayout() {
// overriding
}
open func configureAppearance() {
selectionStyle = .none
}
open func localize() {
// overriding
}
}

View File

@ -0,0 +1,47 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// The main purpose of this class is to fix empty space on top of the screen
/// when view controller view is UICollectionView.
open class CollectionViewWrapperView: ScrollViewHolderView, CollectionViewHolder {
/// Contained collection view.
public let collectionView: UICollectionView
/// Initializer with collection view layout parameter.
///
/// - Parameter layout: UICollectionViewLayout to pass in UICollectionView init.
public init(layout: UICollectionViewLayout) {
self.collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
self.collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
super.init(frame: .zero)
addSubview(collectionView)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,79 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import TableKit
open class ContainerTableCell<TView: UIView>: BaseRxTableViewCell, ConfigurableCell where TView: ConfigurableView {
// MARK: - Properties
private let wrappedView = TView()
open var shouldConfigureDefaultConstraints: Bool {
true
}
open var contentInsets: UIEdgeInsets {
.zero
}
open var contentViewBackgroundColor: UIColor {
.clear
}
// MARK: - ConfigurableCell
open func configure(with viewModel: TView.ViewModelType) {
disposeBag = DisposeBag()
wrappedView.configure(with: viewModel)
}
// MARK: - InitializableView
override open func addViews() {
super.addViews()
contentView.addSubview(wrappedView)
}
override open func configureLayout() {
super.configureLayout()
if shouldConfigureDefaultConstraints {
wrappedView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(contentInsets)
}
} else {
configureCustomConstraints(forWrappedView: wrappedView)
}
}
override open func configureAppearance() {
super.configureAppearance()
contentView.backgroundColor = contentViewBackgroundColor
backgroundColor = contentViewBackgroundColor
}
open func configureCustomConstraints(forWrappedView view: TView) { }
}

View File

@ -0,0 +1,109 @@
//
// 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 UIKit
import RxCocoa
import RxSwift
/// class that is a CustomizableButtonView subview and gives it a button functionality
open class CustomizableButton: UIButton {
// MARK: - Constants
private let defaultBackgroundColor = UIColor.white
// MARK: - Background
private var backgroundColors: [UIControl.State: UIColor] = [:] {
didSet {
updateBackgroundColor()
}
}
func set(backgroundColors: [UIControl.State: UIColor]) {
backgroundColors.forEach { setBackgroundColor($1, for: $0) }
}
func setBackgroundColor(_ color: UIColor, for state: UIControl.State) {
backgroundColors[state] = color
}
func backgroundColor(for state: UIControl.State) -> UIColor? {
backgroundColors[state]
}
private func updateBackgroundColor() {
if isEnabled {
if isHighlighted {
updateBackgroundColor(to: .highlighted)
} else {
updateBackgroundColor(to: .normal)
}
} else {
updateBackgroundColor(to: .disabled)
}
}
private func updateBackgroundColor(to state: UIControl.State) {
if let stateColor = backgroundColor(for: state) {
backgroundColor = stateColor
} else if state != .normal, let normalStateColor = backgroundColor(for: .normal) {
backgroundColor = normalStateColor
} else {
backgroundColor = defaultBackgroundColor
}
}
// MARK: - Title
func set(titleColors: [UIControl.State: UIColor]) {
titleColors.forEach { setTitleColor($1, for: $0) }
}
func set(titles: [UIControl.State: String]) {
titles.forEach { setTitle($1, for: $0) }
}
func set(attributtedTitles: [UIControl.State: NSAttributedString]) {
attributtedTitles.forEach { setAttributedTitle($1, for: $0) }
}
// MARK: - Images
func set(images: [UIControl.State: UIImage]) {
images.forEach { setImage($1, for: $0) }
}
// MARK: - State
override open var isEnabled: Bool {
didSet {
updateBackgroundColor()
}
}
override open var isHighlighted: Bool {
didSet {
updateBackgroundColor()
}
}
}

View File

@ -0,0 +1,342 @@
//
// 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 typealias Spinner = UIView & Animatable
public struct CustomizableButtonState: OptionSet {
// MARK: - OptionSet conformance
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
// MARK: - States
public static let highlighted = CustomizableButtonState(rawValue: 1 << 1)
public static let normal = CustomizableButtonState(rawValue: 1 << 2)
public static let enabled = CustomizableButtonState(rawValue: 1 << 3)
public static let disabled = CustomizableButtonState(rawValue: 1 << 4)
public static let loading = CustomizableButtonState(rawValue: 1 << 5)
// MARK: - Properties
public var isLoading: Bool {
contains(.loading)
}
}
/// container class that acts like a button and provides great customization
open class CustomizableButtonView: UIView, InitializableView, ConfigurableView {
// MARK: - Stored Properties
public private(set) var disposeBag = DisposeBag()
private let button = CustomizableButton()
open var tapOnDisabledButton: VoidBlock?
public var shadowView = UIView() {
willSet {
shadowView.removeFromSuperview()
}
didSet {
insertSubview(shadowView, at: 0)
configureShadowViewConstraints()
}
}
public var spinnerView: Spinner? {
willSet {
removeSpinner()
}
didSet {
if spinnerView != nil {
addSpinner()
}
}
}
public var appearance = Appearance() {
didSet {
configureAppearance()
configureConstraints()
}
}
public var buttonTitle: String = "" {
willSet {
button.text = newValue
}
}
public var hidesLabelWhenLoading = false
// MARK: - Computed Properties
public var tapObservable: Observable<Void> {
button.rx.tap.asObservable()
}
override open var forFirstBaselineLayout: UIView {
button.forFirstBaselineLayout
}
override open var forLastBaselineLayout: UIView {
button.forLastBaselineLayout
}
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if var touchPoint = touches.first?.location(in: self) {
touchPoint = convert(touchPoint, to: self)
if button.frame.contains(touchPoint) && !button.isEnabled {
tapOnDisabledButton?()
}
}
super.touchesBegan(touches, with: event)
}
// MARK: - Initialization
override public init(frame: CGRect) {
super.init(frame: frame)
initializeView()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeView()
}
// MARK: - UI
override open func layoutSubviews() {
super.layoutSubviews()
if shadowView.layer.cornerRadius == 0 {
shadowView.layer.shadowPath = UIBezierPath(rect: button.bounds).cgPath
}
}
private func set(active: Bool) {
if hidesLabelWhenLoading {
button.titleLabel?.layer.opacity = active ? 0 : 1
}
spinnerView?.isHidden = !active
if active {
spinnerView?.startAnimating()
} else {
spinnerView?.stopAnimating()
}
}
private func addSpinner() {
if let spinner = spinnerView {
addSubview(spinner)
configureSpinnerConstraints()
spinner.isHidden = true
}
}
private func removeSpinner() {
if spinnerView != nil {
self.spinnerView?.removeFromSuperview()
}
}
// MARK: - Layout
private func configureConstraints() {
button.pinToSuperview(with: appearance.buttonInsets)
configureShadowViewConstraints()
layoutIfNeeded()
}
private func configureSpinnerConstraints() {
guard let spinnerView = spinnerView else {
return
}
spinnerView.translatesAutoresizingMaskIntoConstraints = false
var constraints = [NSLayoutConstraint]()
switch appearance.spinnerPosition {
case .center:
constraints = [
spinnerView.centerXAnchor.constraint(equalTo: button.centerXAnchor),
spinnerView.centerYAnchor.constraint(equalTo: button.centerYAnchor)
]
case .leftToText(let offset):
if let buttonLabel = button.titleLabel {
constraints = [
spinnerView.centerYAnchor.constraint(equalTo: buttonLabel.centerYAnchor),
spinnerView.trailingAnchor.constraint(equalTo: buttonLabel.leadingAnchor, constant: -offset)
]
}
case .rightToText(let offset):
if let buttonLabel = button.titleLabel {
constraints = [
spinnerView.centerYAnchor.constraint(equalTo: buttonLabel.centerYAnchor),
spinnerView.leadingAnchor.constraint(equalTo: buttonLabel.trailingAnchor, constant: offset)
]
}
}
NSLayoutConstraint.activate(constraints)
}
private func configureShadowViewConstraints() {
shadowView.constraintToEdges(of: button, with: .zero)
}
// MARK: - Initializable View
open func addViews() {
addSubviews(shadowView, button)
}
open func configureAppearance() {
button.titleLabel?.numberOfLines = appearance.numberOfLines
button.titleLabel?.font = appearance.buttonFont
button.alpha = appearance.alpha
button.set(attributtedTitles: appearance.buttonStateAttributtedTitles)
button.set(titleColors: appearance.buttonTitleStateColors)
button.set(images: appearance.buttonStateIcons)
button.set(backgroundColors: appearance.buttonBackgroundStateColors)
let offset = appearance.buttonIconOffset
button.imageEdgeInsets = UIEdgeInsets(top: offset.vertical,
left: offset.horizontal,
bottom: -offset.vertical,
right: -offset.horizontal)
if let cornerRadius = appearance.buttonCornerRadius {
button.layer.cornerRadius = cornerRadius
} else {
button.layer.cornerRadius = 0
}
setNeedsDisplay()
}
open func configure(with viewModel: CustomizableButtonViewModel) {
disposeBag = DisposeBag()
viewModel.stateDriver.drive(stateBinder).disposed(by: disposeBag)
viewModel.bind(tapObservable: tapObservable).disposed(by: disposeBag)
button.text = viewModel.buttonTitle
appearance = viewModel.appearance
}
private var stateBinder: Binder<CustomizableButtonState> {
Binder(self) { base, value in
base.configureButton(withState: value)
base.onStateChange(value)
}
}
open func onStateChange(_ state: CustomizableButtonState) {
/// override in subclass
}
open func configureButton(withState state: CustomizableButtonState) {
button.isEnabled = ![.disabled, .loading].contains(state)
isUserInteractionEnabled = button.isEnabled
button.isHighlighted = state.contains(.highlighted) && !state.contains(.normal)
set(active: state.contains(.loading))
setNeedsDisplay()
}
}
private extension UIView {
func constraintToEdges(of view: UIView, with offset: UIEdgeInsets) {
translatesAutoresizingMaskIntoConstraints = false
let constraints = [
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: offset.left),
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: offset.right),
topAnchor.constraint(equalTo: view.topAnchor, constant: offset.top),
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: offset.bottom)
]
NSLayoutConstraint.activate(constraints)
}
}
public extension CustomizableButtonView {
struct Appearance {
public var buttonFont: UIFont
public var buttonStateAttributtedTitles: [UIControl.State: NSAttributedString]
public var buttonTitleStateColors: [UIControl.State: UIColor]
public var buttonBackgroundStateColors: [UIControl.State: UIColor]
public var buttonStateIcons: [UIControl.State: UIImage]
public var buttonIconOffset: UIOffset
public var buttonInsets: UIEdgeInsets
public var buttonCornerRadius: CGFloat?
public var spinnerPosition: SpinnerPosition
public var numberOfLines: Int
public var alpha: CGFloat
public init(buttonFont: UIFont = .systemFont(ofSize: 15),
buttonStateAttributtedTitles: [UIControl.State: NSAttributedString] = [:],
buttonTitleStateColors: [UIControl.State: UIColor] = [:],
buttonBackgroundStateColors: [UIControl.State: UIColor] = [:],
buttonStateIcons: [UIControl.State: UIImage] = [:],
buttonIconOffset: UIOffset = .zero,
buttonInsets: UIEdgeInsets = .zero,
buttonCornerRadius: CGFloat? = nil,
spinnerPosition: SpinnerPosition = .center,
numberOfLines: Int = 0,
alpha: CGFloat = 1) {
self.buttonFont = buttonFont
self.buttonStateAttributtedTitles = buttonStateAttributtedTitles
self.buttonTitleStateColors = buttonTitleStateColors
self.buttonBackgroundStateColors = buttonBackgroundStateColors
self.buttonStateIcons = buttonStateIcons
self.buttonIconOffset = buttonIconOffset
self.buttonInsets = buttonInsets
self.buttonCornerRadius = buttonCornerRadius
self.spinnerPosition = spinnerPosition
self.numberOfLines = numberOfLines
self.alpha = alpha
}
}
enum SpinnerPosition {
case center
case leftToText(offset: CGFloat)
case rightToText(offset: CGFloat)
}
}
extension UIControl.State: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(Int(rawValue))
}
}

View File

@ -0,0 +1,56 @@
//
// 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 RxCocoa
import RxSwift
/// viewModel class for CustomizableButtonView configuration
open class CustomizableButtonViewModel {
public typealias Appearance = CustomizableButtonView.Appearance
private let stateRelay = BehaviorRelay(value: CustomizableButtonState.enabled)
private let tapRelay = BehaviorRelay(value: ())
public let appearance: Appearance
public let buttonTitle: String
public init(buttonTitle: String, appearance: Appearance) {
self.buttonTitle = buttonTitle
self.appearance = appearance
}
open var stateDriver: Driver<CustomizableButtonState> {
stateRelay.asDriver()
}
func bind(tapObservable: Observable<Void>) -> Disposable {
tapObservable.bind(to: tapRelay)
}
public var tapDriver: Driver<Void> {
tapRelay.asDriver()
}
public func updateState(with newState: CustomizableButtonState) {
stateRelay.accept(newState)
}
}

View File

@ -0,0 +1,54 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
internal final class TextPlaceholderView: UIView {
enum PlaceholderText: String {
case empty = "There is nothing here"
case error = "An error has occurred"
case loading = "Loading..."
case retry = "Retry"
case retryLoadMore = "Retry load more"
}
init(title: PlaceholderText) {
super.init(frame: .zero)
let label = UILabel()
label.text = title.rawValue
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

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

View File

@ -34,7 +34,7 @@ public final class EmptyCell: SeparatorCell, AppearanceConfigurable, Configurabl
}
}
public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
resetAppearance()
@ -61,5 +61,4 @@ public final class EmptyCell: SeparatorCell, AppearanceConfigurable, Configurabl
private func resetAppearance() {
configure(appearance: Appearance())
}
}

View File

@ -48,7 +48,6 @@ public final class EmptyCellRow: TableRow<EmptyCell> {
/// Used for set custom height to each cell, not for each cell type
override public var defaultHeight: CGFloat? {
return rowHeight
rowHeight
}
}

View File

@ -0,0 +1,52 @@
//
// 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.
//
/// Default view model for LabelTableViewCell.
open class LabelCellViewModel {
public let viewText: ViewText
public let contentBackground: ViewBackground
public let contentInsets: UIEdgeInsets
public let labelInsets: UIEdgeInsets
public let separatorType: CellSeparatorType
/// Memberwise initializer.
///
/// - Parameters:
/// - viewText: View text to configure label.
/// - contentBackground: View background to configure background.
/// - contentInsets: Content insets to use for layout whole content.
/// - labelInsets: Label insets to use for layout label.
/// - separatorType: Separator type to use for separators.
public init(viewText: ViewText,
contentBackground: ViewBackground = .color(.clear),
contentInsets: UIEdgeInsets = .zero,
labelInsets: UIEdgeInsets = .zero,
separatorType: CellSeparatorType = .none) {
self.viewText = viewText
self.contentBackground = contentBackground
self.contentInsets = contentInsets
self.labelInsets = labelInsets
self.separatorType = separatorType
}
}

View File

@ -0,0 +1,143 @@
//
// 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 TableKit
import SnapKit
/// Label cell with separators, includes background image view.
open class LabelTableViewCell: SeparatorCell, InitializableView, ConfigurableCell {
// MARK: - Properties
private let label = UILabel()
private let backgroundImageView = UIImageView()
private let contentContainerView = UIView()
private var viewModel: LabelCellViewModel?
// MARK: - Init
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
initializeView()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeView()
}
open override func updateConstraints() {
let topSeparatorHeight = viewModel?.separatorType.topConfiguration?.totalHeight ?? 0
let bottomSeparatorHeight = viewModel?.separatorType.bottomConfiguration?.totalHeight ?? 0
contentContainerView.snp.remakeConstraints { make in
make.top.equalToSuperview().inset(contentInsets.top + topSeparatorHeight)
make.leading.equalToSuperview().inset(contentInsets.left)
make.trailing.equalToSuperview().inset(contentInsets.right)
make.bottom.equalToSuperview().inset(contentInsets.bottom + bottomSeparatorHeight)
}
label.snp.remakeConstraints { make in
make.edges.equalToSuperview().inset(labelInsets)
}
backgroundImageView.snp.remakeConstraints { make in
make.edges.equalToSuperview()
}
super.updateConstraints()
}
// MARK: - InitializableView
open func addViews() {
contentView.addSubview(contentContainerView)
contentContainerView.addSubviews(backgroundImageView, label)
}
open func configureAppearance() {
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
configureAppearance(of: label, backgroundImageView: backgroundImageView)
}
// MARK: - ConfigurableCell
public func configure(with viewModel: LabelCellViewModel) {
configureLabelCell(with: viewModel)
}
// MARK: - Private
private var labelInsets: UIEdgeInsets {
viewModel?.labelInsets ?? .zero
}
private var contentInsets: UIEdgeInsets {
viewModel?.contentInsets ?? .zero
}
// MARK: - Subclass methods to override
/// Callback for label and background image view appearance configuration.
///
/// - Parameters:
/// - label: Internal UILabel instance to configure.
/// - backgroundImageView: Internal UIImageView instance to configure.
open func configureAppearance(of label: UILabel, backgroundImageView: UIImageView) {
label.numberOfLines = 0
}
// MARK: - Configuration methods
/// Convenient method for configuration cell with LabelCellViewModel.
///
/// - Parameter viewModel: LabelCellViewModel instance.
public func configureLabelCell(with viewModel: LabelCellViewModel) {
self.viewModel = viewModel
configureSeparator(with: viewModel.separatorType)
configureLabelText(with: viewModel.viewText)
configureContentBackground(with: viewModel.contentBackground)
setNeedsUpdateConstraints()
}
/// Method for background configuration.
///
/// - Parameter contentBackground: Content background to use as background.
public func configureContentBackground(with contentBackground: ViewBackground) {
contentBackground.configure(backgroundView: contentContainerView,
backgroundImageView: backgroundImageView)
}
/// Method for text configuration.
///
/// - Parameter viewText: View text to use as background.
public func configureLabelText(with viewText: ViewText) {
label.configure(with: viewText)
}
}

View File

@ -39,23 +39,22 @@ open class SeparatorCell: UITableViewCell {
/// Configure separator with viewModel
/// - parameter separatorType: type of separators
public func configureSeparator(with separatorType: CellSeparatorType) {
topView.isHidden = separatorType.topIsHidden
bottomView.isHidden = separatorType.bottomIsHidden
switch separatorType {
case .none:
topView.isHidden = true
bottomView.isHidden = true
break
case .bottom(let configuration):
topView.isHidden = true
bottomView.isHidden = false
updateBottomSeparator(with: configuration)
setNeedsUpdateConstraints()
case .top(let configuration):
topView.isHidden = false
bottomView.isHidden = true
updateTopSeparator(with: configuration)
setNeedsUpdateConstraints()
case .full(let topConfiguration, let bottomConfiguration):
topView.isHidden = false
bottomView.isHidden = false
case let .full(topConfiguration, bottomConfiguration):
updateTopSeparator(with: topConfiguration)
updateBottomSeparator(with: bottomConfiguration)
setNeedsUpdateConstraints()
@ -64,18 +63,20 @@ open class SeparatorCell: UITableViewCell {
/// Move separator upward in hierarchy
public func bringSeparatorsToFront() {
contentView.bringSubview(toFront: topView)
contentView.bringSubview(toFront: bottomView)
contentView.bringSubviewToFront(topView)
contentView.bringSubviewToFront(bottomView)
}
/// Move separator backward in hierarchy
public func sendSeparatorsToBack() {
contentView.sendSubview(toBack: topView)
contentView.sendSubview(toBack: bottomView)
contentView.sendSubviewToBack(topView)
contentView.sendSubviewToBack(bottomView)
}
// MARK: - Private
// swiftlint:disable implicitly_unwrapped_optional
private var topView: UIView!
private var bottomView: UIView!
@ -91,10 +92,12 @@ open class SeparatorCell: UITableViewCell {
private var bottomViewBottomConstraint: NSLayoutConstraint!
private var bottomViewHeightConstraint: NSLayoutConstraint!
private var topSeparatorInsets = UIEdgeInsets.zero
// swiftlint:enable implicitly_unwrapped_optional
private var topSeparatorInsets = UIEdgeInsets.zero
private var bottomSeparatorInsets = UIEdgeInsets.zero
private var topSeparatorHeight = Constants.defaultSeparatorHeight
private var topSeparatorHeight = Constants.defaultSeparatorHeight
private var bottomSeparatorHeight = Constants.defaultSeparatorHeight
// MARK: - Initialization
@ -105,20 +108,20 @@ open class SeparatorCell: UITableViewCell {
configureLineViews()
}
public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureLineViews()
}
override open func updateConstraints() {
topViewTopConstraint.constant = topSeparatorInsets.top
topViewLeftConstraint.constant = topSeparatorInsets.left
topViewRightConstraint.constant = topSeparatorInsets.right
topViewHeightConstraint.constant = topSeparatorHeight
topViewTopConstraint.constant = topSeparatorInsets.top
topViewLeftConstraint.constant = topSeparatorInsets.left
topViewRightConstraint.constant = topSeparatorInsets.right
topViewHeightConstraint.constant = topSeparatorHeight
bottomViewLeftConstraint.constant = bottomSeparatorInsets.left
bottomViewRightConstraint.constant = bottomSeparatorInsets.right
bottomViewLeftConstraint.constant = bottomSeparatorInsets.left
bottomViewRightConstraint.constant = bottomSeparatorInsets.right
bottomViewBottomConstraint.constant = bottomSeparatorInsets.bottom
bottomViewHeightConstraint.constant = bottomSeparatorHeight
@ -146,13 +149,13 @@ open class SeparatorCell: UITableViewCell {
private func updateTopSeparator(with configuration: SeparatorConfiguration) {
topView.backgroundColor = configuration.color
topSeparatorHeight = configuration.height
topSeparatorInsets = configuration.insets ?? .zero
topSeparatorInsets = configuration.insets
}
private func updateBottomSeparator(with configuration: SeparatorConfiguration) {
bottomView.backgroundColor = configuration.color
bottomSeparatorHeight = configuration.height
bottomSeparatorInsets = configuration.insets ?? .zero
bottomSeparatorHeight = configuration.height
bottomSeparatorInsets = configuration.insets
}
private func createConstraints() {
@ -188,5 +191,4 @@ open class SeparatorCell: UITableViewCell {
super.prepareForReuse()
configureSeparator(with: .none)
}
}

View File

@ -24,19 +24,19 @@ import UIKit
/// Separator configuration. Supports positioning, color and height per each separator
public struct SeparatorConfiguration {
let color: UIColor
let insets: UIEdgeInsets?
let height: CGFloat
public let color: UIColor
public let insets: UIEdgeInsets
public let height: CGFloat
/// Initialize configuration with parameters
/// - parameter color: Color must be provided
/// - parameter insets: Insets for separator. Default is no insets
/// - parameter height: Height for separator. Default is 1 pixel
/// - returns: Ready to use separator configuration
public init(color: UIColor, insets: UIEdgeInsets? = .zero, height: CGFloat = CGFloat(pixels: 1)) {
self.color = color
public init(color: UIColor, insets: UIEdgeInsets = .zero, height: CGFloat = CGFloat(pixels: 1)) {
self.color = color
self.insets = insets
self.height = height
}
}

View File

@ -39,5 +39,4 @@ public final class SeparatorRowBox {
self.row = row
setSeparatorHandler = row.set
}
}

View File

@ -24,21 +24,27 @@ import UIKit
public final class SpinnerView: UIView, Animatable, LoadingIndicator {
private(set) var animating: Bool = false
private var animating: Bool {
imageView?.layer.animation(forKey: CABasicAnimation.rotationKeyPath) != nil
}
private var startTime = CFTimeInterval(0)
private var stopTime = CFTimeInterval(0)
private weak var imageView: UIImageView?
private let hidesWhenStopped: Bool
private let animationDuration: CFTimeInterval
private let animationRepeatCount: Float
private let clockwiseAnimation: Bool
public init(image: UIImage,
hidesWhenStopped: Bool = true,
animationDuration: CFTimeInterval = 1,
animationRepeatCount: Float = Float.infinity,
clockwiseAnimation: Bool = true) {
self.hidesWhenStopped = hidesWhenStopped
self.animationDuration = animationDuration
self.animationRepeatCount = animationRepeatCount
self.clockwiseAnimation = clockwiseAnimation
@ -60,7 +66,7 @@ public final class SpinnerView: UIView, Animatable, LoadingIndicator {
NotificationCenter.default.addObserver(self,
selector: #selector(SpinnerView.restartAnimationIfNeeded),
name: .UIApplicationWillEnterForeground,
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
@ -80,6 +86,14 @@ public final class SpinnerView: UIView, Animatable, LoadingIndicator {
}
}
override public func didMoveToSuperview() {
super.didMoveToSuperview()
if superview != nil {
restartAnimationIfNeeded()
}
}
// MARK: - Animatable
@objc public func startAnimating() {
@ -87,9 +101,9 @@ public final class SpinnerView: UIView, Animatable, LoadingIndicator {
return
}
animating = true
imageView?.isHidden = false
if hidesWhenStopped {
imageView?.isHidden = false
}
addAnimation()
}
@ -99,9 +113,9 @@ public final class SpinnerView: UIView, Animatable, LoadingIndicator {
return
}
animating = false
imageView?.isHidden = true
if hidesWhenStopped {
imageView?.isHidden = true
}
removeAnimation()
}
@ -139,5 +153,4 @@ public final class SpinnerView: UIView, Animatable, LoadingIndicator {
addAnimation()
}
}
}

View File

@ -0,0 +1,47 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UITableView
/// The main purpose of this class is to fix empty space on top of the screen
/// when view controller view is UITableView.
open class TableViewWrapperView: ScrollViewHolderView, TableViewHolder {
/// Contained table view.
public let tableView: UITableView
/// Initializer with tableViewStyle parameter.
///
/// - Parameter tableViewStyle: UITableViewStyle to pass in UITableView init.
public init(tableViewStyle: UITableView.Style) {
self.tableView = UITableView(frame: .zero, style: tableViewStyle)
self.tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
super.init(frame: .zero)
addSubview(tableView)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,130 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
/// Class that maps data model field to String and vise-versa.
public final class DataModelFieldBinding<T> {
public typealias GetFieldClosure = (T) -> String?
public typealias MergeFieldClosure = (T, String?) -> T
private let modelRelay: BehaviorRelay<T>
private let modelDriver: Driver<T>
private let getFieldClosure: DataModelFieldBinding<T>.GetFieldClosure
private let mergeFieldClosure: DataModelFieldBinding<T>.MergeFieldClosure
/// Memberwise initializer.
///
/// - Parameters:
/// - modelRelay: BehaviourRelay that contains data model.
/// - modelDriver: Driver that emits new data models.
/// - getFieldClosure: Closure for getting field string reprerentation from data model.
/// - mergeFieldClosure: Closure for merging new field value into data model.
public init(modelRelay: BehaviorRelay<T>,
modelDriver: Driver<T>,
getFieldClosure: @escaping GetFieldClosure,
mergeFieldClosure: @escaping MergeFieldClosure) {
self.modelRelay = modelRelay
self.modelDriver = modelDriver
self.getFieldClosure = getFieldClosure
self.mergeFieldClosure = mergeFieldClosure
}
/// Method that merges new field values with data model.
///
/// - Parameter textDriver: Driver that emits new text values.
/// - Returns: Disposable object that can be used to unsubscribe the observer from the behaviour relay.
public func mergeStringToModel(from textDriver: Driver<String?>) -> Disposable {
textDriver.map { [modelRelay, mergeFieldClosure] in
mergeFieldClosure(modelRelay.value, $0)
}
.drive(modelRelay)
}
/// A Driver that will emit current field value.
public var fieldDriver: Driver<String?> {
modelDriver.map(getFieldClosure)
}
}
public extension DataModelFieldBinding {
/// Convenience initializer without modelDriver, which will be obtained from modelRelay.
///
/// - Parameters:
/// - modelRelay: BehaviourRelay that contains data model.
/// - getFieldClosure: Closure for getting field string reprerentation from data model.
/// - mergeFieldClosure: Closure for merging new field value into data model.
convenience init(modelRelay: BehaviorRelay<T>,
getFieldClosure: @escaping GetFieldClosure,
mergeFieldClosure: @escaping MergeFieldClosure) {
self.init(modelRelay: modelRelay,
modelDriver: modelRelay.asDriver(),
getFieldClosure: getFieldClosure,
mergeFieldClosure: mergeFieldClosure)
}
}
public extension DataModelFieldBinding where T == String? {
/// Convenience initializer for data model of string.
///
/// - Parameter modelRelay: BehaviourRelay that contains data model.
convenience init(modelRelay: BehaviorRelay<T>) {
self.init(modelRelay: modelRelay,
modelDriver: modelRelay.asDriver(),
getFieldClosure: { $0 },
mergeFieldClosure: { $1 })
}
}
public extension BehaviorRelay {
/// Creates DataModelFieldBinding configured with given closures and behaviour relay itself.
///
/// - Parameters:
/// - getFieldClosure: Closure for getting field string reprerentation from data model.
/// - mergeFieldClosure: Closure for merging new field value into data model.
/// - Returns: DataModelFieldBinding instance.
func fieldBinding(getFieldClosure: @escaping DataModelFieldBinding<Element>.GetFieldClosure,
mergeFieldClosure: @escaping DataModelFieldBinding<Element>.MergeFieldClosure)
-> DataModelFieldBinding<Element> {
DataModelFieldBinding(modelRelay: self,
getFieldClosure: getFieldClosure,
mergeFieldClosure: mergeFieldClosure)
}
}
public extension BehaviorRelay where Element == String? {
/// Creates DataModelFieldBinding configured with behaviour relay itself.
///
/// - Returns: DataModelFieldBinding instance.
func fieldBinding() -> DataModelFieldBinding<Element> {
DataModelFieldBinding(modelRelay: self)
}
}

View File

@ -0,0 +1,99 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import RxCocoa
/// Class that used for binding text field with upper level view model.
open class TextFieldViewModel < ViewEvents: TextFieldViewEvents,
ViewModelEvents: TextFieldViewModelEvents> {
/// Events that can be emitted by view model.
public let viewModelEvents: ViewModelEvents
private let viewEventsRelay = BehaviorRelay<ViewEvents?>(value: nil)
private(set) public var disposeBag = DisposeBag()
/// Initializer with view model events.
///
/// - Parameter viewModelEvents: Events that can be emitted by view model.
public init(viewModelEvents: ViewModelEvents) {
self.viewModelEvents = viewModelEvents
}
/// View events driver that will emit view events structure
/// when view will bind itself to the view model.
public var viewEventsDriver: Driver<ViewEvents> {
viewEventsRelay
.asDriver()
.flatMap { viewEvents -> Driver<ViewEvents> in
guard let viewEvents = viewEvents else {
return .empty()
}
return .just(viewEvents)
}
}
/// Method that performs binding view events to view model.
///
/// - Parameter viewEvents: View events structure.
public func bind(viewEvents: ViewEvents) {
viewEventsRelay.accept(viewEvents)
}
/// Unbinds view from view model.
public func unbindView() {
disposeBag = DisposeBag()
}
}
public extension TextFieldViewModel {
typealias MapViewEventClosure = (ViewEvents) -> Disposable
/// Convenient method for binding to the current view events structure.
///
/// - Parameter closure: Closure that takes a view events parameter and returns Disposable.
/// - Returns: Disposable object that can be used to unsubscribe the observer from the binding.
func mapViewEvents(_ closure: @escaping MapViewEventClosure) -> Disposable {
mapViewEvents { [closure($0)] }
}
typealias MapViewEventsClosure = (ViewEvents) -> [Disposable]
/// Convenient method for binding to the current view events structure.
///
/// - Parameter closure: Closure that takes a view events parameter and returns [Disposable].
/// - Returns: Disposable object that can be used to unsubscribe the observer from the binding.
func mapViewEvents(_ closure: @escaping MapViewEventsClosure) -> Disposable {
viewEventsDriver
.map { [weak self] in
guard let strongSelf = self else {
return
}
closure($0).forEach { $0.disposed(by: strongSelf.disposeBag) }
}
.drive()
}
}

View File

@ -0,0 +1,47 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit.UIFont
import UIKit.UIColor
/// Base set of attributes to configure appearance of text.
open class BaseTextAttributes {
/// Text font.
public let font: UIFont
/// Text color.
public let color: UIColor
/// Text alignment.
public let alignment: NSTextAlignment
/// Memberwise initializer.
///
/// - Parameters:
/// - font: Text font.
/// - color: Text color.
/// - alignment: Text alignment.
public init(font: UIFont, color: UIColor, alignment: NSTextAlignment = .natural) {
self.font = font
self.color = color
self.alignment = alignment
}
}

View File

@ -28,7 +28,7 @@ open class XibView: UIView {
/// Nib name used to instantiate inner view
/// - NOTE: Be very carefully when you're intending to change this line
open var innerViewNibName: String {
return typeName(of: type(of: self))
typeName(of: type(of: self))
}
public convenience init() {
@ -60,7 +60,5 @@ open class XibView: UIView {
/// Provide initial configuration. Called once
open func configure() {
}
}

View File

@ -28,5 +28,4 @@ import Foundation
public enum CursorError: Error {
case exhausted
}

View File

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

View File

@ -0,0 +1,101 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// Enum that contains states for paginated data loading.
///
/// - initial: Initial state. Before something will happen.
/// - initialLoading: Initial loading state. When data was requested initially.
/// - loadingMore: Loading more state. When additional data was requested.
/// - results: Result state from a specific data source after a given state.
/// - error: Error state with a specific error after a given state.
/// - empty: Empty state. When data was initially requested and empty result was received.
/// - exhausted: Exhausted state. When no more results can be received.
public indirect enum PaginationDataLoadingState<DS: DataSource> {
case initial
case initialLoading(after: PaginationDataLoadingState)
case loadingMore(after: PaginationDataLoadingState)
case results(newItems: DS.ResultType, from: DS, after: PaginationDataLoadingState)
case error(error: Error, after: PaginationDataLoadingState)
case empty
case exhausted
}
extension PaginationDataLoadingState: DataLoadingState {
public typealias DataSourceType = DS
public static var initialState: PaginationDataLoadingState<DS> {
.initial
}
public static var emptyState: PaginationDataLoadingState<DS> {
.empty
}
public static func initialLoadingState(after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
.initialLoading(after: after)
}
public static func resultState(result: DS.ResultType,
from: DataSourceType,
after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
.results(newItems: result, from: from, after: after)
}
public static func errorState(error: Error,
after: PaginationDataLoadingState<DS>) -> PaginationDataLoadingState<DS> {
.error(error: error, after: after)
}
public var isInitialState: Bool {
switch self {
case .initial:
return true
default:
return false
}
}
public var result: DS.ResultType? {
switch self {
case .results(let newItems, _, _):
return newItems
default:
return nil
}
}
public var error: Error? {
switch self {
case .error(let error, _):
return error
default:
return nil
}
}
}

View File

@ -25,8 +25,11 @@ import Foundation
/// Enum that represents common errors in LeadKit framework
///
/// - failedToCastValue: attempt to cast was failed
/// - failedToDecode: attempt to decoding was failed
/// - failedToEncodeQueryItems: attempt to encoding to query items was failed
public enum LeadKitError: Error {
case failedToCastValue(expectedType: Any.Type, givenType: Any.Type)
case failedToDecode(reason: String)
case failedToEncodeQueryItems
}

View File

@ -31,9 +31,8 @@ import Alamofire
/// - mapping: Errors that occurs during mapping json into model.
public enum RequestError: Error {
case noConnection
case network(error: Error)
case invalidResponse(error: AFError)
case mapping(error: Error, response: Any)
case noConnection(url: String?)
case network(error: Error, response: Data?, url: String?)
case invalidResponse(error: AFError, response: Data?, url: String?)
case mapping(error: Error, response: Data, url: String?)
}

View File

@ -0,0 +1,29 @@
//
// 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 TableKit
public enum SearchResultsViewControllerState {
case initial
case rowsContent(rows: [Row])
case sectionsContent(sections: [TableSection])
}

View File

@ -0,0 +1,32 @@
//
// 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.
//
/// Title type for UIViewController title.
///
/// - large: large sized title
/// - normal: normal sized title
/// - empty: empty title
public enum TitleType {
case large(title: String)
case normal(title: String)
case empty
}

View File

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

View File

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

View File

@ -0,0 +1,33 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
/// Enum that describes text with appearance options.
///
/// - string: Regular string with common and often-used text attributes.
/// - attributedString: Attributed string.
public enum ViewText {
case string(String, textAttributes: BaseTextAttributes)
case attributedString(NSAttributedString)
}

View File

@ -1,123 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
import RxSwift
import RxAlamofire
import ObjectMapper
public extension Alamofire.SessionManager {
/// The default acceptable range 200...299
static let defaultAcceptableStatusCodes = Array(200..<300)
}
public extension Reactive where Base: Alamofire.SessionManager {
/// Method which executes request with given api parameters
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Returns: Observable with request
func apiRequest(requestParameters: ApiRequestParameters,
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<DataRequest> {
return request(requestParameters.method,
requestParameters.url,
parameters: requestParameters.parameters,
encoding: requestParameters.encoding,
headers: requestParameters.headers)
.map { $0.validate(statusCode: acceptableStatusCodes) }
}
/// Method that executes request and serializes response into target object
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func responseModel<T: ImmutableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = .global(),
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<(response: HTTPURLResponse, model: T)> {
return apiRequest(requestParameters: requestParameters, acceptableStatusCodes: acceptableStatusCodes)
.flatMap { $0.rx.apiResponse(mappingQueue: mappingQueue) }
}
/// Method that executes request and serializes response into array of target objects
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func responseModel<T: ImmutableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = .global(),
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<(response: HTTPURLResponse, models: [T])> {
return apiRequest(requestParameters: requestParameters, acceptableStatusCodes: acceptableStatusCodes)
.flatMap { $0.rx.apiResponse(mappingQueue: mappingQueue) }
}
/// Method that executes request and serializes response into target type
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func responseObject<T>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = .global(),
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<(response: HTTPURLResponse, object: T)> {
return apiRequest(requestParameters: requestParameters, acceptableStatusCodes: acceptableStatusCodes)
.flatMap { $0.rx.apiResponse(mappingQueue: mappingQueue) }
}
/// Method that executes request and serializes response into target object
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func responseObservableModel<T: ObservableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = .global(),
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
return apiRequest(requestParameters: requestParameters, acceptableStatusCodes: acceptableStatusCodes)
.flatMap { $0.rx.observableApiResponse(mappingQueue: mappingQueue) }
}
/// Method that executes request and serializes response into array of target objects
///
/// - Parameter requestParameters: api parameters to pass Alamofire
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func responseObservableModel<T: ObservableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = .global(),
acceptableStatusCodes: [Int] = Base.defaultAcceptableStatusCodes)
-> Observable<(response: HTTPURLResponse, models: [T])> where T.ModelType == T {
return apiRequest(requestParameters: requestParameters, acceptableStatusCodes: acceptableStatusCodes)
.flatMap { $0.rx.observableApiResponse(mappingQueue: mappingQueue) }
}
}

View File

@ -1,167 +0,0 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
import RxSwift
import ObjectMapper
import RxAlamofire
typealias ServerResponse = (response: HTTPURLResponse, result: Any)
public extension Reactive where Base: DataRequest {
private typealias JSON = [String: Any]
/// Method that serializes response into target object
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func apiResponse<T: ImmutableMappable>(mappingQueue: DispatchQueue = .global())
-> Observable<(response: HTTPURLResponse, model: T)> {
return responseJSONOnQueue(mappingQueue)
.tryMapResult { resp, value in
let json = try cast(value) as JSON
return (resp, try T(JSON: json))
}
}
/// Method that serializes response into array of target objects
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func apiResponse<T: ImmutableMappable>(mappingQueue: DispatchQueue = .global())
-> Observable<(response: HTTPURLResponse, models: [T])> {
return responseJSONOnQueue(mappingQueue)
.tryMapResult { resp, value in
let jsonArray = try cast(value) as [JSON]
return (resp, try Mapper<T>().mapArray(JSONArray: jsonArray))
}
}
/// Method that serializes response into target type
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func apiResponse<T>(mappingQueue: DispatchQueue = .global())
-> Observable<(response: HTTPURLResponse, object: T)> {
return responseJSONOnQueue(mappingQueue)
.tryMapResult { resp, value in
return (resp, try cast(value) as T)
}
}
/// Method that serializes response into target object
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func observableApiResponse<T: ObservableMappable>(mappingQueue: DispatchQueue = .global())
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
return responseJSONOnQueue(mappingQueue)
.tryMapObservableResult { resp, value in
let json = try cast(value) as JSON
return T.createFrom(map: Map(mappingType: .fromJSON, JSON: json))
.map { (resp, $0) }
}
}
/// Method that serializes response into array of target objects
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func observableApiResponse<T: ObservableMappable>(mappingQueue: DispatchQueue = .global())
-> Observable<(response: HTTPURLResponse, models: [T])> where T.ModelType == T {
return responseJSONOnQueue(mappingQueue)
.tryMapObservableResult { resp, value in
let jsonArray = try cast(value) as [JSON]
let createFromList = jsonArray.map {
T.createFrom(map: Map(mappingType: .fromJSON, JSON: $0))
}
return Observable.zip(createFromList) { $0 }
.map { (resp, $0) }
}
}
internal func responseJSONOnQueue(_ queue: DispatchQueue) -> Observable<ServerResponse> {
let responseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
return responseResult(queue: queue, responseSerializer: responseSerializer)
.map { ServerResponse(response: $0.0, result: $0.1) }
.catchError {
switch $0 {
case let urlError as URLError:
switch urlError.code {
case .notConnectedToInternet, .timedOut:
throw RequestError.noConnection
default:
throw RequestError.network(error: urlError)
}
case let afError as AFError:
switch afError {
case .responseSerializationFailed, .responseValidationFailed:
throw RequestError.invalidResponse(error: afError)
default:
throw RequestError.network(error: afError)
}
default:
throw RequestError.network(error: $0)
}
}
}
}
private extension ObservableType where E == ServerResponse {
func tryMapResult<R>(_ transform: @escaping (E) throws -> R) -> Observable<R> {
return map {
do {
return try transform($0)
} catch {
throw RequestError.mapping(error: error, response: $0.1)
}
}
}
func tryMapObservableResult<R>(_ transform: @escaping (E) throws -> Observable<R>) -> Observable<R> {
return flatMap { response -> Observable<R> in
do {
return try transform(response)
.catchError {
throw RequestError.mapping(error: $0, response: response.result)
}
} catch {
throw RequestError.mapping(error: error, response: response.result)
}
}
}
}

View File

@ -0,0 +1,167 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
import RxSwift
import RxAlamofire
typealias ServerResponse = (HTTPURLResponse, Data)
public extension Reactive where Base: DataRequest {
/// Method that serializes response into target object
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Parameter decoder: JSONDecoder used to decode a decodable type
/// - Returns: Observable with HTTP URL Response and target object
func apiResponse<T: Decodable>(mappingQueue: DispatchQueue = .global(), decoder: JSONDecoder)
-> Observable<SessionManager.ModelResponse<T>> {
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
///
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func observableApiResponse<T: ObservableMappable>(mappingQueue: DispatchQueue = .global(), decoder: JSONDecoder)
-> Observable<SessionManager.ModelResponse<T>> {
response(onQueue: mappingQueue)
.tryMapObservableResult { response, value in
let json = try JSONSerialization.jsonObject(with: value, options: [])
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<SessionManager.DataResponse> {
response(onQueue: mappingQueue)
.map { $0 as SessionManager.DataResponse }
.catchAsRequestError(with: self.base)
}
private func response(onQueue queue: DispatchQueue) -> Observable<(HTTPURLResponse, Data)> {
responseResult(queue: queue, responseSerializer: DataResponseSerializer())
}
}
public extension ObservableType where Element == 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<Int>, url: String? = nil) -> Observable<Element> {
map { $0.validate(statusCode: statusCodes) }
.catchAsRequestError(url: url)
}
}
private extension ObservableType where Element == ServerResponse {
func tryMapResult<R>(_ transform: @escaping (Element) throws -> R) -> Observable<R> {
map {
do {
return try transform($0)
} catch {
throw RequestError.mapping(error: error,
response: $0.1,
url: $0.0.url?.absoluteString)
}
}
}
func tryMapObservableResult<R>(_ transform: @escaping (Element) throws -> Observable<R>) -> Observable<R> {
flatMap { response, result -> Observable<R> in
do {
return try transform((response, result))
.catch {
throw RequestError.mapping(error: $0,
response: result,
url: response.url?.absoluteString)
}
} catch {
throw RequestError.mapping(error: error,
response: result,
url: response.url?.absoluteString)
}
}
}
}
private extension ObservableType {
func catchAsRequestError(with request: DataRequest? = nil,
url: String? = nil) -> Observable<Element> {
self.catch { error in
let resultError: RequestError
let response = request?.data
switch error {
case let requestError as RequestError:
resultError = requestError
case let urlError as URLError:
switch urlError.code {
case .notConnectedToInternet:
resultError = .noConnection(url: url)
default:
resultError = .network(error: urlError, response: response, url: url)
}
case let afError as AFError:
switch afError {
case let .sessionTaskFailed(error):
switch error {
case let urlError as URLError where urlError.code == .notConnectedToInternet:
resultError = .noConnection(url: url)
default:
resultError = .network(error: error, response: response, url: url)
}
case .responseSerializationFailed, .responseValidationFailed:
resultError = .invalidResponse(error: afError, response: response, url: url)
default:
resultError = .network(error: afError, response: response, url: url)
}
default:
resultError = .network(error: error, response: response, url: url)
}
throw resultError
}
}
}

View File

@ -0,0 +1,202 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Alamofire
import RxSwift
import RxAlamofire
/// Enum that represents wrong usage of requset parameters
///
/// - getMethodForbidden: invalid usage of get method
/// - urlEncodingForbidden: invalid usage of URLEncoding
enum RequestUsageError: Error {
case getMethodForbidden
case urlEncodingForbidden
case unableToHandleQueryParams
}
public extension Reactive where Base: SessionManager {
/// Creates an observable of the `Request`.
///
/// - Parameters:
/// - method: Alamofire method object
/// - url: An object adopting `URLConvertible`
/// - parameters: An array of JSON objects containing all necessary options
/// - encoding: The kind of encoding used to process parameters
/// - headers: A dictionary containing all the additional headers
/// - Returns: An observable of the `Request`
func request(_ method: Alamofire.HTTPMethod,
_ url: URLConvertible,
parameters: [Any]? = nil,
encoding: JSONEncoding = .default,
headers: HTTPHeaders? = nil)
-> Observable<DataRequest> {
Observable.deferred {
guard method != .get else {
assertionFailure("Unable to pass array in get request")
throw RequestUsageError.getMethodForbidden
}
let urlRequest = try URLRequest(url: try url.asURL(), method: method, headers: headers)
let encodedUrlRequest = try encoding.encode(urlRequest, withJSONObject: parameters)
return self.request(urlRequest: encodedUrlRequest)
}
}
/// Method which executes request with given api parameters
///
/// - Parameters:
/// - requestParameters: api parameters to pass Alamofire
/// - additionalValidStatusCodes: set of additional valid status codes
/// - Returns: Observable with request
func apiRequest(requestParameters: ApiRequestParameters, additionalValidStatusCodes: Set<Int>) -> Observable<DataRequest> {
.deferred {
var url = try requestParameters.url.asURL()
if let queryItems = requestParameters.queryItems {
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return .error(RequestUsageError.unableToHandleQueryParams)
}
urlComponents.queryItems = queryItems
url = try urlComponents.asURL()
}
let requestObservable: Observable<DataRequest>
switch requestParameters.parameters {
case .dictionary(let parameters)?:
requestObservable = self.request(requestParameters.method,
url,
parameters: parameters,
encoding: requestParameters.encoding,
headers: requestParameters.headers)
case .array(let parameters)?:
guard let encoding = requestParameters.encoding as? JSONEncoding else {
assertionFailure("Invalid encoding type with array parameter")
return .error(RequestUsageError.urlEncodingForbidden)
}
requestObservable = self.request(requestParameters.method,
url,
parameters: parameters,
encoding: encoding,
headers: requestParameters.headers)
case .none:
requestObservable = self.request(requestParameters.method,
url,
parameters: nil as Parameters?,
encoding: requestParameters.encoding,
headers: requestParameters.headers)
}
return requestObservable
.validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes),
url: url.absoluteString)
}
}
/// Method that executes request and serializes response into target object
///
/// - 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<T: Decodable>(requestParameters: ApiRequestParameters,
additionalValidStatusCodes: Set<Int>,
decoder: JSONDecoder)
-> Observable<SessionManager.ModelResponse<T>> {
apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes)
.flatMap {
$0.rx.apiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder)
}
}
/// Method that executes request and serializes response into target object
///
/// - 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<T: ObservableMappable>(requestParameters: ApiRequestParameters,
additionalValidStatusCodes: Set<Int>,
decoder: JSONDecoder)
-> Observable<SessionManager.ModelResponse<T>> {
apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes)
.flatMap {
$0.rx.observableApiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder)
}
}
/// Method that executes request and returns data
///
/// - 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, additionalValidStatusCodes: Set<Int>)
-> Observable<SessionManager.DataResponse> {
apiRequest(requestParameters: requestParameters, additionalValidStatusCodes: additionalValidStatusCodes)
.flatMap {
$0.rx.dataApiResponse(mappingQueue: self.base.mappingQueue)
}
}
/// 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<T: Decodable>(requestParameters: ApiUploadRequestParameters,
additionalValidStatusCodes: Set<Int>,
decoder: JSONDecoder)
-> Observable<SessionManager.ModelResponse<T>> {
Observable.deferred {
let urlRequest = try URLRequest(url: requestParameters.url, method: .post, headers: requestParameters.headers)
let data = try requestParameters.formData.encode()
return self.upload(data, urlRequest: urlRequest)
.map { $0 as DataRequest }
.validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes),
url: try? requestParameters.url.asURL().absoluteString)
.flatMap {
$0.rx.apiResponse(mappingQueue: self.base.mappingQueue, decoder: decoder)
}
}
}
}

View File

@ -63,14 +63,12 @@ public extension Array where Element: Equatable {
let allValues = values.flatMap { $0 }
return filter { !allValues.contains($0) }
}
}
public extension Array {
// Subscript for safe access to element by index
subscript(safe index: Index) -> Element? {
return index < count ? self[index] : nil
(index < count && index >= 0) ? self[index] : nil
}
}

View File

@ -0,0 +1,36 @@
//
// Copyright (c) 2018 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import TableKit
public extension Array where Element: TableKitViewModel {
/// Creates [Row] array from TableKitViewModels.
var tableRows: [Row] {
map { $0.tableRow }
}
/// Creates TableSection with empty, zero height header and footer.
var onlyRowsSection: TableSection {
TableSection(onlyRows: tableRows)
}
}

View File

@ -26,7 +26,7 @@ public extension Array where Element == SeparatorRowBox {
/// Create rows from SeparatorRowBox array
var rows: [Row] {
return map { $0.row }
map { $0.row }
}
/// Configure separators from SeparatorRowBox array
@ -35,18 +35,31 @@ public extension Array where Element == SeparatorRowBox {
func configureSeparators(extreme extremeSeparatorConfiguration: SeparatorConfiguration,
middle middleSeparatorConfiguration: SeparatorConfiguration) {
configureSeparators(first: extremeSeparatorConfiguration,
middle: middleSeparatorConfiguration,
last: extremeSeparatorConfiguration)
}
/// Configure separators from SeparatorRowBox array
/// - parameter first: Configuration of the top separator of the first row
/// - parameter middle: Configuration of the separators between the rows
/// - parameter last: Configuration of the bottom separator of the last row
func configureSeparators(first firstSeparatorConfiguration: SeparatorConfiguration,
middle middleSeparatorConfiguration: SeparatorConfiguration,
last lastSeparatorConfiguration: SeparatorConfiguration) {
if isEmpty {
return
}
switch count {
case 1:
first?.set(separatorType: .full(extremeSeparatorConfiguration, extremeSeparatorConfiguration))
first?.set(separatorType: .full(firstSeparatorConfiguration, lastSeparatorConfiguration))
default:
forEach { $0.set(separatorType: .full(middleSeparatorConfiguration, middleSeparatorConfiguration))}
first?.set(separatorType: .top(extremeSeparatorConfiguration))
last?.set(separatorType: .bottom(extremeSeparatorConfiguration))
forEach { $0.set(separatorType: .bottom(middleSeparatorConfiguration)) }
first?.set(separatorType: .full(firstSeparatorConfiguration, middleSeparatorConfiguration))
last?.set(separatorType: .bottom(lastSeparatorConfiguration))
}
}
}

View File

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

View File

@ -27,7 +27,7 @@ extension CABasicAnimation {
static let rotationKeyPath = "transform.rotation.z"
static func zRotationAnimationWith(duration: CFTimeInterval = 1,
repeatCount: Float = Float.infinity,
repeatCount: Float = .infinity,
clockwise: Bool = true) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: CABasicAnimation.rotationKeyPath)
@ -36,8 +36,10 @@ extension CABasicAnimation {
animation.duration = duration
animation.isCumulative = true
animation.repeatCount = repeatCount
if repeatCount == .infinity {
animation.isRemovedOnCompletion = false
}
return animation
}
}

Some files were not shown because too many files have changed in this diff Show More