Compare commits
242 Commits
feature/sw
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
3d57f5ccc4 | |
|
|
e8780d0238 | |
|
|
b7862cb891 | |
|
|
e8ca5ad039 | |
|
|
8fe2bfd6e8 | |
|
|
18e5421e14 | |
|
|
5549854c75 | |
|
|
af7c5bc9fd | |
|
|
6a7af59ffc | |
|
|
36b4f84306 | |
|
|
5c7df9373d | |
|
|
5c44471dbb | |
|
|
7a7987239d | |
|
|
b65029056f | |
|
|
815d749bd9 | |
|
|
03059969b2 | |
|
|
1a9ed8353b | |
|
|
03aa25e529 | |
|
|
48a2b47428 | |
|
|
d06c1c35d4 | |
|
|
9fab98b560 | |
|
|
94cf900f7c | |
|
|
c1e96eee35 | |
|
|
ed8a2113c4 | |
|
|
8652795ddb | |
|
|
38dc604cbc | |
|
|
abe9ad5dc1 | |
|
|
abd18d848c | |
|
|
4a076b8865 | |
|
|
767c19d17b | |
|
|
90cd941eff | |
|
|
2bf1fc052a | |
|
|
a03fc1f7ee | |
|
|
7945aa3a62 | |
|
|
93b0f61b00 | |
|
|
a0c7faa4a3 | |
|
|
60734996f6 | |
|
|
eaea4abd75 | |
|
|
b8611321fb | |
|
|
13a5925443 | |
|
|
c485434f51 | |
|
|
843a887ec7 | |
|
|
8007532351 | |
|
|
0ef1edfacb | |
|
|
27d5a3a9ca | |
|
|
4e8f60543d | |
|
|
c2b31a90d6 | |
|
|
1a4c42fa46 | |
|
|
094c0c40d8 | |
|
|
b22fd239e9 | |
|
|
65ae079e62 | |
|
|
7c8a29a7f9 | |
|
|
df2faa4cd5 | |
|
|
36019f7429 | |
|
|
5245d48a8a | |
|
|
83655d2bac | |
|
|
6b7be340f5 | |
|
|
6084dd5fec | |
|
|
85b206bf18 | |
|
|
da527644a7 | |
|
|
45c060403f | |
|
|
a79ff67a38 | |
|
|
c631053131 | |
|
|
c55b8f73a9 | |
|
|
25c0d04d11 | |
|
|
b97ea5bc8f | |
|
|
06a83190ab | |
|
|
3f112d2d26 | |
|
|
808d40eca5 | |
|
|
46ecd6970f | |
|
|
0776c99e38 | |
|
|
a06f4952b9 | |
|
|
77559babdb | |
|
|
86413b3bb4 | |
|
|
9056b2fe8c | |
|
|
0e45bb462d | |
|
|
e8b026302e | |
|
|
818d4dbe8d | |
|
|
86fddafcdf | |
|
|
919423ecda | |
|
|
c06bb56964 | |
|
|
b141dc5a45 | |
|
|
1e3b986c83 | |
|
|
2ea88a94aa | |
|
|
1be28959bc | |
|
|
bf613b99e8 | |
|
|
23c17c9d85 | |
|
|
f50bb09ad8 | |
|
|
005d80c531 | |
|
|
33cc31b957 | |
|
|
19a319f03c | |
|
|
193a060cff | |
|
|
a5bc2dc8f0 | |
|
|
5a74c342d9 | |
|
|
5bb3092726 | |
|
|
83a3e5b491 | |
|
|
7b9e8b0885 | |
|
|
529277d098 | |
|
|
ecfb83bafa | |
|
|
c0189dc7ae | |
|
|
a0401bc9fa | |
|
|
43a12e322f | |
|
|
5ca564476a | |
|
|
0e0a8d733e | |
|
|
7fd33b6157 | |
|
|
4f0c9a8ed1 | |
|
|
e7517c23f8 | |
|
|
975435bb90 | |
|
|
fd0365a370 | |
|
|
4ea76a8499 | |
|
|
6358386303 | |
|
|
dd4c9072a9 | |
|
|
5aff5f99bb | |
|
|
4fe395d295 | |
|
|
a925f70c04 | |
|
|
41d18bf3a6 | |
|
|
fc6a735d94 | |
|
|
77ce6a1c95 | |
|
|
cf54d8c798 | |
|
|
93a8ee68f3 | |
|
|
5a14c61a9a | |
|
|
4a9355fd80 | |
|
|
45e2b9ff19 | |
|
|
26866427a9 | |
|
|
26a7578dc8 | |
|
|
0612370587 | |
|
|
fe80863656 | |
|
|
3dd0c34d27 | |
|
|
56527b6dba | |
|
|
af044aa591 | |
|
|
c442ee2623 | |
|
|
3ccadd07b4 | |
|
|
9a9b57df4b | |
|
|
06b687c47e | |
|
|
a99c29ea73 | |
|
|
270ac1a4d5 | |
|
|
22b133648b | |
|
|
2245765b27 | |
|
|
30479ae8b8 | |
|
|
8d253d1458 | |
|
|
dcc9d23676 | |
|
|
d9e4ea5ae8 | |
|
|
775f95a931 | |
|
|
39bad32c49 | |
|
|
05a6236425 | |
|
|
7f45ce0594 | |
|
|
c407dabdf5 | |
|
|
29d7a6ca65 | |
|
|
2d0819064a | |
|
|
76da4ab223 | |
|
|
66508d505d | |
|
|
c8985cde1e | |
|
|
1bc200034c | |
|
|
cde2420f7d | |
|
|
026700b4c7 | |
|
|
4ccebe8e8b | |
|
|
f3ed27e83f | |
|
|
a234943394 | |
|
|
1374c4df1f | |
|
|
318fd40f0b | |
|
|
5ebb97de4c | |
|
|
507fc8fa05 | |
|
|
1932262ad5 | |
|
|
2c23e86852 | |
|
|
164edf9a5d | |
|
|
2f71b10dc0 | |
|
|
9d99d4e4e3 | |
|
|
9cc412208c | |
|
|
a8fc13ff1e | |
|
|
22fc660e56 | |
|
|
5d2bea19fb | |
|
|
e2c9c6c102 | |
|
|
332e895659 | |
|
|
9dca21afd9 | |
|
|
511c2b9653 | |
|
|
2b1511657d | |
|
|
f91453a065 | |
|
|
23b74ec3d5 | |
|
|
4c973b393d | |
|
|
0090c83f87 | |
|
|
144ea7b703 | |
|
|
278e175f3a | |
|
|
6af9a64135 | |
|
|
efde6153a8 | |
|
|
e3ae781f1d | |
|
|
23c2cbacea | |
|
|
40aa2876d1 | |
|
|
2250b1b4d9 | |
|
|
19bb08aa66 | |
|
|
597755474c | |
|
|
cb29a3e9ca | |
|
|
eafb434c88 | |
|
|
cfd5d5f2f8 | |
|
|
9c8510af14 | |
|
|
e942d08503 | |
|
|
2c081d508f | |
|
|
f01644b408 | |
|
|
b533eaaae6 | |
|
|
9db353b360 | |
|
|
e4c84ca511 | |
|
|
ce2f3ca064 | |
|
|
cbd38b84e2 | |
|
|
c8a4b0bd51 | |
|
|
7a0747843a | |
|
|
343d36cb85 | |
|
|
29347d77e1 | |
|
|
64604bdce0 | |
|
|
e27c844f92 | |
|
|
01d99cb246 | |
|
|
48f6655efc | |
|
|
bc9ac01463 | |
|
|
dda06ece3c | |
|
|
5ea58f3746 | |
|
|
2061050c78 | |
|
|
e199cd4220 | |
|
|
6e30957fe5 | |
|
|
84e7093903 | |
|
|
1da2d4d501 | |
|
|
118bca1c9d | |
|
|
fbab4a491b | |
|
|
fd2fa45909 | |
|
|
e471bae469 | |
|
|
698243ee39 | |
|
|
59ef1093c7 | |
|
|
7442884856 | |
|
|
6e506aa385 | |
|
|
9f5d7387d7 | |
|
|
e9b32ce326 | |
|
|
a9a8ddde9e | |
|
|
893f6f191d | |
|
|
56d1ee998d | |
|
|
55fe6b7126 | |
|
|
5f7e0bf273 | |
|
|
3a321a7fbf | |
|
|
7765c01074 | |
|
|
407995db35 | |
|
|
63777fef99 | |
|
|
caeded9561 | |
|
|
3d5aa7a41d | |
|
|
f885183499 | |
|
|
aff54859eb | |
|
|
a7e44a3d9a |
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "build-scripts"]
|
||||
path = build-scripts
|
||||
url = https://github.com/TouchInstinct/BuildScripts.git
|
||||
url = https://git.svc.touchin.ru/TouchInstinct/BuildScripts.git
|
||||
|
|
|
|||
184
CHANGELOG.md
184
CHANGELOG.md
|
|
@ -1,5 +1,189 @@
|
|||
# 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
|
||||
|
|
|
|||
|
|
@ -89,9 +89,10 @@ GEM
|
|||
|
||||
PLATFORMS
|
||||
x86_64-darwin-20
|
||||
x86_64-darwin-21
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (~> 1.11)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.10
|
||||
2.3.26
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ Pod::Spec.new do |s|
|
|||
s.name = "LeadKit"
|
||||
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.source = { :git => "https://git.svc.touchin.ru/TouchInstinct/LeadKit.git", :tag => s.version }
|
||||
s.platform = :ios, '10.0'
|
||||
s.swift_versions = ['5.1']
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
168
Package.resolved
168
Package.resolved
|
|
@ -1,79 +1,95 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Alamofire",
|
||||
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f96b619bcb2383b43d898402283924b80e2c4bae",
|
||||
"version": "5.4.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Cache",
|
||||
"repositoryURL": "https://github.com/hyperoslo/Cache.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c7f4d633049c3bd649a353bad36f6c17e9df085f",
|
||||
"version": "6.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Cursors",
|
||||
"repositoryURL": "https://github.com/petropavel13/Cursors",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "a1561869135e72832eff3b1e729075c56c2eebf6",
|
||||
"version": "0.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "KeychainAccess",
|
||||
"repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||
"version": "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Moya",
|
||||
"repositoryURL": "https://github.com/Moya/Moya.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "9b906860e3c3c09032879465c471e6375829593f",
|
||||
"version": "15.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "ReactiveSwift",
|
||||
"repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c43bae3dac73fdd3cb906bd5a1914686ca71ed3c",
|
||||
"version": "6.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "RxSwift",
|
||||
"repositoryURL": "https://github.com/ReactiveX/RxSwift.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "b4307ba0b6425c0ba4178e138799946c3da594f8",
|
||||
"version": "6.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "TableKit",
|
||||
"repositoryURL": "https://github.com/maxsokolov/TableKit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8bf4840d9d0475a92352f02f368f88b74eced447",
|
||||
"version": "2.11.0"
|
||||
}
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||
"state" : {
|
||||
"revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
|
||||
"version" : "5.7.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 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
|
||||
}
|
||||
|
|
|
|||
158
Package.swift
158
Package.swift
|
|
@ -1,27 +1,38 @@
|
|||
// swift-tools-version:5.1
|
||||
// swift-tools-version:5.7
|
||||
|
||||
#if canImport(PackageDescription)
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "LeadKit",
|
||||
platforms: [
|
||||
.iOS(.v11)
|
||||
.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: "TILogging", targets: ["TILogging"]),
|
||||
.library(name: "TIDeeplink", targets: ["TIDeeplink"]),
|
||||
.library(name: "TIDeveloperUtils", targets: ["TIDeveloperUtils"]),
|
||||
|
||||
// MARK: - Networking
|
||||
|
|
@ -34,63 +45,154 @@ let package = Package(
|
|||
|
||||
.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"]),
|
||||
|
||||
//MARK: - Skolkovo
|
||||
.library(name: "TIEcommerce", targets: ["TIEcommerce"])
|
||||
.library(name: "TIEcommerce", targets: ["TIEcommerce"]),
|
||||
.library(name: "TITextProcessing", targets: ["TITextProcessing"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/maxsokolov/TableKit.git", .upToNextMajor(from: "2.11.0")),
|
||||
.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/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", "TISwiftUtils"], path: "TIUIElements/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"),
|
||||
|
||||
|
||||
.target(name: "TISwiftUICore",
|
||||
dependencies: ["TIUIKitCore", "TISwiftUtils"],
|
||||
path: "TISwiftUICore/Sources"),
|
||||
|
||||
// MARK: - Utils
|
||||
.target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"),
|
||||
.target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils"),
|
||||
.target(name: "TIKeychainUtils", dependencies: ["TIFoundationUtils", "KeychainAccess"], path: "TIKeychainUtils/Sources"),
|
||||
|
||||
.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: "TILogging", dependencies: ["TIUIElements", "TISwiftUtils", "TIUIKitCore"], path: "TILogging/Sources"),
|
||||
.target(name: "TIDeveloperUtils", dependencies: [], path: "TIDeveloperUtils/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"], path: "TINetworking/Sources"),
|
||||
.target(name: "TIMoyaNetworking", dependencies: ["TINetworking", "TIFoundationUtils", "Moya"], path: "TIMoyaNetworking"),
|
||||
.target(name: "TINetworkingCache", dependencies: ["TIFoundationUtils", "TINetworking", "Cache"], path: "TINetworkingCache/Sources"),
|
||||
|
||||
.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: [], path: "TIMapUtils/Sources"),
|
||||
.target(name: "TIAppleMapUtils", dependencies: ["TIMapUtils"], path: "TIAppleMapUtils/Sources"),
|
||||
|
||||
|
||||
.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: ["TIFoundationUtils", "TIUIKitCore", "KeychainAccess"], path: "TIAuth/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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
]
|
||||
}
|
||||
}
|
||||
88
README.md
88
README.md
|
|
@ -20,8 +20,86 @@ This repository contains the following frameworks:
|
|||
- [TIYandexMapUtils](TIYandexMapUtils) - set of helpers for map objects clustering and interacting using Yandex Maps SDK.
|
||||
- [TIAuth](TIAuth) - login, registration, confirmation and other related actions
|
||||
|
||||
Useful docs:
|
||||
## 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.
|
||||
|
||||
|
|
@ -32,7 +110,7 @@ Useful docs:
|
|||
./setup
|
||||
```
|
||||
|
||||
- If legacy [Source](https://github.com/TouchInstinct/LeadKit/tree/master/Sources) folder needed, [build dependencies for LeadKit.xcodeproj](https://github.com/TouchInstinct/LeadKit/blob/master/docs/snippets.md#build-dependencies-for-LeadKit.xcodeproj).
|
||||
- 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).
|
||||
|
||||
|
|
@ -42,14 +120,14 @@ Useful docs:
|
|||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/TouchInstinct/LeadKit.git", from: "x.y.z"),
|
||||
.package(url: "https://git.svc.touchin.ru/TouchInstinct/LeadKit.git", from: "x.y.z"),
|
||||
],
|
||||
```
|
||||
|
||||
### Cocoapods
|
||||
|
||||
```ruby
|
||||
source 'https://github.com/TouchInstinct/Podspecs.git'
|
||||
source 'https://git.svc.touchin.ru/TouchInstinct/Podspecs.git'
|
||||
|
||||
pod 'TISwiftUtils', 'x.y.z'
|
||||
pod 'TIFoundationUtils', 'x.y.z'
|
||||
|
|
@ -58,4 +136,4 @@ 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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ import TIMapUtils
|
|||
import MapKit
|
||||
import UIKit
|
||||
|
||||
open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, [ApplePlacemarkManager<Model>], MKMapRect>, MKMapViewDelegate {
|
||||
open class AppleClusterPlacemarkManager<Model>: BaseClusterPlacemarkManager<MKAnnotationView,
|
||||
ApplePlacemarkManager<Model>,
|
||||
MKMapRect>,
|
||||
MKMapViewDelegate {
|
||||
|
||||
public weak var mapViewDelegate: MKMapViewDelegate?
|
||||
|
||||
private let mapDelegateSelectors = NSObject.instanceMethodSelectors(of: MKMapViewDelegate.self)
|
||||
|
|
@ -36,7 +40,8 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
|
||||
self.mapViewDelegate = mapViewDelegate
|
||||
|
||||
super.init(dataModel: placemarkManagers,
|
||||
super.init(placemarkPosition: .from(coordinates: placemarkManagers.map(\.placemarkPosition)),
|
||||
dataModel: placemarkManagers,
|
||||
iconFactory: iconFactory?.asAnyMarkerIconFactory { $0.map { $0.dataModel } },
|
||||
tapHandler: tapHandler)
|
||||
}
|
||||
|
|
@ -55,10 +60,10 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
override open func configure(placemark: MKAnnotationView) {
|
||||
guard let clusterAnnotation = placemark.annotation as? MKClusterAnnotation,
|
||||
let placemarkManagers = clusterAnnotation.memberAnnotations as? [ApplePlacemarkManager<Model>] else {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
placemark.image = iconFactory?.markerIcon(for: placemarkManagers)
|
||||
placemark.image = iconFactory?.markerIcon(for: placemarkManagers, state: .default)
|
||||
}
|
||||
|
||||
// MARK: - MKMapViewDelegate
|
||||
|
|
@ -79,6 +84,7 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
configure(placemark: defaultAnnotationView)
|
||||
|
||||
return defaultAnnotationView
|
||||
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
let defaultAnnotationView = placemarkManager.iconFactory != nil
|
||||
? MKAnnotationView(annotation: annotation,
|
||||
|
|
@ -89,6 +95,7 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
placemarkManager.configure(placemark: defaultAnnotationView)
|
||||
|
||||
return defaultAnnotationView
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
@ -107,8 +114,29 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
}
|
||||
|
||||
_ = tapHandler?(placemarkManagers, .from(coordinates: placemarkManagers.map { $0.coordinate }))
|
||||
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
_ = placemarkManager.tapHandler?(placemarkManager.dataModel, placemarkManager.coordinate)
|
||||
let isTapHandled = placemarkManager.tapHandler?(placemarkManager.dataModel, placemarkManager.coordinate) ?? false
|
||||
|
||||
if isTapHandled {
|
||||
placemarkManager.state = .selected
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
open func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
|
||||
guard !(mapViewDelegate?.responds(to: #selector(mapView(_:didDeselect:))) ?? false) else {
|
||||
mapViewDelegate?.mapView?(mapView, didDeselect: view)
|
||||
return
|
||||
}
|
||||
|
||||
switch view.annotation {
|
||||
case let placemarkManager as ApplePlacemarkManager<Model>:
|
||||
placemarkManager.state = .default
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ import TIMapUtils
|
|||
import MapKit
|
||||
|
||||
open class AppleMapManager<DataModel>: BaseMapManager<MKMapView,
|
||||
ApplePlacemarkManager<DataModel>,
|
||||
AppleClusterPlacemarkManager<DataModel>,
|
||||
MKCameraUpdate> {
|
||||
ApplePlacemarkManager<DataModel>,
|
||||
AppleClusterPlacemarkManager<DataModel>,
|
||||
MKCameraUpdate,
|
||||
AppleMapUISettings> {
|
||||
|
||||
public typealias ClusteringIdentifier = String
|
||||
|
||||
|
|
@ -44,7 +45,8 @@ open class AppleMapManager<DataModel>: BaseMapManager<MKMapView,
|
|||
return nil
|
||||
}
|
||||
|
||||
return ApplePlacemarkManager(dataModel: $0,
|
||||
return ApplePlacemarkManager(map: map,
|
||||
dataModel: $0,
|
||||
position: position,
|
||||
clusteringIdentifier: clusteringIdentifierGetter($0),
|
||||
iconFactory: iconFactory?.asAnyMarkerIconFactory(),
|
||||
|
|
@ -65,27 +67,18 @@ open class AppleMapManager<DataModel>: BaseMapManager<MKMapView,
|
|||
}
|
||||
|
||||
public convenience init(map: MKMapView,
|
||||
positionGetter: @escaping PositionGetter,
|
||||
clusteringIdentifierGetter: @escaping (DataModel) -> ClusteringIdentifier,
|
||||
iconFactory: DefaultMarkerIconFactory<DataModel>? = nil,
|
||||
clusterIconFactory: DefaultClusterMarkerIconFactory<DataModel>? = nil,
|
||||
mapViewDelegate: MKMapViewDelegate? = nil,
|
||||
selectPlacemarkHandler: @escaping SelectPlacemarkHandler) {
|
||||
|
||||
self.init(map: map,
|
||||
positionGetter: positionGetter,
|
||||
clusteringIdentifierGetter: clusteringIdentifierGetter,
|
||||
iconFactory: nil as DefaultMarkerIconFactory<DataModel>?,
|
||||
clusterIconFactory: nil as DefaultClusterMarkerIconFactory<DataModel>?,
|
||||
mapViewDelegate: mapViewDelegate,
|
||||
selectPlacemarkHandler: selectPlacemarkHandler)
|
||||
}
|
||||
|
||||
public convenience init(map: MKMapView,
|
||||
mapViewDelegate: MKMapViewDelegate? = nil,
|
||||
selectPlacemarkHandler: @escaping SelectPlacemarkHandler) where DataModel: MapLocatable & Clusterable, DataModel.ClusterIdentifier == ClusteringIdentifier, DataModel.Position == CLLocationCoordinate2D {
|
||||
selectPlacemarkHandler: @escaping SelectPlacemarkHandler)
|
||||
where DataModel: MapLocatable, DataModel.Position == CLLocationCoordinate2D,
|
||||
DataModel: Clusterable, DataModel.ClusterIdentifier == ClusteringIdentifier {
|
||||
|
||||
self.init(map: map,
|
||||
positionGetter: { $0.position },
|
||||
clusteringIdentifierGetter: { $0.clusterIdentifier },
|
||||
iconFactory: iconFactory,
|
||||
clusterIconFactory: clusterIconFactory,
|
||||
mapViewDelegate: mapViewDelegate,
|
||||
selectPlacemarkHandler: selectPlacemarkHandler)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// 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 TIMapUtils
|
||||
import MapKit
|
||||
|
||||
open class AppleMapUISettings: BaseMapUISettings<MKMapView> {
|
||||
open class Defaults: BaseMapUISettings<MKMapView>.Defaults {
|
||||
public static var showCompassButton: Bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
public var showCompassButton = false
|
||||
|
||||
public init(showUserLocation: Bool = Defaults.showUserLocation,
|
||||
isZoomEnabled: Bool = Defaults.isZoomEnabled,
|
||||
isTiltEnabled: Bool = Defaults.isTiltEnabled,
|
||||
isRotationEnabled: Bool = Defaults.isRotationEnabled,
|
||||
showCompassButton: Bool = Defaults.showCompassButton) {
|
||||
|
||||
self.showCompassButton = showCompassButton
|
||||
|
||||
super.init(showUserLocation: showUserLocation,
|
||||
isZoomEnabled: isZoomEnabled,
|
||||
isTiltEnabled: isTiltEnabled,
|
||||
isRotationEnabled: isRotationEnabled)
|
||||
}
|
||||
|
||||
override open func apply(to mapView: MKMapView) {
|
||||
super.apply(to: mapView)
|
||||
|
||||
mapView.showsUserLocation = showUserLocation
|
||||
mapView.isZoomEnabled = isZoomEnabled
|
||||
mapView.isPitchEnabled = isTiltEnabled
|
||||
mapView.isRotateEnabled = isRotationEnabled
|
||||
mapView.showsCompass = showCompassButton
|
||||
}
|
||||
}
|
||||
|
|
@ -23,23 +23,58 @@
|
|||
import TIMapUtils
|
||||
import MapKit
|
||||
|
||||
open class ApplePlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, Model, CLLocationCoordinate2D>, MKAnnotation {
|
||||
open class ApplePlacemarkManager<Model>: BaseItemPlacemarkManager<MKAnnotationView, Model, CLLocationCoordinate2D>,
|
||||
MKAnnotation {
|
||||
|
||||
// MARK: - MKAnnotation
|
||||
|
||||
/// A map where all placemarks are placed
|
||||
public let map: MKMapView
|
||||
|
||||
public let coordinate: CLLocationCoordinate2D
|
||||
|
||||
/// Identifier required for correct cluster placement
|
||||
public var clusteringIdentifier: String?
|
||||
|
||||
/// Point (coordinates) itself of the current placemark manager
|
||||
public var coordinate: CLLocationCoordinate2D {
|
||||
placemarkPosition
|
||||
}
|
||||
|
||||
/// The current state of a manager's placemark
|
||||
override public var state: MarkerState {
|
||||
didSet {
|
||||
guard let placemark = placemark else {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Although the icon is being updated, it is necessary to manually deselect
|
||||
the annotation of the current placemark if it is currently selected.
|
||||
*/
|
||||
if let annotation = placemark.annotation {
|
||||
switch state {
|
||||
case .default:
|
||||
map.deselectAnnotation(annotation, animated: true)
|
||||
case .selected:
|
||||
map.selectAnnotation(annotation, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
placemark.image = iconFactory?.markerIcon(for: dataModel, state: state)
|
||||
}
|
||||
}
|
||||
|
||||
public init(dataModel: Model,
|
||||
public init(map: MKMapView,
|
||||
dataModel: Model,
|
||||
position: CLLocationCoordinate2D,
|
||||
clusteringIdentifier: String?,
|
||||
iconFactory: AnyMarkerIconFactory<DataModel>?,
|
||||
tapHandler: TapHandlerClosure?) {
|
||||
|
||||
self.coordinate = position
|
||||
self.map = map
|
||||
self.clusteringIdentifier = clusteringIdentifier
|
||||
|
||||
super.init(dataModel: dataModel,
|
||||
super.init(placemarkPosition: position,
|
||||
dataModel: dataModel,
|
||||
iconFactory: iconFactory,
|
||||
tapHandler: tapHandler)
|
||||
}
|
||||
|
|
@ -47,7 +82,10 @@ open class ApplePlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView,
|
|||
// MARK: - PlacemarkManager
|
||||
|
||||
override open func configure(placemark: MKAnnotationView) {
|
||||
placemark.image = iconFactory?.markerIcon(for: dataModel)
|
||||
placemark.clusteringIdentifier = clusteringIdentifier
|
||||
super.configure(placemark: placemark)
|
||||
|
||||
// Setting required values of the current manager and placemark respectively
|
||||
self.placemark?.clusteringIdentifier = clusteringIdentifier
|
||||
self.state = .default
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.35.0'
|
||||
s.version = '1.56.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
sources = 'Sources/**/*'
|
||||
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
|
||||
|
||||
s.dependency 'TIMapUtils', s.version.to_s
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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 Foundation
|
||||
|
||||
public struct BundleIdentifier {
|
||||
public let appPrefix: String
|
||||
public let appIdentifier: String
|
||||
public let fullIdentifier: String
|
||||
public let defaultAppGroupIdenfier: String
|
||||
|
||||
static let bundleIdentifierSeparator = "."
|
||||
|
||||
public init(appPrefix: String, appIdentifier: String) {
|
||||
self.appPrefix = appPrefix
|
||||
self.appIdentifier = appIdentifier
|
||||
self.fullIdentifier = appPrefix + Self.bundleIdentifierSeparator + appIdentifier
|
||||
self.defaultAppGroupIdenfier = "group." + fullIdentifier
|
||||
}
|
||||
|
||||
public init?(bundle: Bundle = .main) {
|
||||
guard let fullIdenfifier = bundle.bundleIdentifier,
|
||||
var components = bundle.bundleIdentifier?
|
||||
.components(separatedBy: Self.bundleIdentifierSeparator),
|
||||
let lastComponent = components.popLast() else {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
self.fullIdentifier = fullIdenfifier
|
||||
self.appIdentifier = lastComponent
|
||||
self.appPrefix = components.joined(separator: Self.bundleIdentifierSeparator)
|
||||
self.defaultAppGroupIdenfier = "group." + fullIdentifier
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
//
|
||||
// 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 Foundation
|
||||
import TIFoundationUtils
|
||||
import KeychainAccess
|
||||
import TILogging
|
||||
import UIKit
|
||||
|
||||
public struct CoreDependencies {
|
||||
public var dateFormattersResusePool = DateFormattersReusePool()
|
||||
public var iso8601DateFormattersReusePool = ISO8601DateFormattersReusePool()
|
||||
public var jsonCodingConfigurator: JsonCodingConfigurator
|
||||
public var jsonKeyValueDecoder: JSONKeyValueDecoder
|
||||
public var jsonKeyValueEncoder: JSONKeyValueEncoder
|
||||
|
||||
public var device: UIDevice = .current
|
||||
|
||||
public var bundle: Bundle = .main
|
||||
public var fileManager: FileManager = .default
|
||||
|
||||
public var logger: DefaultOSLogErrorLogger
|
||||
|
||||
public var keychain: Keychain
|
||||
public var defaults: UserDefaults
|
||||
|
||||
public var appGroupDefaults: UserDefaults?
|
||||
public var appGroupKeychain: Keychain?
|
||||
|
||||
public var appGroupCacheDirectory: URL?
|
||||
|
||||
public var networkCallbackQueue: DispatchQueue
|
||||
|
||||
public init(bundleIdentifier: BundleIdentifier,
|
||||
customAppGroupIdentifier: String? = nil) {
|
||||
|
||||
jsonCodingConfigurator = JsonCodingConfigurator(dateFormattersReusePool: dateFormattersResusePool,
|
||||
iso8601DateFormattersReusePool: iso8601DateFormattersReusePool)
|
||||
|
||||
jsonKeyValueDecoder = JSONKeyValueDecoder(jsonDecoder: jsonCodingConfigurator.jsonDecoder)
|
||||
jsonKeyValueEncoder = JSONKeyValueEncoder(jsonEncoder: jsonCodingConfigurator.jsonEncoder)
|
||||
|
||||
logger = DefaultOSLogErrorLogger(subsystem: bundleIdentifier.fullIdentifier, category: "general")
|
||||
|
||||
keychain = Keychain(service: bundleIdentifier.fullIdentifier).accessibility(.whenUnlockedThisDeviceOnly)
|
||||
defaults = .standard
|
||||
|
||||
let appGroupIdentifier = customAppGroupIdentifier ?? bundleIdentifier.defaultAppGroupIdenfier
|
||||
|
||||
if let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) {
|
||||
var appGroupCacheURL: URL
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
appGroupCacheURL = containerURL.appending(path: "Caches", directoryHint: .isDirectory)
|
||||
} else {
|
||||
appGroupCacheURL = containerURL.appendingPathComponent("Caches", isDirectory: true)
|
||||
}
|
||||
|
||||
var resourceValues = URLResourceValues()
|
||||
resourceValues.isExcludedFromBackup = true
|
||||
|
||||
do {
|
||||
try fileManager.createDirectory(at: appGroupCacheURL,
|
||||
withIntermediateDirectories: true)
|
||||
|
||||
try appGroupCacheURL.setResourceValues(resourceValues)
|
||||
|
||||
appGroupCacheDirectory = appGroupCacheURL
|
||||
} catch {
|
||||
logger.log(error: error, file: #file, line: #line)
|
||||
}
|
||||
|
||||
appGroupDefaults = UserDefaults(suiteName: appGroupIdentifier)
|
||||
appGroupKeychain = Keychain(service: bundleIdentifier.appPrefix,
|
||||
accessGroup: appGroupIdentifier)
|
||||
.accessibility(.whenUnlockedThisDeviceOnly)
|
||||
} else {
|
||||
appGroupCacheDirectory = nil
|
||||
appGroupDefaults = nil
|
||||
appGroupKeychain = nil
|
||||
}
|
||||
|
||||
networkCallbackQueue = DispatchQueue(label: bundleIdentifier.fullIdentifier + ".network-callback-queue",
|
||||
attributes: .concurrent)
|
||||
}
|
||||
|
||||
public init?(bundle: Bundle = .main,
|
||||
customAppGroupIdentifier: String? = nil) {
|
||||
|
||||
guard let bundleIdentifier = BundleIdentifier(bundle: bundle) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(bundleIdentifier: bundleIdentifier,
|
||||
customAppGroupIdentifier: customAppGroupIdentifier)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
// 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
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol FingerprintsSettingsStorage {
|
||||
/// Should be true by default (on app first run)
|
||||
var shouldResetFingerprints: Bool { get set }
|
||||
public protocol TargetDependencies {
|
||||
static func assemble() -> Self
|
||||
static func assembleForPreview() -> Self
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIApplication'
|
||||
s.version = '1.56.0'
|
||||
s.summary = 'Application architecture.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = 'Sources/**/*'
|
||||
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
|
||||
|
||||
s.dependency 'TIFoundationUtils', s.version.to_s
|
||||
s.dependency 'TILogging', s.version.to_s
|
||||
s.dependency 'KeychainAccess', "~> 4.2"
|
||||
end
|
||||
|
|
@ -22,12 +22,13 @@
|
|||
|
||||
import KeychainAccess
|
||||
import Foundation
|
||||
import TIFoundationUtils
|
||||
import LocalAuthentication
|
||||
|
||||
open class DefaultEncryptedTokenKeyStorage: SingleValueAuthKeychainStorage<Data> {
|
||||
open class Defaults: SingleValueAuthKeychainStorage<StringEncryptionResult>.Defaults {
|
||||
public static var encryptedTokenKeyStorageKey: String {
|
||||
keychainServiceIdentifier + ".encryptedTokenKey"
|
||||
public static var encryptedTokenKeyStorageKey: StorageKey<Data> {
|
||||
.init(rawValue: keychainServiceIdentifier + ".encryptedTokenKey")
|
||||
}
|
||||
|
||||
public static var reusableLAContext: LAContext {
|
||||
|
|
@ -39,12 +40,12 @@ open class DefaultEncryptedTokenKeyStorage: SingleValueAuthKeychainStorage<Data>
|
|||
|
||||
public init(keychain: Keychain = Keychain(service: Defaults.keychainServiceIdentifier),
|
||||
localAuthContext: LAContext = Defaults.reusableLAContext,
|
||||
settingsStorage: AuthSettingsStorage = DefaultAuthSettingsStorage(),
|
||||
encryptedTokenKeyStorageKey: String = Defaults.encryptedTokenKeyStorageKey) {
|
||||
encryptedTokenKeyStorageKey: StorageKey<Data> = Defaults.encryptedTokenKeyStorageKey,
|
||||
appFirstRunCheckStorage: BoolValueDefaultsStorage = DefaultResetAuthSettingsStorage()) {
|
||||
|
||||
let getValueClosure: GetValueClosure = { keychain, storageKey in
|
||||
do {
|
||||
guard let value = try keychain.getData(storageKey) else {
|
||||
guard let value = try keychain.getData(storageKey.rawValue) else {
|
||||
return .failure(.valueNotFound)
|
||||
}
|
||||
|
||||
|
|
@ -54,18 +55,18 @@ open class DefaultEncryptedTokenKeyStorage: SingleValueAuthKeychainStorage<Data>
|
|||
}
|
||||
}
|
||||
|
||||
let setValueClosure: SetValueClosure = { keychain, value, storageKey in
|
||||
let storeValueClosure: StoreValueClosure = { keychain, value, storageKey in
|
||||
do {
|
||||
return .success(try keychain.set(value, key: storageKey))
|
||||
return .success(try keychain.set(value, key: storageKey.rawValue))
|
||||
} catch {
|
||||
return .failure(.unableToWriteData(underlyingError: error))
|
||||
}
|
||||
}
|
||||
|
||||
super.init(keychain: keychain.authenticationContext(localAuthContext),
|
||||
settingsStorage: settingsStorage,
|
||||
storageKey: encryptedTokenKeyStorageKey,
|
||||
getValueClosure: getValueClosure,
|
||||
setValueClosure: setValueClosure)
|
||||
storeValueClosure: storeValueClosure,
|
||||
appFirstRunCheckStorage: appFirstRunCheckStorage)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,22 +21,23 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import TIFoundationUtils
|
||||
import KeychainAccess
|
||||
|
||||
open class DefaultEncryptedTokenStorage: SingleValueAuthKeychainStorage<StringEncryptionResult> {
|
||||
open class Defaults: SingleValueAuthKeychainStorage<StringEncryptionResult>.Defaults {
|
||||
public static var encryptedTokenStorageKey: String {
|
||||
keychainServiceIdentifier + ".encryptedToken"
|
||||
public static var encryptedTokenStorageKey: StorageKey<StringEncryptionResult> {
|
||||
.init(rawValue: keychainServiceIdentifier + ".encryptedToken")
|
||||
}
|
||||
}
|
||||
|
||||
public init(keychain: Keychain = Keychain(service: Defaults.keychainServiceIdentifier),
|
||||
settingsStorage: AuthSettingsStorage = DefaultAuthSettingsStorage(),
|
||||
encryptedTokenStorageKey: String = Defaults.encryptedTokenStorageKey) {
|
||||
encryptedTokenStorageKey: StorageKey<StringEncryptionResult> = Defaults.encryptedTokenStorageKey,
|
||||
appFirstRunCheckStorage: BoolValueDefaultsStorage = DefaultResetAuthSettingsStorage()) {
|
||||
|
||||
let getValueClosure: GetValueClosure = { keychain, storageKey in
|
||||
do {
|
||||
guard let value = try keychain.getData(storageKey) else {
|
||||
guard let value = try keychain.getData(storageKey.rawValue) else {
|
||||
return .failure(.valueNotFound)
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +51,18 @@ open class DefaultEncryptedTokenStorage: SingleValueAuthKeychainStorage<StringEn
|
|||
}
|
||||
}
|
||||
|
||||
let setValueClosure: SetValueClosure = { keychain, value, storageKey in
|
||||
let storeValueClosure: StoreValueClosure = { keychain, value, storageKey in
|
||||
do {
|
||||
return .success(try keychain.set(value.asStorableData(), key: storageKey))
|
||||
return .success(try keychain.set(value.asStorableData(), key: storageKey.rawValue))
|
||||
} catch {
|
||||
return .failure(.unableToWriteData(underlyingError: error))
|
||||
}
|
||||
}
|
||||
|
||||
super.init(keychain: keychain,
|
||||
settingsStorage: settingsStorage,
|
||||
storageKey: encryptedTokenStorageKey,
|
||||
getValueClosure: getValueClosure,
|
||||
setValueClosure: setValueClosure)
|
||||
storeValueClosure: storeValueClosure,
|
||||
appFirstRunCheckStorage: appFirstRunCheckStorage)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 TIFoundationUtils
|
||||
import Foundation
|
||||
|
||||
open class DefaultResetAuthSettingsStorage: DefaultAppFirstRunCheckStorage {
|
||||
public enum Defaults {
|
||||
public static var shouldResetAuthDataKey: StorageKey<Bool> {
|
||||
.init(rawValue: "shouldResetAuthData")
|
||||
}
|
||||
}
|
||||
|
||||
public override init(defaults: UserDefaults = .standard,
|
||||
storageKey: StorageKey<Bool> = Defaults.shouldResetAuthDataKey) {
|
||||
|
||||
super.init(defaults: defaults, storageKey: storageKey)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,62 +23,31 @@
|
|||
import TIFoundationUtils
|
||||
import KeychainAccess
|
||||
import Foundation
|
||||
import TIKeychainUtils
|
||||
|
||||
open class SingleValueAuthKeychainStorage<ValueType>:
|
||||
AppInstallLifetimeSingleValueStorage<BaseSingleValueKeychainStorage<ValueType>, BoolValueDefaultsStorage> {
|
||||
|
||||
public typealias GetValueClosure = BaseSingleValueKeychainStorage<ValueType>.GetValueClosure
|
||||
public typealias StoreValueClosure = BaseSingleValueKeychainStorage<ValueType>.StoreValueClosure
|
||||
|
||||
open class SingleValueAuthKeychainStorage<ValueType>: SingleValueStorage {
|
||||
open class Defaults {
|
||||
public static var keychainServiceIdentifier: String {
|
||||
Bundle.main.bundleIdentifier ?? "ru.touchin.TIAuth"
|
||||
}
|
||||
}
|
||||
|
||||
public typealias GetValueClosure = (Keychain, String) -> Result<ValueType, StorageError>
|
||||
public typealias SetValueClosure = (Keychain, ValueType, String) -> Result<Void, StorageError>
|
||||
|
||||
public let keychain: Keychain
|
||||
public let settingsStorage: AuthSettingsStorage
|
||||
public let storageKey: String
|
||||
public let getValueClosure: GetValueClosure
|
||||
public let setValueClosure: SetValueClosure
|
||||
|
||||
public init(keychain: Keychain = Keychain(service: Defaults.keychainServiceIdentifier),
|
||||
settingsStorage: AuthSettingsStorage = DefaultAuthSettingsStorage(),
|
||||
storageKey: String,
|
||||
storageKey: StorageKey<ValueType>,
|
||||
getValueClosure: @escaping GetValueClosure,
|
||||
setValueClosure: @escaping SetValueClosure) {
|
||||
storeValueClosure: @escaping StoreValueClosure,
|
||||
appFirstRunCheckStorage: BoolValueDefaultsStorage = DefaultResetAuthSettingsStorage()) {
|
||||
|
||||
self.keychain = keychain
|
||||
self.settingsStorage = settingsStorage
|
||||
self.storageKey = storageKey
|
||||
self.getValueClosure = getValueClosure
|
||||
self.setValueClosure = setValueClosure
|
||||
}
|
||||
let keychainStorage = BaseSingleValueKeychainStorage(keychain: keychain,
|
||||
storageKey: storageKey,
|
||||
getValueClosure: getValueClosure,
|
||||
storeValueClosure: storeValueClosure)
|
||||
|
||||
// MARK: - SingleValueStorage
|
||||
|
||||
open func hasStoredValue() -> Bool {
|
||||
!settingsStorage.shouldResetStoredAuthData && ((try? keychain.contains(storageKey)) ?? false)
|
||||
}
|
||||
|
||||
open func store(value: ValueType) -> Result<Void, StorageError> {
|
||||
return setValueClosure(keychain, value, storageKey)
|
||||
}
|
||||
|
||||
open func getValue() -> Result<ValueType, StorageError> {
|
||||
guard !settingsStorage.shouldResetStoredAuthData else {
|
||||
let result: Result<ValueType, StorageError>
|
||||
|
||||
do {
|
||||
try keychain.remove(storageKey)
|
||||
settingsStorage.shouldResetStoredAuthData = false
|
||||
|
||||
result = .failure(.valueNotFound)
|
||||
} catch {
|
||||
result = .failure(.unableToWriteData(underlyingError: error))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return getValueClosure(keychain, storageKey)
|
||||
super.init(storage: keychainStorage, appFirstRunCheckStorage: appFirstRunCheckStorage)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAuth'
|
||||
s.version = '1.35.0'
|
||||
s.version = '1.56.0'
|
||||
s.summary = 'Login, registration, confirmation and other related actions'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '13.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
sources = 'Sources/**/*'
|
||||
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
|
||||
|
||||
s.dependency 'TIFoundationUtils', s.version.to_s
|
||||
s.dependency 'TIKeychainUtils', s.version.to_s
|
||||
s.dependency 'TIUIKitCore', s.version.to_s
|
||||
s.dependency 'KeychainAccess', "~> 4.2"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
source 'https://git.svc.touchin.ru/TouchInstinct/Podspecs.git'
|
||||
|
||||
target 'TIBottomSheet' do
|
||||
platform :ios, 11
|
||||
use_frameworks!
|
||||
|
||||
pod 'TIUIElements', :path => '../../../../TIUIElements/TIUIElements.podspec'
|
||||
pod 'TIUIKitCore', :path => '../../../../TIUIKitCore/TIUIKitCore.podspec'
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIBottomSheet', :path => '../../../../TIBottomSheet/TIBottomSheet.podspec'
|
||||
pod 'TILogging', :path => '../../../../TILogging/TILogging.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// 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 TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
extension BaseModalViewController {
|
||||
|
||||
open class BaseAppearance: UIView.BaseAppearance<UIView.DefaultWrappedLayout> {
|
||||
|
||||
public var presentationDetents: [ModalViewPresentationDetent]
|
||||
public var dragViewState: DragView.State
|
||||
public var headerViewState: ModalHeaderView.State
|
||||
public var footerViewState: ModalFooterView<FooterContentView>.State
|
||||
|
||||
public init(layout: UIView.DefaultWrappedLayout = .defaultLayout,
|
||||
background: UIViewBackground = UIViewColorBackground(color: .clear),
|
||||
border: UIViewBorder = BaseUIViewBorder(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
presentationDetents: [ModalViewPresentationDetent] = [.maxHeight],
|
||||
dragViewState: DragView.State = .hidden,
|
||||
headerViewState: ModalHeaderView.State = .hidden,
|
||||
footerViewState: ModalFooterView<FooterContentView>.State = .hidden) {
|
||||
|
||||
self.presentationDetents = presentationDetents
|
||||
self.dragViewState = dragViewState
|
||||
self.headerViewState = headerViewState
|
||||
self.footerViewState = footerViewState
|
||||
|
||||
super.init(layout: layout,
|
||||
background: background,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
|
||||
public final class DefaultAppearance: BaseAppearance, ViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,473 @@
|
|||
//
|
||||
// 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 TISwiftUtils
|
||||
import TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
import PanModal
|
||||
|
||||
open class BaseModalViewController<ContentView: UIView,
|
||||
FooterContentView: UIView>: BaseInitializableViewController, PanModalPresentable {
|
||||
|
||||
// MARK: - Public Properties
|
||||
|
||||
public let dragView = DragView()
|
||||
public let headerView = ModalHeaderView()
|
||||
private(set) public lazy var contentView = createContentView()
|
||||
private(set) public lazy var footerView = createFooterView()
|
||||
|
||||
public private(set) lazy var dragViewBottomToHeaderViewTopConstraint: NSLayoutConstraint = {
|
||||
dragView.bottomAnchor.constraint(equalTo: headerView.topAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var dragViewBottomToContentViewTopConstraint: NSLayoutConstraint = {
|
||||
dragView.bottomAnchor.constraint(equalTo: contentView.topAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var dragViewConstraints: SubviewConstraints = {
|
||||
let trailingConstraint = dragView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: dragView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
trailingConstraint: trailingConstraint,
|
||||
topConstraint: dragView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
bottomConstraint: dragViewBottomToHeaderViewTopConstraint)
|
||||
|
||||
let centerXConstraint = dragView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
|
||||
let centerYConstraint = dragView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: dragView.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: dragView.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
public private(set) lazy var headerViewToSuperviewTopConstraint: NSLayoutConstraint = {
|
||||
headerView.topAnchor.constraint(equalTo: view.topAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var headerBottomToContentTopConstraint: NSLayoutConstraint = {
|
||||
headerView.bottomAnchor.constraint(equalTo: contentView.topAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var headerViewConstraints: SubviewConstraints = {
|
||||
let leadingConstraint = headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
|
||||
let trailingConstraint = headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
|
||||
trailingConstraint: trailingConstraint,
|
||||
topConstraint: dragViewBottomToHeaderViewTopConstraint,
|
||||
bottomConstraint: headerBottomToContentTopConstraint)
|
||||
|
||||
let centerXConstraint = headerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
|
||||
let centerYConstraint = headerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: headerView.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: headerView.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
public private(set) lazy var contentViewTopToSuperviewConstraint: NSLayoutConstraint = {
|
||||
contentView.topAnchor.constraint(equalTo: view.topAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var contentViewBottomToSuperviewConstraint: NSLayoutConstraint = {
|
||||
contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var contentViewConstraints: SubviewConstraints = {
|
||||
let leadingConstraint = contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
|
||||
let trailingConstraint = contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
|
||||
trailingConstraint: trailingConstraint,
|
||||
topConstraint: headerBottomToContentTopConstraint,
|
||||
bottomConstraint: contentViewBottomToSuperviewConstraint)
|
||||
|
||||
let centerXConstraint = contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
|
||||
let centerYConstraint = contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: contentView.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: contentView.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
public private(set) lazy var footerViewConstraints: SubviewConstraints = {
|
||||
let leadingConstraint = footerView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
|
||||
let trailingConstraint = footerView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
|
||||
trailingConstraint: trailingConstraint,
|
||||
topConstraint: footerView.topAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
bottomConstraint: footerView.bottomAnchor.constraint(equalTo: view.bottomAnchor))
|
||||
|
||||
let centerXConstraint = footerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
|
||||
let centerYConstraint = footerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: footerView.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: footerView.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
// MARK: - Modal View Controller Configuration
|
||||
|
||||
public var viewControllerAppearance: BaseAppearance = .init(background: UIViewColorBackground(color: .white))
|
||||
|
||||
open var panScrollable: UIScrollView? {
|
||||
contentView as? UIScrollView
|
||||
}
|
||||
|
||||
public var panScrollableInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
panScrollable?.contentInset = panScrollableInsets
|
||||
}
|
||||
}
|
||||
|
||||
public var dimmedView = DimmedView()
|
||||
|
||||
public var showDragIndicator = true
|
||||
|
||||
open var headerViewHeight: CGFloat {
|
||||
let dragViewHeight = getHeight(of: dragView)
|
||||
let headerViewHeight = getHeight(of: headerView)
|
||||
let dragViewVerticalInsets = getDragViewVerticalInsets()
|
||||
let headerViewVerticalInsets = getHeaderViewVerticalInsets()
|
||||
|
||||
return dragViewHeight + headerViewHeight + dragViewVerticalInsets + headerViewVerticalInsets
|
||||
}
|
||||
|
||||
open var longFormHeight: PanModalHeight {
|
||||
let detents = getSortedDetents()
|
||||
|
||||
return detents.max()?.panModalHeight(headerHeight: headerViewHeight) ?? .maxHeight
|
||||
}
|
||||
|
||||
open var mediumFormHeight: PanModalHeight {
|
||||
let detents = getSortedDetents()
|
||||
|
||||
if detents.count > 1 {
|
||||
return detents[1].panModalHeight(headerHeight: headerViewHeight)
|
||||
}
|
||||
|
||||
return detents.first?.panModalHeight(headerHeight: headerViewHeight) ?? .maxHeight
|
||||
}
|
||||
|
||||
open var shortFormHeight: PanModalHeight {
|
||||
let detents = getSortedDetents()
|
||||
|
||||
return detents.min()?.panModalHeight(headerHeight: headerViewHeight) ?? .maxHeight
|
||||
}
|
||||
|
||||
private var keyboardDidShownObserver: NSObjectProtocol?
|
||||
private var keyboardDidHiddenObserver: NSObjectProtocol?
|
||||
|
||||
// MARK: - Life Cycle
|
||||
|
||||
deinit {
|
||||
let notificationCenter = NotificationCenter.default
|
||||
|
||||
if let keyboardDidShownObserver {
|
||||
notificationCenter.removeObserver(keyboardDidShownObserver)
|
||||
}
|
||||
|
||||
if let keyboardDidHiddenObserver {
|
||||
notificationCenter.removeObserver(keyboardDidHiddenObserver)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BaseInitializableViewController
|
||||
|
||||
open override func addViews() {
|
||||
super.addViews()
|
||||
|
||||
view.addSubviews(dragView, headerView, contentView, footerView)
|
||||
}
|
||||
|
||||
open override func configureLayout() {
|
||||
super.configureLayout()
|
||||
|
||||
for view in [dragView, headerView, contentView, footerView] {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
configureDragViewLayout()
|
||||
configureHeaderViewLayout()
|
||||
configureContentViewLayout()
|
||||
configureFooterViewLayout()
|
||||
}
|
||||
|
||||
open override func bindViews() {
|
||||
super.bindViews()
|
||||
|
||||
keyboardDidShownObserver = NotificationCenter.default
|
||||
.addObserver(forName: UIResponder.keyboardDidShowNotification,
|
||||
object: nil,
|
||||
queue: .main) { [weak self] notification in
|
||||
self?.configureLayoutForKeyboard(notification, isKeyboardHidden: false)
|
||||
}
|
||||
|
||||
keyboardDidHiddenObserver = NotificationCenter.default
|
||||
.addObserver(forName: UIResponder.keyboardDidHideNotification,
|
||||
object: nil,
|
||||
queue: .main) { [weak self] notification in
|
||||
self?.configureLayoutForKeyboard(notification, isKeyboardHidden: true)
|
||||
}
|
||||
}
|
||||
|
||||
open override func configureAppearance() {
|
||||
super.configureAppearance()
|
||||
|
||||
view.configureUIView(appearance: viewControllerAppearance)
|
||||
configureDragViewAppearance()
|
||||
configureHeaderViewAppearance()
|
||||
configureFooterViewAppearance()
|
||||
}
|
||||
|
||||
// MARK: - Open Methods
|
||||
|
||||
open func createContentView() -> ContentView {
|
||||
ContentView()
|
||||
}
|
||||
|
||||
open func createFooterView() -> ModalFooterView<FooterContentView> {
|
||||
ModalFooterView<FooterContentView>()
|
||||
}
|
||||
|
||||
open func configureLayoutForKeyboard(_ notification: Notification, isKeyboardHidden: Bool) {
|
||||
guard let keyboardHeight = getKeyboardHeight(notification) else {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState {
|
||||
let bottomInset = footerViewAppearance.layout.insets.add(\.bottom,
|
||||
to: \.bottom,
|
||||
of: .vertical(bottom: keyboardHeight))
|
||||
|
||||
footerViewConstraints.edgeConstraints.bottomConstraint.constant = bottomInset
|
||||
} else if let panScrollable {
|
||||
var insets = panScrollableInsets
|
||||
|
||||
if isKeyboardHidden {
|
||||
panScrollable.contentInset = insets
|
||||
} else {
|
||||
insets.bottom += keyboardHeight
|
||||
|
||||
panScrollable.contentInset = insets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
private func configureDragViewLayout() {
|
||||
guard case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState else {
|
||||
return
|
||||
}
|
||||
|
||||
let bottomConstraint: NSLayoutConstraint
|
||||
let bottomConstant: CGFloat
|
||||
|
||||
if case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState {
|
||||
dragViewBottomToContentViewTopConstraint.isActive = false
|
||||
bottomConstraint = dragViewBottomToHeaderViewTopConstraint
|
||||
bottomConstant = dragViewAppearance.layout.insets.add(\.bottom,
|
||||
to: \.top,
|
||||
of: headerViewAppearance.layout.insets)
|
||||
} else {
|
||||
dragViewBottomToHeaderViewTopConstraint.isActive = false
|
||||
bottomConstraint = dragViewBottomToContentViewTopConstraint
|
||||
bottomConstant = dragViewAppearance.layout.insets.bottom
|
||||
}
|
||||
|
||||
dragViewConstraints.edgeConstraints.bottomConstraint = bottomConstraint
|
||||
dragViewConstraints.update(from: dragViewAppearance.layout)
|
||||
|
||||
bottomConstraint.setActiveConstantOrDeactivate(constant: bottomConstant)
|
||||
}
|
||||
|
||||
private func configureHeaderViewLayout() {
|
||||
guard case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState else {
|
||||
return
|
||||
}
|
||||
|
||||
let topConstraint: NSLayoutConstraint
|
||||
let topConstant: CGFloat
|
||||
|
||||
if case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState {
|
||||
dragViewBottomToContentViewTopConstraint.isActive = false
|
||||
topConstraint = dragViewBottomToHeaderViewTopConstraint
|
||||
|
||||
topConstant = dragViewAppearance.layout.insets.add(\.bottom,
|
||||
to: \.top,
|
||||
of: headerViewAppearance.layout.insets)
|
||||
} else {
|
||||
dragViewBottomToHeaderViewTopConstraint.isActive = false
|
||||
topConstraint = headerViewToSuperviewTopConstraint
|
||||
|
||||
let topInset = headerViewAppearance.layout.insets.top
|
||||
topConstant = topInset.isFinite ? topInset : .zero
|
||||
}
|
||||
|
||||
headerViewConstraints.edgeConstraints.topConstraint = topConstraint
|
||||
headerViewConstraints.update(from: headerViewAppearance.layout)
|
||||
|
||||
topConstraint.setActiveConstantOrDeactivate(constant: topConstant)
|
||||
}
|
||||
|
||||
private func configureContentViewLayout() {
|
||||
let topConstraint: NSLayoutConstraint
|
||||
let topConstant: CGFloat
|
||||
|
||||
if case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState {
|
||||
dragViewBottomToContentViewTopConstraint.isActive = false
|
||||
contentViewTopToSuperviewConstraint.isActive = false
|
||||
|
||||
topConstraint = headerBottomToContentTopConstraint
|
||||
topConstant = headerViewAppearance.layout.insets.bottom
|
||||
} else if case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState {
|
||||
contentViewTopToSuperviewConstraint.isActive = false
|
||||
headerBottomToContentTopConstraint.isActive = false
|
||||
|
||||
topConstraint = dragViewBottomToContentViewTopConstraint
|
||||
topConstant = dragViewAppearance.layout.insets.bottom
|
||||
} else {
|
||||
headerBottomToContentTopConstraint.isActive = false
|
||||
dragViewBottomToContentViewTopConstraint.isActive = false
|
||||
|
||||
topConstraint = contentViewTopToSuperviewConstraint
|
||||
topConstant = .zero
|
||||
}
|
||||
|
||||
contentViewConstraints.edgeConstraints.topConstraint = topConstraint
|
||||
let layout = UIView.DefaultWrappedLayout(insets: .horizontal(.zero)
|
||||
.vertical(top: topConstant)
|
||||
.replacingNan(with: .zero))
|
||||
contentViewConstraints.update(from: layout)
|
||||
}
|
||||
|
||||
private func configureFooterViewLayout() {
|
||||
guard case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState else {
|
||||
return
|
||||
}
|
||||
|
||||
contentViewConstraints.edgeConstraints.bottomConstraint.isActive = false
|
||||
contentViewConstraints.edgeConstraints.bottomConstraint = footerViewConstraints.edgeConstraints.topConstraint
|
||||
|
||||
footerViewConstraints.update(from: footerViewAppearance.layout)
|
||||
}
|
||||
|
||||
private func configureDragViewAppearance() {
|
||||
switch viewControllerAppearance.dragViewState {
|
||||
case .hidden:
|
||||
dragView.isHidden = true
|
||||
|
||||
case let .presented(appearance):
|
||||
dragView.configure(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
||||
private func configureHeaderViewAppearance() {
|
||||
switch viewControllerAppearance.headerViewState {
|
||||
case .hidden:
|
||||
headerView.isHidden = true
|
||||
|
||||
case let .presented(appearance):
|
||||
headerView.configure(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
||||
private func configureFooterViewAppearance() {
|
||||
switch viewControllerAppearance.footerViewState {
|
||||
case .hidden:
|
||||
footerView.isHidden = true
|
||||
|
||||
case let .presented(appearance):
|
||||
footerView.configureBaseWrappedViewHolder(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
||||
private func getSortedDetents() -> [ModalViewPresentationDetent] {
|
||||
viewControllerAppearance.presentationDetents.uniqued().sorted()
|
||||
}
|
||||
|
||||
private func getHeight(of view: UIView) -> CGFloat {
|
||||
guard !view.isHidden else {
|
||||
return .zero
|
||||
}
|
||||
|
||||
return getFittingSize(forView: view).height
|
||||
}
|
||||
|
||||
private func getDragViewVerticalInsets() -> CGFloat {
|
||||
guard case let .presented(appearance) = viewControllerAppearance.dragViewState else {
|
||||
return .zero
|
||||
}
|
||||
|
||||
return appearance.layout.insets.vertical(onNan: .zero)
|
||||
}
|
||||
|
||||
private func getHeaderViewVerticalInsets() -> CGFloat {
|
||||
guard case let .presented(appearance) = viewControllerAppearance.headerViewState else {
|
||||
return .zero
|
||||
}
|
||||
|
||||
return appearance.layout.insets.vertical(onNan: .zero)
|
||||
}
|
||||
|
||||
private func getFittingSize(forView view: UIView) -> CGSize {
|
||||
let targetSize = CGSize(width: UIScreen.main.bounds.width,
|
||||
height: UIView.layoutFittingCompressedSize.height)
|
||||
|
||||
return view.systemLayoutSizeFitting(targetSize)
|
||||
}
|
||||
|
||||
private func getKeyboardHeight(_ notification: Notification) -> CGFloat? {
|
||||
guard let userInfo = notification.userInfo else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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 UIKit
|
||||
|
||||
open class DefaultModalWrapperViewController<ContentController: UIViewController>: BaseModalViewController<UIView, UIView> {
|
||||
|
||||
private(set) public var contentViewController: ContentController
|
||||
|
||||
public init(contentViewController: ContentController) {
|
||||
self.contentViewController = contentViewController
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
addChild(contentViewController)
|
||||
contentViewController.didMove(toParent: self)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required public init?(coder: NSCoder) {
|
||||
contentViewController = ContentController()
|
||||
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
open override func createContentView() -> UIView {
|
||||
contentViewController.view
|
||||
}
|
||||
}
|
||||
|
||||
public extension UIViewController {
|
||||
func wrappedInBottomSheetController() -> DefaultModalWrapperViewController<UIViewController> {
|
||||
DefaultModalWrapperViewController(contentViewController: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// 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 TIUIKitCore
|
||||
|
||||
extension UIViewShadow {
|
||||
public static var defaultModalViewShadow: Self {
|
||||
.init {
|
||||
Color(.black)
|
||||
Opacity(0.5)
|
||||
Offset(0, -5)
|
||||
Radius(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// Copyright (c) 2022 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 PanModal
|
||||
|
||||
public struct ModalViewPresentationDetent: Hashable {
|
||||
|
||||
// MARK: - Default Values
|
||||
|
||||
public static var headerOnly: Self {
|
||||
Self(height: -.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
public static func height(_ height: CGFloat) -> Self {
|
||||
Self(height: height)
|
||||
}
|
||||
|
||||
public static var maxHeight: Self {
|
||||
Self(height: .greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
// MARK: - Public Properties
|
||||
|
||||
public var height: CGFloat
|
||||
|
||||
// MARK: - Internal Methods
|
||||
|
||||
func panModalHeight(headerHeight: CGFloat = .zero) -> PanModalHeight {
|
||||
if self == .headerOnly {
|
||||
return .contentHeight(headerHeight)
|
||||
}
|
||||
|
||||
if self == .maxHeight {
|
||||
return .maxHeight
|
||||
}
|
||||
|
||||
return .contentHeight(height)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Comparable
|
||||
|
||||
extension ModalViewPresentationDetent: Comparable {
|
||||
public static func < (lhs: ModalViewPresentationDetent, rhs: ModalViewPresentationDetent) -> Bool {
|
||||
lhs.height < rhs.height
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// 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 TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
public final class DragView: BaseInitializableView, AppearanceConfigurable {
|
||||
|
||||
// MARK: - Nested Types
|
||||
|
||||
private enum Constants {
|
||||
static var dragViewTopInset: CGFloat {
|
||||
8
|
||||
}
|
||||
|
||||
static var dragViewBorder: UIViewBorder {
|
||||
UIViewRoundedBorder(cornerRadius: dragViewSize.height / 2)
|
||||
}
|
||||
|
||||
static var dragViewBackground: UIViewColorBackground {
|
||||
UIViewColorBackground(color: .lightGray)
|
||||
}
|
||||
|
||||
static var dragViewSize: CGSize {
|
||||
CGSize(width: 52, height: 7)
|
||||
}
|
||||
}
|
||||
|
||||
public enum State {
|
||||
case hidden
|
||||
case presented(Appearance)
|
||||
}
|
||||
|
||||
public final class Appearance: UIView.BaseWrappedAppearance<UIView.DefaultWrappedLayout>, WrappedViewAppearance {
|
||||
|
||||
public static var defaultAppearance: Self {
|
||||
Self(layout: DefaultWrappedLayout(insets: .vertical(top: Constants.dragViewTopInset),
|
||||
size: Constants.dragViewSize,
|
||||
centerOffset: .centerHorizontal()),
|
||||
background: Constants.dragViewBackground,
|
||||
border: Constants.dragViewBorder)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppearanceConfigurable
|
||||
|
||||
public func configure(appearance: Appearance) {
|
||||
configureUIView(appearance: appearance)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// 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 TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
open class ModalFooterView<ContentView: UIView>: ContainerView<ContentView> {
|
||||
// MARK: - Nested Types
|
||||
|
||||
public enum State {
|
||||
case hidden
|
||||
case presented(Appearance)
|
||||
}
|
||||
}
|
||||
|
||||
public extension ModalFooterView {
|
||||
|
||||
final class Appearance: BaseWrappedViewHolderAppearance<DefaultWrappedAppearance, DefaultWrappedLayout>,
|
||||
WrappedViewHolderAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self(layout: DefaultWrappedLayout(insets: .horizontal(.zero).vertical(bottom: .zero),
|
||||
size: .fixedHeight(44)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
//
|
||||
// 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 TIUIElements
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
|
||||
|
||||
// MARK: - Nested Types
|
||||
|
||||
public enum State {
|
||||
case hidden
|
||||
case presented(Appearance)
|
||||
}
|
||||
|
||||
public enum ContentViewState {
|
||||
case leadingButton(BaseButtonStyle)
|
||||
case trailingButton(BaseButtonStyle)
|
||||
case buttons(leading: BaseButtonStyle, trailing: BaseButtonStyle)
|
||||
case custom(view: UIView, appearance: UIView.BaseWrappedAppearance<UIView.DefaultWrappedLayout>)
|
||||
}
|
||||
|
||||
// MARK: - Public properties
|
||||
|
||||
public let leadingButton = StatefulButton()
|
||||
public let trailingButton = StatefulButton()
|
||||
|
||||
public private(set) lazy var leftTrailingToRightLeadingConstraint: NSLayoutConstraint = {
|
||||
leadingButton.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var leftTrailingToSuperviewTrailing: NSLayoutConstraint = {
|
||||
leadingButton.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var leadingButtonConstraints: SubviewConstraints = {
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingButton.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingConstraint: leftTrailingToRightLeadingConstraint,
|
||||
topConstraint: leadingButton.topAnchor.constraint(equalTo: topAnchor),
|
||||
bottomConstraint: leadingButton.bottomAnchor.constraint(equalTo: bottomAnchor))
|
||||
|
||||
let centerXConstraint = leadingButton.centerXAnchor.constraint(equalTo: centerXAnchor)
|
||||
let centerYConstraint = leadingButton.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: leadingButton.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: leadingButton.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
public private(set) lazy var rightLeadingToSuperviewLeadingConstraint: NSLayoutConstraint = {
|
||||
trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor)
|
||||
}()
|
||||
|
||||
public private(set) lazy var trailingButtonConstraints: SubviewConstraints = {
|
||||
let trailingConstraint = trailingButton.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: leftTrailingToRightLeadingConstraint,
|
||||
trailingConstraint: trailingConstraint,
|
||||
topConstraint: trailingButton.topAnchor.constraint(equalTo: topAnchor),
|
||||
bottomConstraint: trailingButton.bottomAnchor.constraint(equalTo: bottomAnchor))
|
||||
|
||||
let centerXConstraint = trailingButton.centerXAnchor.constraint(equalTo: centerXAnchor)
|
||||
let centerYConstraint = trailingButton.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
||||
centerYConstraint: centerYConstraint)
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: trailingButton.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: trailingButton.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
}()
|
||||
|
||||
public var customViewConstraints: SubviewConstraints?
|
||||
|
||||
// MARK: - BaseInitializableView
|
||||
|
||||
open override func addViews() {
|
||||
super.addViews()
|
||||
|
||||
addSubviews(leadingButton, trailingButton)
|
||||
}
|
||||
|
||||
open override func configureLayout() {
|
||||
super.configureLayout()
|
||||
|
||||
for view in [leadingButton, trailingButton] {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppearanceConfigurable
|
||||
|
||||
open func configure(appearance: Appearance) {
|
||||
configureUIView(appearance: appearance)
|
||||
|
||||
configureContentView(state: appearance.contentViewState)
|
||||
}
|
||||
|
||||
open func configureContentView(state: ContentViewState) {
|
||||
var leadingButtonStyle: BaseButtonStyle?
|
||||
var trailingButtonStyle: BaseButtonStyle?
|
||||
|
||||
switch state {
|
||||
case let .leadingButton(style):
|
||||
leadingButtonStyle = style
|
||||
leadingButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToSuperviewTrailing
|
||||
|
||||
case let .trailingButton(style):
|
||||
trailingButtonStyle = style
|
||||
trailingButtonConstraints.edgeConstraints.leadingConstraint = rightLeadingToSuperviewLeadingConstraint
|
||||
|
||||
case let .buttons(leading, trailing):
|
||||
leadingButtonStyle = leading
|
||||
trailingButtonStyle = trailing
|
||||
|
||||
case let .custom(view, appearance):
|
||||
configureCustomView(view, withLayout: appearance.layout)
|
||||
view.configureUIView(appearance: appearance)
|
||||
}
|
||||
|
||||
configure(buttonStyle: leadingButtonStyle, forButton: leadingButton, constraints: leadingButtonConstraints)
|
||||
configure(buttonStyle: trailingButtonStyle, forButton: trailingButton, constraints: trailingButtonConstraints)
|
||||
|
||||
if let leadingButtonStyle, let trailingButtonStyle {
|
||||
leadingButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToRightLeadingConstraint
|
||||
trailingButtonConstraints.edgeConstraints.leadingConstraint = leftTrailingToRightLeadingConstraint
|
||||
|
||||
let spacing = leadingButtonStyle.appearance.layout.insets.add(\.right,
|
||||
to: \.left,
|
||||
of: trailingButtonStyle.appearance.layout.insets)
|
||||
|
||||
leftTrailingToRightLeadingConstraint.setActiveConstantOrDeactivate(constant: spacing)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func configureCustomView(_ view: UIView, withLayout layout: WrappedViewLayout) {
|
||||
addSubview(view)
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let edgeConstraints = EdgeConstraints(leadingConstraint: view.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingConstraint: view.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
topConstraint: view.topAnchor.constraint(equalTo: topAnchor),
|
||||
bottomConstraint: view.bottomAnchor.constraint(equalTo: bottomAnchor))
|
||||
|
||||
let centerConstraints = CenterConstraints(centerXConstraint: view.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
centerYConstraint: view.centerYAnchor.constraint(equalTo: centerYAnchor))
|
||||
|
||||
let sizeConstraints = SizeConstraints(widthConstraint: view.widthAnchor.constraint(equalToConstant: .zero),
|
||||
heightConstraint: view.heightAnchor.constraint(equalToConstant: .zero))
|
||||
|
||||
let customViewConstraints = SubviewConstraints(edgeConstraints: edgeConstraints,
|
||||
centerConstraints: centerConstraints,
|
||||
sizeConstraints: sizeConstraints)
|
||||
|
||||
self.customViewConstraints = customViewConstraints
|
||||
|
||||
customViewConstraints.update(from: layout)
|
||||
}
|
||||
|
||||
private func configure(buttonStyle: BaseButtonStyle?,
|
||||
forButton button: StatefulButton,
|
||||
constraints: SubviewConstraints?) {
|
||||
|
||||
guard let buttonStyle else {
|
||||
button.isHidden = true
|
||||
return
|
||||
}
|
||||
|
||||
button.isHidden = false
|
||||
|
||||
constraints?.update(from: buttonStyle.appearance.layout)
|
||||
|
||||
button.apply(style: buttonStyle)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Appearance
|
||||
|
||||
public extension ModalHeaderView {
|
||||
|
||||
final class Appearance: BaseWrappedAppearance<DefaultWrappedLayout>, WrappedViewAppearance {
|
||||
public static var defaultAppearance: Self {
|
||||
Self(layout: DefaultWrappedLayout(insets: .horizontal(.zero).vertical(top: .zero),
|
||||
size: .fixedHeight(44)))
|
||||
}
|
||||
|
||||
public var contentViewState: ContentViewState
|
||||
|
||||
public init(layout: DefaultWrappedLayout = .defaultLayout,
|
||||
background: UIViewBackground = UIViewColorBackground(color: .clear),
|
||||
border: UIViewBorder = BaseUIViewBorder(),
|
||||
shadow: UIViewShadow? = nil,
|
||||
contentViewState: ContentViewState = .leadingButton(.init())) {
|
||||
|
||||
self.contentViewState = contentViewState
|
||||
|
||||
super.init(layout: layout,
|
||||
background: background,
|
||||
border: border,
|
||||
shadow: shadow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// 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 UIKit
|
||||
import PanModal
|
||||
|
||||
open class PassthroughDimmedView: DimmedView {
|
||||
public weak var hitTestHandlerView: UIView?
|
||||
|
||||
public var isTransparent = false
|
||||
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
guard let hitTestHandlerView else {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
return hitTestHandlerView.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
hitTestHandlerView == nil || super.point(inside: point, with: event)
|
||||
}
|
||||
|
||||
open override func onChange(dimState: DimmedView.DimState) {
|
||||
super.onChange(dimState: dimState)
|
||||
|
||||
if isTransparent {
|
||||
alpha = .zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>launcher</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fortysevendeg.nef</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 The nef Authors. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
||||
## User data
|
||||
**/xcuserdata/
|
||||
podfile.lock
|
||||
**.DS_Store
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## CocoaPods
|
||||
**Pods**
|
||||
|
||||
## Carthage
|
||||
**Carthage**
|
||||
|
||||
## SPM
|
||||
.build
|
||||
.swiftpm
|
||||
swiftpm
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
source 'https://git.svc.touchin.ru/TouchInstinct/Podspecs.git'
|
||||
|
||||
target 'TIBottomSheet' do
|
||||
platform :ios, 12
|
||||
use_frameworks!
|
||||
|
||||
pod 'TIUIElements', :path => '../../../../TIUIElements/TIUIElements.podspec'
|
||||
pod 'TIUIKitCore', :path => '../../../../TIUIKitCore/TIUIKitCore.podspec'
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIBottomSheet', :path => '../../../../TIBottomSheet/TIBottomSheet.podspec'
|
||||
pod 'TILogging', :path => '../../../../TILogging/TILogging.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*:
|
||||
# TIBottomSheet
|
||||
|
||||
TIBottomSheet содержить базовую реализацию модального котроллера и немного видоизмененную библиотеку PanModal.
|
||||
|
||||
## Базовый контроллер
|
||||
|
||||
Для создания модального котроллера можно унаследоваться от `BaseModalViewController`. Данный клас принимает два generic типа: тип основного контента, тип контента футера.
|
||||
*/
|
||||
import TIBottomSheet
|
||||
import UIKit
|
||||
|
||||
class EmptyViewController: BaseModalViewController<UIView, UIView> { }
|
||||
|
||||
/*:
|
||||
## Обертка вокруг существующего контроллера
|
||||
|
||||
Может быть такое, что из уже существующего контроллера нужно сделать модальное окно. С этим может помочь обертка `DefaultModalWrapperViewController`. Данный контроллер является наследником BaseModalViewController, что позволяет его настраивать так же, как и базовый модальный котроллер
|
||||
*/
|
||||
|
||||
import TIUIKitCore
|
||||
|
||||
final class OldMassiveViewController: BaseInitializableViewController {
|
||||
// some implementation
|
||||
}
|
||||
|
||||
typealias ModalOldMassiveViewController = DefaultModalWrapperViewController<OldMassiveViewController>
|
||||
|
||||
class PresentingViewController: BaseInitializableViewController {
|
||||
// some implementation
|
||||
|
||||
@objc private func onButtonTapped() {
|
||||
presentPanModal(ModalOldMassiveViewController(contentViewController: OldMassiveViewController()))
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
## Контент модального контроллера
|
||||
|
||||
Модальный котроллер может содержать следующие элементы: `DragView`, `HeaderView`, `FooterView`. Каждый из них является опциональным и без дополнительных настроек не будет показываться.
|
||||
|
||||
DragView - небольшая view, за которую пользователь "держит" модальный контроллер
|
||||
HeaderView - контейнер, содержащий в себе кнопки назад/закрыть или какие-то другие элементы управления
|
||||
FooterView - view, располагающаяся внизу контроллера, поверх всего контента (модальный контроллер уже настроен так, чтобы при скролле в самый низ, футер не перекрывал последнюю ячейку)
|
||||
|
||||
Для настройки каждого у котроллера есть свойство `viewControllerAppearance`. Через него будет настраиваться весь контроллер. Однако стоит заметить, что котроллер не будет настраивать передаваимую вью, содержащую основной контент. Стандартно котроллер будет пытаться расположить контент так, чтобы он заполнил все пространство.
|
||||
|
||||
Вот пример настройки внешнего вида так, чтобы был видет dragView и headerView с левой кнопкой:
|
||||
*/
|
||||
|
||||
import TIUIElements
|
||||
|
||||
let customViewController = BaseModalViewController<UIView, UIView>()
|
||||
customViewController.viewControllerAppearance = BaseModalViewController.DefaultAppearance.make {
|
||||
$0.dragViewState = .presented(.defaultAppearance)
|
||||
$0.headerViewState = .presented(.make {
|
||||
$0.layout.size = .fixedHeight(52)
|
||||
$0.backgroundColor = .white
|
||||
$0.contentViewState = .leadingButton(.init(titles: [.normal: "Close"],
|
||||
appearance: .init(stateAppearances: [
|
||||
.normal: .init(background: UIViewColorBackground(color: .blue))
|
||||
])))
|
||||
})
|
||||
}
|
||||
|
||||
/*:
|
||||
## "Якори" контроллера
|
||||
|
||||
Раньше для настройки высоты контроллера необходимо было пользоваться свойствами `longFormHeight`, `shortFormHeight`. В базовом контроллере можно лишь передать список точек на которых контроллер должен будет задержаться:
|
||||
*/
|
||||
|
||||
let detentsViewController = BaseModalViewController<UIView, UIView>()
|
||||
detentsViewController.viewControllerAppearance.presentationDetents = [.headerOnly, .height(300), .maxHeight]
|
||||
|
||||
/*:
|
||||
- headerOnly будет сам пытаться вычеслить высоту хедера и dragView, показывая только их
|
||||
- height(_) будет показывать контроллер на переданной высоте
|
||||
- maxHeight - вся высота экрана (до safeArea)
|
||||
|
||||
В данный массив не рекомендуется передавать больше 3 значений, т.к. модальное окно все равно сможет занять только 3 положения на экране.
|
||||
|
||||
## DimmedView и PassthroughDimmedView
|
||||
|
||||
Для контроля `DimmedView` (затемняющей view) есть отдельное свойство `dimmedView`. Эти классы позволяют настраивать поведение при тапе в затемнённую область и кастомизировать затемнение под ваши нужды.
|
||||
*/
|
||||
|
||||
let shadowViewController = BaseModalViewController<UIView, UIView>()
|
||||
let dimmedView = PassthroughDimmedView()
|
||||
dimmedView.hitTestHandlerView = shadowViewController.view
|
||||
dimmedView.configureUIView(appearance: UIView.DefaultAppearance(shadow: UIViewShadow(radius: 8,
|
||||
color: .black,
|
||||
opacity: 0.3)))
|
||||
|
||||
shadowViewController.dimmedView = dimmedView
|
||||
|
||||
/*:
|
||||
## Контроль закрытия
|
||||
|
||||
`PanModalPresentable` не умеет в настройку закрытия контроллера, делая это самостоятельно через `dismiss(animated:completion:)`. Теперь можно настроить закрытие самостоятельно через свойства: `onTapToDismiss` и `onDragToDismiss`.
|
||||
|
||||
# Взаимодействие с PanModal
|
||||
|
||||
Если нет необходимости или возможности использовать `BaseModalViewController`, вы все так же можете пользоваться протоколом `PanModalRepresentable`. Вот список изменений протокола:
|
||||
|
||||
- Открытие/закрытие модального окна теперь можно настроить с помощью свойств `onTapToDismiss` и `onDragToDismiss`
|
||||
- Можно настроить промежуточное состояние модального окна с `mediumFormHeight`
|
||||
- `DimmedView` открыт для наследования и может создаваться в `dimmedView` у
|
||||
|
||||
> Для `BaseModalViewController` все свойства из `PanModalPresentable` все также работают, т.е. вы можете их переопределять, добавлять и изменять по необходимости.
|
||||
*/
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import UIKit
|
||||
|
||||
public protocol NefPlaygroundLiveViewable {}
|
||||
extension UIView: NefPlaygroundLiveViewable {}
|
||||
extension UIViewController: NefPlaygroundLiveViewable {}
|
||||
|
||||
#if NOT_IN_PLAYGROUND
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {}
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
import PlaygroundSupport
|
||||
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {
|
||||
PlaygroundPage.current.liveView = (view as! PlaygroundLiveViewable)
|
||||
}
|
||||
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {
|
||||
PlaygroundPage.current.needsIndefiniteExecution = state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
|
||||
public extension Nef {
|
||||
|
||||
static func run<T: XCTestCase>(testCase class: T.Type) {
|
||||
startTestObserver()
|
||||
T.defaultTestSuite.run()
|
||||
}
|
||||
|
||||
static private func startTestObserver() {
|
||||
_ = testObserverInstalled
|
||||
}
|
||||
|
||||
static private var testObserverInstalled = { () -> NefTestFailObserver in
|
||||
let testObserver = NefTestFailObserver()
|
||||
XCTestObservationCenter.shared.addTestObserver(testObserver)
|
||||
return testObserver
|
||||
}()
|
||||
}
|
||||
|
||||
// MARK: enrich the output for XCTest
|
||||
fileprivate class NefTestFailObserver: NSObject, XCTestObservation {
|
||||
|
||||
private var numberOfFailedTests = 0
|
||||
|
||||
func testSuiteWillStart(_ testSuite: XCTestSuite) {
|
||||
numberOfFailedTests = 0
|
||||
}
|
||||
|
||||
func testSuiteDidFinish(_ testSuite: XCTestSuite) {
|
||||
if numberOfFailedTests > 0 {
|
||||
print("💢 Test Suite '\(testSuite.name)' finished with \(numberOfFailedTests) failed \(numberOfFailedTests > 1 ? "tests" : "test").")
|
||||
} else {
|
||||
print("🔅 Test Suite '\(testSuite.name)' finished successfully.")
|
||||
}
|
||||
}
|
||||
|
||||
func testCase(_ testCase: XCTestCase,
|
||||
didFailWithDescription description: String,
|
||||
inFile filePath: String?,
|
||||
atLine lineNumber: Int) {
|
||||
|
||||
numberOfFailedTests += 1
|
||||
print("❗️Test Fail '\(testCase.name)':\(UInt(lineNumber)): \(description.description)")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='6.0' target-platform='ios' display-mode='raw' buildActiveScheme='true'/>
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
83C08983988F66570478C40D /* Pods_TIBottomSheet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8EF3488B86B483233C2CC631 /* Pods_TIBottomSheet.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7B6955D74676A5427AC42234 /* Pods-TIBottomSheet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIBottomSheet.debug.xcconfig"; path = "Target Support Files/Pods-TIBottomSheet/Pods-TIBottomSheet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
8BACBE8322576CAD00266845 /* TIBottomSheet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TIBottomSheet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8BACBE8622576CAD00266845 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8EF3488B86B483233C2CC631 /* Pods_TIBottomSheet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TIBottomSheet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
AA57D8210790AD14BCC54A7E /* Pods-TIBottomSheet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIBottomSheet.release.xcconfig"; path = "Target Support Files/Pods-TIBottomSheet/Pods-TIBottomSheet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8BACBE8022576CAD00266845 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
83C08983988F66570478C40D /* Pods_TIBottomSheet.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
11F06D2789C6CF40767861CF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8EF3488B86B483233C2CC631 /* Pods_TIBottomSheet.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1F7782E3A7AD7291B7C09F56 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B6955D74676A5427AC42234 /* Pods-TIBottomSheet.debug.xcconfig */,
|
||||
AA57D8210790AD14BCC54A7E /* Pods-TIBottomSheet.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26221D40F8700DE2643 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8422576CAD00266845 /* TIBottomSheet */,
|
||||
8B39A26C21D40F8700DE2643 /* Products */,
|
||||
1F7782E3A7AD7291B7C09F56 /* Pods */,
|
||||
11F06D2789C6CF40767861CF /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26C21D40F8700DE2643 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8322576CAD00266845 /* TIBottomSheet.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8BACBE8422576CAD00266845 /* TIBottomSheet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8622576CAD00266845 /* Info.plist */,
|
||||
);
|
||||
path = TIBottomSheet;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8BACBE7E22576CAD00266845 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8BACBE8222576CAD00266845 /* TIBottomSheet */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIBottomSheet" */;
|
||||
buildPhases = (
|
||||
4E98D4C60DCD00EB801E579E /* [CP] Check Pods Manifest.lock */,
|
||||
8BACBE7E22576CAD00266845 /* Headers */,
|
||||
8BACBE7F22576CAD00266845 /* Sources */,
|
||||
8BACBE8022576CAD00266845 /* Frameworks */,
|
||||
8BACBE8122576CAD00266845 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TIBottomSheet;
|
||||
productName = TIBottomSheet2;
|
||||
productReference = 8BACBE8322576CAD00266845 /* TIBottomSheet.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8B39A26321D40F8700DE2643 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "47 Degrees";
|
||||
TargetAttributes = {
|
||||
8BACBE8222576CAD00266845 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIBottomSheet" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 8B39A26221D40F8700DE2643;
|
||||
productRefGroup = 8B39A26C21D40F8700DE2643 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8BACBE8222576CAD00266845 /* TIBottomSheet */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8BACBE8122576CAD00266845 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
4E98D4C60DCD00EB801E579E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-TIBottomSheet-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8BACBE7F22576CAD00266845 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8B39A27721D40F8800DE2643 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8B39A27821D40F8800DE2643 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8BACBE8822576CAD00266845 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7B6955D74676A5427AC42234 /* Pods-TIBottomSheet.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIBottomSheet_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIBottomSheet/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIBottomSheet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8BACBE8922576CAD00266845 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AA57D8210790AD14BCC54A7E /* Pods-TIBottomSheet.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIBottomSheet_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIBottomSheet/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIBottomSheet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIBottomSheet" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8B39A27721D40F8800DE2643 /* Debug */,
|
||||
8B39A27821D40F8800DE2643 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIBottomSheet" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8BACBE8822576CAD00266845 /* Debug */,
|
||||
8BACBE8922576CAD00266845 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8B39A26321D40F8700DE2643 /* Project object */;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:TIBottomSheet.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIBottomSheet.framework"
|
||||
BlueprintName = "TIBottomSheet"
|
||||
ReferencedContainer = "container:TIBottomSheet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</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 = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIBottomSheet.framework"
|
||||
BlueprintName = "TIBottomSheet"
|
||||
ReferencedContainer = "container:TIBottomSheet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIBottomSheet.framework"
|
||||
BlueprintName = "TIBottomSheet"
|
||||
ReferencedContainer = "container:TIBottomSheet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
11
TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.xcworkspace/contents.xcworkspacedata
generated
Normal file
11
TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef location = "group:TIBottomSheet.playground"></FileRef>
|
||||
<FileRef
|
||||
location = "group:TIBottomSheet.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019. The nef authors.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
workspace="TIBottomSheet.xcworkspace"
|
||||
workspacePath=$(echo "$0" | rev | cut -f2- -d '/' | rev)
|
||||
|
||||
open "`pwd`/$workspacePath/$workspace"
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
TIBottomSheet.app/Contents/MacOS/TIBottomSheet.playground
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIBottomSheet'
|
||||
s.version = '1.56.0'
|
||||
s.summary = 'Base models for creating bottom sheet view controllers'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'castlele' => 'nikita.semenov@touchin.ru',
|
||||
'petropavel13' => 'ivan.smolin@touchin.ru'}
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = 'Sources/**/*'
|
||||
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
|
||||
|
||||
s.dependency 'TIUIElements', s.version.to_s
|
||||
s.dependency 'TIUIKitCore', s.version.to_s
|
||||
s.dependency 'TISwiftUtils', s.version.to_s
|
||||
s.dependency 'PanModal', '~> 1.3.0'
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TICoreGraphicsUtils' do
|
||||
platform :ios, 11.0
|
||||
use_frameworks!
|
||||
|
||||
pod 'TICoreGraphicsUtils', :path => '../../../../TICoreGraphicsUtils/TICoreGraphicsUtils.podspec'
|
||||
end
|
||||
|
|
@ -22,11 +22,9 @@
|
|||
|
||||
import CoreGraphics.CGGeometry
|
||||
|
||||
public typealias CGContextSize = (width: Int, height: Int)
|
||||
|
||||
public extension CGSize {
|
||||
var ceiledContextSize: CGContextSize {
|
||||
(width: Int(ceil(width)), height: Int(ceil(height)))
|
||||
var ceiledContextSize: CGSize {
|
||||
CGSize(width: ceil(width), height: ceil(height))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -23,19 +23,19 @@
|
|||
import UIKit
|
||||
|
||||
public struct BorderDrawingOperation: DrawingOperation {
|
||||
public var frameableContentSize: CGSize
|
||||
public var frameableContentRect: CGRect
|
||||
public var border: CGFloat
|
||||
public var color: CGColor
|
||||
public var radius: CGFloat
|
||||
public var exteriorBorder: Bool
|
||||
|
||||
public init(frameableContentSize: CGSize,
|
||||
public init(frameableContentRect: CGRect,
|
||||
border: CGFloat,
|
||||
color: CGColor,
|
||||
radius: CGFloat,
|
||||
exteriorBorder: Bool) {
|
||||
|
||||
self.frameableContentSize = frameableContentSize
|
||||
self.frameableContentRect = frameableContentRect
|
||||
self.border = border
|
||||
self.color = color
|
||||
self.radius = radius
|
||||
|
|
@ -47,10 +47,10 @@ public struct BorderDrawingOperation: DrawingOperation {
|
|||
public func affectedArea(in context: CGContext?) -> CGRect {
|
||||
let margin = exteriorBorder ? border : 0
|
||||
|
||||
let width = frameableContentSize.width + margin * 2
|
||||
let height = frameableContentSize.height + margin * 2
|
||||
let width = frameableContentRect.width + margin * 2
|
||||
let height = frameableContentRect.height + margin * 2
|
||||
|
||||
return CGRect(origin: .zero,
|
||||
return CGRect(origin: frameableContentRect.origin,
|
||||
size: CGSize(width: width, height: height))
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,8 @@ public struct BorderDrawingOperation: DrawingOperation {
|
|||
|
||||
let ctxSize = drawArea.size.ceiledContextSize
|
||||
|
||||
let ctxRect = CGRect(origin: .zero, size: CGSize(width: ctxSize.width, height: ctxSize.height))
|
||||
let ctxRect = CGRect(origin: frameableContentRect.origin,
|
||||
size: CGSize(width: ctxSize.width, height: ctxSize.height))
|
||||
|
||||
let widthDiff = CGFloat(ctxSize.width) - drawArea.width // difference between context width and real width
|
||||
let heightDiff = CGFloat(ctxSize.height) - drawArea.height // difference between context height and real height
|
||||
|
|
@ -45,6 +45,6 @@ public struct CALayerDrawingOperation: DrawingOperation {
|
|||
|
||||
context.concatenate(offsetTransform)
|
||||
layer.render(in: context)
|
||||
offsetTransform.concatenating(offsetTransform.inverted())
|
||||
context.concatenate(offsetTransform.inverted())
|
||||
}
|
||||
}
|
||||
|
|
@ -55,8 +55,10 @@ public struct SolidFillDrawingOperation: DrawingOperation {
|
|||
switch shape {
|
||||
case let .rect(rect):
|
||||
return rect
|
||||
|
||||
case let .ellipse(rect):
|
||||
return rect
|
||||
|
||||
case let .path(path):
|
||||
return path.boundingBox
|
||||
}
|
||||
|
|
@ -68,8 +70,10 @@ public struct SolidFillDrawingOperation: DrawingOperation {
|
|||
switch shape {
|
||||
case let .rect(rect):
|
||||
context.fill(rect)
|
||||
|
||||
case let .ellipse(rect):
|
||||
context.fillEllipse(in: rect)
|
||||
|
||||
case let .path(path):
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
|
@ -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 CoreGraphics
|
||||
|
||||
public struct TemplateDrawingOperation: OrientationAwareDrawingOperation {
|
||||
|
||||
public var image: CGImage
|
||||
public var imageSize: CGSize
|
||||
public var color: CGColor
|
||||
public var flipHorizontallyDuringDrawing: Bool
|
||||
|
||||
public init(image: CGImage,
|
||||
imageSize: CGSize,
|
||||
color: CGColor,
|
||||
flipHorizontallyDuringDrawing: Bool = true) {
|
||||
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.color = color
|
||||
self.flipHorizontallyDuringDrawing = flipHorizontallyDuringDrawing
|
||||
}
|
||||
|
||||
public func affectedArea(in context: CGContext?) -> CGRect {
|
||||
CGRect(origin: .zero, size: imageSize)
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
let imageRect = CGRect(origin: .zero, size: imageSize)
|
||||
|
||||
apply(in: context) {
|
||||
$0.setFillColor(color)
|
||||
$0.clip(to: imageRect, mask: image)
|
||||
$0.fill(imageRect)
|
||||
$0.setBlendMode(.multiply)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,48 +26,45 @@ import CoreText
|
|||
|
||||
public struct TextDrawingOperation: OrientationAwareDrawingOperation {
|
||||
public var text: String
|
||||
public var font: CTFont
|
||||
public var textColor: CGColor
|
||||
public var textAttributes: [NSAttributedString.Key: Any]
|
||||
public var flipHorizontallyDuringDrawing: Bool
|
||||
public var desiredOffset: CGPoint
|
||||
|
||||
private var line: CTLine {
|
||||
let textAttributes: [NSAttributedString.Key : Any] = [
|
||||
.font: font,
|
||||
.foregroundColor: textColor
|
||||
]
|
||||
public var attributedString: NSAttributedString {
|
||||
NSAttributedString(string: text, attributes: textAttributes)
|
||||
}
|
||||
|
||||
let attributedString = NSAttributedString(string: text, attributes: textAttributes)
|
||||
public var line: CTLine {
|
||||
CTLineCreateWithAttributedString(attributedString)
|
||||
}
|
||||
|
||||
return CTLineCreateWithAttributedString(attributedString)
|
||||
public var desiredContextSize: CGSize {
|
||||
let imageBounds = CTLineGetImageBounds(line, nil)
|
||||
|
||||
return CGSize(width: max(imageBounds.width, imageBounds.maxX) + desiredOffset.x,
|
||||
height: max(imageBounds.height, imageBounds.maxY) + desiredOffset.y)
|
||||
}
|
||||
|
||||
public init(text: String,
|
||||
font: CTFont,
|
||||
textColor: CGColor,
|
||||
textAttributes: [NSAttributedString.Key: Any],
|
||||
flipHorizontallyDuringDrawing: Bool = true,
|
||||
desiredOffset: CGPoint = .zero) {
|
||||
|
||||
self.text = text
|
||||
self.font = font
|
||||
self.textColor = textColor
|
||||
self.textAttributes = textAttributes
|
||||
self.flipHorizontallyDuringDrawing = flipHorizontallyDuringDrawing
|
||||
self.desiredOffset = desiredOffset
|
||||
}
|
||||
|
||||
// MARK: - DrawingOperation
|
||||
|
||||
public func affectedArea(in context: CGContext? = nil) -> CGRect {
|
||||
CGRect(origin: desiredOffset, size: CTLineGetImageBounds(line, context).size)
|
||||
CTLineGetImageBounds(line, context).offset(by: desiredOffset)
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
apply(in: context) {
|
||||
let originForDrawing = offsetForDrawing(CTLineGetImageBounds(line, context).origin)
|
||||
let desiredOffsetForDrawing = offsetForDrawing(desiredOffset)
|
||||
let textPosition = CGPoint(x: desiredOffsetForDrawing.x - originForDrawing.x,
|
||||
y: desiredOffsetForDrawing.y - originForDrawing.y)
|
||||
|
||||
$0.textPosition = textPosition
|
||||
|
||||
$0.textPosition = offsetForDrawing(affectedArea(in: context).origin)
|
||||
CTLineDraw(line, $0)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
end
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>launcher</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fortysevendeg.nef</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 The nef Authors. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
||||
## User data
|
||||
**/xcuserdata/
|
||||
podfile.lock
|
||||
**.DS_Store
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## CocoaPods
|
||||
**Pods**
|
||||
|
||||
## Carthage
|
||||
**Carthage**
|
||||
|
||||
## SPM
|
||||
.build
|
||||
.swiftpm
|
||||
swiftpm
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TICoreGraphicsUtils' do
|
||||
platform :ios, 12.0
|
||||
use_frameworks!
|
||||
|
||||
pod 'TICoreGraphicsUtils', :path => '../../../../TICoreGraphicsUtils/TICoreGraphicsUtils.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*:
|
||||
# `DrawingOperation` - протокол для инкапсулирования низкоуровневых вызовов отрисовки CoreGraphicss.
|
||||
|
||||
Позволяет:
|
||||
|
||||
- сгруппировать низкоуровневые операции CoreGraphics в более высокоуровневые
|
||||
- использовать композицию из высокоуровневых операций
|
||||
- получить размер изображения необходимый для создания контекста
|
||||
|
||||
## Базовые операции
|
||||
|
||||
"Из коробки" доступны самые часто испольуемые операции.
|
||||
*/
|
||||
|
||||
/*:
|
||||
### `SolidFillDrawingOperation`
|
||||
|
||||
Операция для заполнения области рисования выбранным цветом и выбранной формой.
|
||||
Например, можно нарисовать прямоугольник или круг на существующей области рисования
|
||||
или просто создать изображение с однотонный фоном.
|
||||
*/
|
||||
|
||||
import TICoreGraphicsUtils
|
||||
import UIKit
|
||||
|
||||
let solidFillSize = CGSize(width: 200, height: 200)
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: solidFillSize)
|
||||
let solidFillDrawingOperation = SolidFillDrawingOperation(color: UIColor.purple.cgColor,
|
||||
rect: CGRect(origin: .zero,
|
||||
size: solidFillSize))
|
||||
|
||||
let solidFillImage = renderer.image {
|
||||
solidFillDrawingOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
|
||||
/*:
|
||||
### `BorderDrawingOperation`
|
||||
|
||||
Операция создаёт рамку определённой формы и размера.
|
||||
*/
|
||||
|
||||
let borderDrawingOperation = BorderDrawingOperation(frameableContentRect: CGRect(origin: .zero,
|
||||
size: solidFillSize),
|
||||
border: 2,
|
||||
color: UIColor.red.cgColor,
|
||||
radius: 4,
|
||||
exteriorBorder: false)
|
||||
|
||||
let borderImage = renderer.image {
|
||||
borderDrawingOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
|
||||
/*:
|
||||
### `CALayerDrawingOperation`
|
||||
|
||||
Операция отрисовывает содержимое CALayer в изображение. Обычно используется для снапшота UIView.
|
||||
*/
|
||||
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitle("This is button", for: .normal)
|
||||
button.setBackgroundImage(borderImage, for: .normal)
|
||||
|
||||
button.sizeToFit()
|
||||
|
||||
let layerDrawingOperation = CALayerDrawingOperation(layer: button.layer, offset: .zero)
|
||||
|
||||
let layerRenderer = UIGraphicsImageRenderer(size: button.bounds.size)
|
||||
|
||||
let buttonSnapshotImage = layerRenderer.image {
|
||||
layerDrawingOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
|
||||
/*:
|
||||
### `TextDrawingOperation`
|
||||
|
||||
Операция отрисовывает текст с заданными атрибутами
|
||||
*/
|
||||
|
||||
let textDrawingOperaton = TextDrawingOperation(text: "This is string",
|
||||
textAttributes: [
|
||||
.font: UIFont.boldSystemFont(ofSize: 15),
|
||||
.foregroundColor: UIColor.white
|
||||
])
|
||||
|
||||
let textRenderer = UIGraphicsImageRenderer(size: textDrawingOperaton.desiredContextSize)
|
||||
|
||||
let textImage = textRenderer.image {
|
||||
textDrawingOperaton.apply(in: $0.cgContext)
|
||||
}
|
||||
|
||||
/*:
|
||||
### `TemplateDrawingOperation`
|
||||
|
||||
Операция заменяет все цвета кроме прозрачного на переданный цвет.
|
||||
Используется для приданиям иконкам определённого цвета (аналог tintColor).
|
||||
*/
|
||||
|
||||
if let cgImage = textImage.cgImage {
|
||||
let templateDrawingOperation = TemplateDrawingOperation(image: cgImage,
|
||||
imageSize: textImage.size,
|
||||
color: UIColor.red.cgColor)
|
||||
|
||||
let tintedImage = textRenderer.image {
|
||||
templateDrawingOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
### `TransformDrawingOperation`
|
||||
|
||||
Операция производит изменение размера изображения учитывая пропорции
|
||||
и другие настройки (по аналогии с contentMode у UIImage).
|
||||
*/
|
||||
|
||||
if let cgImage = textImage.cgImage {
|
||||
let newSize = CGSize(width: textImage.size.width / 1.5, height: textImage.size.height)
|
||||
let resizeOperation = TransformDrawingOperation(image: cgImage,
|
||||
imageSize: textImage.size,
|
||||
maxNewSize: newSize,
|
||||
resizeMode: .scaleAspectFill)
|
||||
|
||||
let resizeRenderer = UIGraphicsImageRenderer(size: newSize)
|
||||
|
||||
let resizedImage = resizeRenderer.image {
|
||||
resizeOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import UIKit
|
||||
|
||||
public protocol NefPlaygroundLiveViewable {}
|
||||
extension UIView: NefPlaygroundLiveViewable {}
|
||||
extension UIViewController: NefPlaygroundLiveViewable {}
|
||||
|
||||
#if NOT_IN_PLAYGROUND
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {}
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
import PlaygroundSupport
|
||||
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {
|
||||
PlaygroundPage.current.liveView = (view as! PlaygroundLiveViewable)
|
||||
}
|
||||
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {
|
||||
PlaygroundPage.current.needsIndefiniteExecution = state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='6.0' target-platform='ios' display-mode='raw' buildActiveScheme='true'/>
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A319933CF180B210C469FFB1 /* Pods_TICoreGraphicsUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE2E24C8921A6FA2CDB65242 /* Pods_TICoreGraphicsUtils.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
82AF4D8AFDF24586DD661012 /* Pods-TICoreGraphicsUtils.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TICoreGraphicsUtils.debug.xcconfig"; path = "Target Support Files/Pods-TICoreGraphicsUtils/Pods-TICoreGraphicsUtils.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
8BACBE8322576CAD00266845 /* TICoreGraphicsUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TICoreGraphicsUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8BACBE8622576CAD00266845 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9238CAF7BE416E9ACF0C153E /* Pods-TICoreGraphicsUtils.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TICoreGraphicsUtils.release.xcconfig"; path = "Target Support Files/Pods-TICoreGraphicsUtils/Pods-TICoreGraphicsUtils.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AE2E24C8921A6FA2CDB65242 /* Pods_TICoreGraphicsUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TICoreGraphicsUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8BACBE8022576CAD00266845 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A319933CF180B210C469FFB1 /* Pods_TICoreGraphicsUtils.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2238D5BA030C14EBEF939AC4 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
82AF4D8AFDF24586DD661012 /* Pods-TICoreGraphicsUtils.debug.xcconfig */,
|
||||
9238CAF7BE416E9ACF0C153E /* Pods-TICoreGraphicsUtils.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26221D40F8700DE2643 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8422576CAD00266845 /* TICoreGraphicsUtils */,
|
||||
8B39A26C21D40F8700DE2643 /* Products */,
|
||||
2238D5BA030C14EBEF939AC4 /* Pods */,
|
||||
B51B4BA44249652EDC4FBF61 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26C21D40F8700DE2643 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8322576CAD00266845 /* TICoreGraphicsUtils.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8BACBE8422576CAD00266845 /* TICoreGraphicsUtils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8622576CAD00266845 /* Info.plist */,
|
||||
);
|
||||
path = TICoreGraphicsUtils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B51B4BA44249652EDC4FBF61 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AE2E24C8921A6FA2CDB65242 /* Pods_TICoreGraphicsUtils.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8BACBE7E22576CAD00266845 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8BACBE8222576CAD00266845 /* TICoreGraphicsUtils */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TICoreGraphicsUtils" */;
|
||||
buildPhases = (
|
||||
3A6585E20D9DE6C1873231DC /* [CP] Check Pods Manifest.lock */,
|
||||
8BACBE7E22576CAD00266845 /* Headers */,
|
||||
8BACBE7F22576CAD00266845 /* Sources */,
|
||||
8BACBE8022576CAD00266845 /* Frameworks */,
|
||||
8BACBE8122576CAD00266845 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TICoreGraphicsUtils;
|
||||
productName = TICoreGraphicsUtils2;
|
||||
productReference = 8BACBE8322576CAD00266845 /* TICoreGraphicsUtils.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8B39A26321D40F8700DE2643 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "47 Degrees";
|
||||
TargetAttributes = {
|
||||
8BACBE8222576CAD00266845 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TICoreGraphicsUtils" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 8B39A26221D40F8700DE2643;
|
||||
productRefGroup = 8B39A26C21D40F8700DE2643 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8BACBE8222576CAD00266845 /* TICoreGraphicsUtils */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8BACBE8122576CAD00266845 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3A6585E20D9DE6C1873231DC /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-TICoreGraphicsUtils-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8BACBE7F22576CAD00266845 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8B39A27721D40F8800DE2643 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8B39A27821D40F8800DE2643 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8BACBE8822576CAD00266845 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 82AF4D8AFDF24586DD661012 /* Pods-TICoreGraphicsUtils.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TICoreGraphicsUtils_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TICoreGraphicsUtils/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TICoreGraphicsUtils;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8BACBE8922576CAD00266845 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9238CAF7BE416E9ACF0C153E /* Pods-TICoreGraphicsUtils.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TICoreGraphicsUtils_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TICoreGraphicsUtils/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TICoreGraphicsUtils;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TICoreGraphicsUtils" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8B39A27721D40F8800DE2643 /* Debug */,
|
||||
8B39A27821D40F8800DE2643 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TICoreGraphicsUtils" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8BACBE8822576CAD00266845 /* Debug */,
|
||||
8BACBE8922576CAD00266845 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8B39A26321D40F8700DE2643 /* Project object */;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:TICoreGraphicsUtils.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TICoreGraphicsUtils.framework"
|
||||
BlueprintName = "TICoreGraphicsUtils"
|
||||
ReferencedContainer = "container:TICoreGraphicsUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</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 = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TICoreGraphicsUtils.framework"
|
||||
BlueprintName = "TICoreGraphicsUtils"
|
||||
ReferencedContainer = "container:TICoreGraphicsUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TICoreGraphicsUtils.framework"
|
||||
BlueprintName = "TICoreGraphicsUtils"
|
||||
ReferencedContainer = "container:TICoreGraphicsUtils.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef location = "group:TICoreGraphicsUtils.playground"></FileRef>
|
||||
<FileRef
|
||||
location = "group:TICoreGraphicsUtils.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019. The nef authors.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
workspace="TICoreGraphicsUtils.xcworkspace"
|
||||
workspacePath=$(echo "$0" | rev | cut -f2- -d '/' | rev)
|
||||
|
||||
open "`pwd`/$workspacePath/$workspace"
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
TICoreGraphicsUtils.app/Contents/MacOS/TICoreGraphicsUtils.playground
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TICoreGraphicsUtils'
|
||||
s.version = '1.56.0'
|
||||
s.summary = 'CoreGraphics drawing helpers'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = 'Sources/**/*'
|
||||
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
|
||||
|
||||
s.framework = 'CoreGraphics'
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TIDeeplink' do
|
||||
platform :ios, 11
|
||||
use_frameworks!
|
||||
|
||||
pod 'TILogging', :path => '../../../../TILogging/TILogging.podspec'
|
||||
pod 'TIFoundationUtils', :path => '../../../../TIFoundationUtils/TIFoundationUtils.podspec'
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIDeeplink', :path => '../../../../TIDeeplink/TIDeeplink.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// 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 Foundation
|
||||
import TISwiftUtils
|
||||
|
||||
public struct AnyDeeplinkHandler<Deeplink>: DeeplinkHandler {
|
||||
private let handlerClosure: Closure<Deeplink, Operation?>
|
||||
|
||||
public init<Handler: DeeplinkHandler>(handler: Handler) where Handler.Deeplink == Deeplink {
|
||||
self.handlerClosure = handler.handle
|
||||
}
|
||||
|
||||
public func handle(deeplink: Deeplink) -> Operation? {
|
||||
handlerClosure(deeplink)
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeeplinkHandler {
|
||||
func eraseToAnyDeeplinkHandler() -> AnyDeeplinkHandler<Deeplink> {
|
||||
AnyDeeplinkHandler(handler: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// 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 Foundation
|
||||
import TISwiftUtils
|
||||
import UIKit
|
||||
|
||||
open class BaseNavigationStackDeeplinkHandler<Deeplink>: DeeplinkHandler {
|
||||
|
||||
public var rootViewController: UIViewController
|
||||
public var asAnyHandlerClosure: Closure<UIViewController, AnyDeeplinkHandler<Deeplink>?>
|
||||
|
||||
public init(rootViewController: UIViewController,
|
||||
asAnyHandlerClosure: @escaping Closure<UIViewController, AnyDeeplinkHandler<Deeplink>?>) {
|
||||
|
||||
self.rootViewController = rootViewController
|
||||
self.asAnyHandlerClosure = asAnyHandlerClosure
|
||||
}
|
||||
|
||||
// MARK: - DeeplinkHandler
|
||||
|
||||
open func handle(deeplink: Deeplink) -> Operation? {
|
||||
if let handler = asAnyHandlerClosure(rootViewController),
|
||||
let operation = handler.handle(deeplink: deeplink) {
|
||||
return operation
|
||||
}
|
||||
|
||||
let (_, operation) = findHandler(for: deeplink, in: rootViewController)
|
||||
|
||||
return operation
|
||||
}
|
||||
|
||||
// MARK: - Open methods
|
||||
|
||||
open func findHandler(for deeplink: Deeplink,
|
||||
in viewController: UIViewController) -> (AnyDeeplinkHandler<Deeplink>?, Operation?) {
|
||||
|
||||
switch viewController {
|
||||
case let navController as UINavigationController:
|
||||
for controllerInNavigationStack in navController.viewControllers.reversed() {
|
||||
if let handler = asAnyHandlerClosure(controllerInNavigationStack),
|
||||
let operation = handler.handle(deeplink: deeplink) {
|
||||
return (handler, operation)
|
||||
}
|
||||
}
|
||||
|
||||
case let tabController as UITabBarController:
|
||||
if let selectedController = tabController.selectedViewController,
|
||||
let handler = asAnyHandlerClosure(selectedController),
|
||||
let operation = handler.handle(deeplink: deeplink) {
|
||||
return (handler, operation)
|
||||
|
||||
} else if var tabControllers = tabController.viewControllers {
|
||||
if tabController.selectedIndex != NSNotFound {
|
||||
tabControllers.remove(at: tabController.selectedIndex)
|
||||
}
|
||||
|
||||
for tabController in tabControllers {
|
||||
if let handler = asAnyHandlerClosure(tabController),
|
||||
let operation = handler.handle(deeplink: deeplink) {
|
||||
return (handler, operation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
let handler = asAnyHandlerClosure(viewController)
|
||||
let operation = handler?.handle(deeplink: deeplink)
|
||||
|
||||
return (handler, operation)
|
||||
}
|
||||
|
||||
return (nil, nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
// 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
|
||||
|
|
@ -21,8 +21,10 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import TISwiftUtils
|
||||
|
||||
public protocol AuthSettingsStorage: AnyObject {
|
||||
/// Should be true by default (on app first run)
|
||||
var shouldResetStoredAuthData: Bool { get set }
|
||||
public protocol DeeplinkHandler {
|
||||
associatedtype Deeplink
|
||||
|
||||
func handle(deeplink: Deeplink) -> Operation?
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>launcher</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fortysevendeg.nef</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 The nef Authors. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## gitignore nef files
|
||||
**/build/
|
||||
**/nef/
|
||||
LICENSE
|
||||
|
||||
## User data
|
||||
**/xcuserdata/
|
||||
podfile.lock
|
||||
**.DS_Store
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## CocoaPods
|
||||
**Pods**
|
||||
|
||||
## Carthage
|
||||
**Carthage**
|
||||
|
||||
## SPM
|
||||
.build
|
||||
.swiftpm
|
||||
swiftpm
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
ENV["DEVELOPMENT_INSTALL"] = "true"
|
||||
|
||||
target 'TIDeeplink' do
|
||||
platform :ios, 12
|
||||
use_frameworks!
|
||||
|
||||
pod 'TILogging', :path => '../../../../TILogging/TILogging.podspec'
|
||||
pod 'TIFoundationUtils', :path => '../../../../TIFoundationUtils/TIFoundationUtils.podspec'
|
||||
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
|
||||
pod 'TIDeeplink', :path => '../../../../TIDeeplink/TIDeeplink.podspec'
|
||||
end
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*:
|
||||
# Deeplink API
|
||||
|
||||
_TIDeeplink_ добавляет сервис `TIDeeplinksService` для обработки диплинков
|
||||
|
||||
## Как настроить
|
||||
|
||||
1. Создать объект, представляющий диплинк. Такой объект должен соответствовать протоколу `Hashable`
|
||||
*/
|
||||
import Foundation
|
||||
import TIDeeplink
|
||||
import TIFoundationUtils
|
||||
import TISwiftUtils
|
||||
import UIKit
|
||||
|
||||
enum ProjectDeeplink: Hashable {
|
||||
case editProfile
|
||||
case office(id: String)
|
||||
}
|
||||
|
||||
//: 2. Создать объект, соответствующий протоколу `DeeplinkMapper`. Его задачей будет - преобразование _URL_ в диплинк
|
||||
final class ProjectDeeplinkMapper: DeeplinkMapper {
|
||||
func map(url: URL) -> ProjectDeeplink? {
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch components.host {
|
||||
case "office":
|
||||
if let id = components.path.split(separator: "/").last {
|
||||
return .office(id: String(id))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case "editProfile":
|
||||
return .editProfile
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//: 3. Реализовать у объекта отвечающего за навигацию протокол`DeeplinkHandler`. В необходимом для реализации методе `handle(deeplink:)` нужно обработать передаваемый диплинк и вернуть соответствующее действие
|
||||
class MainCoordinator: DeeplinkHandler {
|
||||
func openEditProfileScreen() {
|
||||
print("Presenting edit profile view controller")
|
||||
}
|
||||
|
||||
func openOfficesScreen(completion: VoidClosure?) {
|
||||
print("Presenting offices view controller")
|
||||
completion?()
|
||||
}
|
||||
|
||||
func openOfficesScreen(forId id: String) {
|
||||
print("Presenting offices view controller by id: \(id)")
|
||||
}
|
||||
|
||||
// MARK: DeeplinkHandler
|
||||
|
||||
func handle(deeplink: ProjectDeeplink) -> Operation? {
|
||||
switch deeplink {
|
||||
case .editProfile:
|
||||
return BlockOperation { [weak self] in
|
||||
self?.openEditProfileScreen()
|
||||
}
|
||||
|
||||
case let .office(id):
|
||||
return ClosureAsyncOperation<Void, Never> { [weak self] completion in
|
||||
self?.openOfficesScreen {
|
||||
self?.openOfficesScreen(forId: id)
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
return Cancellables.nonCancellable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
**Опционально** 4. Создать сабкласс `TIDeeplinksService` для удобного использования по проекту
|
||||
*/
|
||||
final class ProjectDeeplinksService: TIDeeplinksService<ProjectDeeplink> {
|
||||
static let shared = ProjectDeeplinksService()
|
||||
|
||||
convenience init(mapper: ProjectDeeplinkMapper = .init(), handler: MainCoordinator = .init()) {
|
||||
self.init(mapper: mapper, handler: handler, operationQueue: .main)
|
||||
}
|
||||
}
|
||||
|
||||
/*:
|
||||
Создаем и передаем в сервис Mapper и Handler.
|
||||
|
||||
> Так же можно воспользоваться инициализатором `init(mapper:handler:operationQueue:)`. Если Mapper и Handler передаются не в инициализаторе, то их самостоятельно необходимо привести к виду `AnyDeeplinkHandler` и `AnyDeeplinkMapper` с момощью методов: `eraseToAnyDeeplinkHandler()` и `eraseToAnyDeeplinkMapper()`
|
||||
|
||||
```swift
|
||||
let coordinator = MainCoordinator()
|
||||
let mapper = ProjectDeeplinkMapper()
|
||||
ProjectDeeplinksService.shared.deeplinkMapper = mapper.eraseToAnyDeeplinkMapper()
|
||||
ProjectDeeplinksService.shared.deeplinkHandler = coordinator.eraseToAnyDeeplinkHandler()
|
||||
```
|
||||
|
||||
В `AppDelegate` использвуем методы `deferredHandle(url:)` и `handlePendingDeeplinks()` для обработки диплинков.
|
||||
|
||||
- `deferredHandle(url:)`: пытается создать из _URL_ диплинк и добавить его в очередь на обработку
|
||||
- `handlePendingDeeplinks()`: обрабатывает первый в череди диплинк
|
||||
*/
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
|
||||
ProjectDeeplinksService.shared.deferredHandle(url: url)
|
||||
ProjectDeeplinksService.shared.handlePendingDeeplinks()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
guard let url = URL(string: "app://office/123") else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
application(.shared, open: url)
|
||||
|
||||
/*:
|
||||
В качестве `DeeplinkHandler` можно использовать базовую реализацию `BaseNavigationStackDeeplinkHandler`, которая позволяет искать handler в иерархии view, способный обработать диплинк.
|
||||
*/
|
||||
|
||||
protocol ProjectDeeplinkHandler: DeeplinkHandler where Deeplink == ProjectDeeplink {
|
||||
}
|
||||
|
||||
class ViewController: UIViewController, ProjectDeeplinkHandler {
|
||||
func handle(deeplink: ProjectDeeplink) -> Operation? {
|
||||
switch deeplink {
|
||||
case .editProfile:
|
||||
return BlockOperation {
|
||||
print("Presenting edit profile view controller")
|
||||
}
|
||||
|
||||
case let .office(id):
|
||||
return BlockOperation {
|
||||
print("Presenting offices view controller by id: \(id)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//: Создание Handler. Для настройки передается rootViewController, который должен наследоваться от `UIViewController` С этого контроллера будет начинаться поиск обработчика
|
||||
let viewController = ViewController()
|
||||
let navigationController = UINavigationController(rootViewController: viewController)
|
||||
let handler = BaseNavigationStackDeeplinkHandler<ProjectDeeplink>(rootViewController: navigationController) {
|
||||
guard let projectHandler = $0 as? (any ProjectDeeplinkHandler) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return projectHandler.eraseToAnyDeeplinkHandler()
|
||||
}
|
||||
|
||||
//: Далее handler может передаваться для использования в `TIDeeplinksService`
|
||||
let mapper = ProjectDeeplinkMapper()
|
||||
let service = TIDeeplinksService(mapper: mapper, handler: handler)
|
||||
|
||||
service.deferredHandle(url: url)
|
||||
service.handlePendingDeeplinks()
|
||||
|
||||
RunLoop.main.run()
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import UIKit
|
||||
|
||||
public protocol NefPlaygroundLiveViewable {}
|
||||
extension UIView: NefPlaygroundLiveViewable {}
|
||||
extension UIViewController: NefPlaygroundLiveViewable {}
|
||||
|
||||
#if NOT_IN_PLAYGROUND
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {}
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
import PlaygroundSupport
|
||||
|
||||
public enum Nef {
|
||||
public enum Playground {
|
||||
public static func liveView(_ view: NefPlaygroundLiveViewable) {
|
||||
PlaygroundPage.current.liveView = (view as! PlaygroundLiveViewable)
|
||||
}
|
||||
|
||||
public static func needsIndefiniteExecution(_ state: Bool) {
|
||||
PlaygroundPage.current.needsIndefiniteExecution = state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='6.0' target-platform='ios' display-mode='raw' buildActiveScheme='true' executeOnSourceChanges='true'/>
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2FC760DADC7ABCE05229EAD6 /* Pods_TIDeeplink.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC1315C38BF91446DB6FCA4 /* Pods_TIDeeplink.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0EC1315C38BF91446DB6FCA4 /* Pods_TIDeeplink.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TIDeeplink.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6BA4C0D578FC5BA8798475E6 /* Pods-TIDeeplink.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIDeeplink.release.xcconfig"; path = "Target Support Files/Pods-TIDeeplink/Pods-TIDeeplink.release.xcconfig"; sourceTree = "<group>"; };
|
||||
8BACBE8322576CAD00266845 /* TIDeeplink.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TIDeeplink.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8BACBE8622576CAD00266845 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
A2898C64EB444BA1068CC72C /* Pods-TIDeeplink.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TIDeeplink.debug.xcconfig"; path = "Target Support Files/Pods-TIDeeplink/Pods-TIDeeplink.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8BACBE8022576CAD00266845 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2FC760DADC7ABCE05229EAD6 /* Pods_TIDeeplink.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
154F2E18EA615FB457290B92 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC1315C38BF91446DB6FCA4 /* Pods_TIDeeplink.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26221D40F8700DE2643 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8422576CAD00266845 /* TIDeeplink */,
|
||||
8B39A26C21D40F8700DE2643 /* Products */,
|
||||
CB5B9E4DF89ADAD1D8849E37 /* Pods */,
|
||||
154F2E18EA615FB457290B92 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8B39A26C21D40F8700DE2643 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8322576CAD00266845 /* TIDeeplink.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8BACBE8422576CAD00266845 /* TIDeeplink */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BACBE8622576CAD00266845 /* Info.plist */,
|
||||
);
|
||||
path = TIDeeplink;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CB5B9E4DF89ADAD1D8849E37 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2898C64EB444BA1068CC72C /* Pods-TIDeeplink.debug.xcconfig */,
|
||||
6BA4C0D578FC5BA8798475E6 /* Pods-TIDeeplink.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8BACBE7E22576CAD00266845 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8BACBE8222576CAD00266845 /* TIDeeplink */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIDeeplink" */;
|
||||
buildPhases = (
|
||||
B00BE0F551706E267B42014F /* [CP] Check Pods Manifest.lock */,
|
||||
8BACBE7E22576CAD00266845 /* Headers */,
|
||||
8BACBE7F22576CAD00266845 /* Sources */,
|
||||
8BACBE8022576CAD00266845 /* Frameworks */,
|
||||
8BACBE8122576CAD00266845 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TIDeeplink;
|
||||
productName = TIDeeplink2;
|
||||
productReference = 8BACBE8322576CAD00266845 /* TIDeeplink.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8B39A26321D40F8700DE2643 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "47 Degrees";
|
||||
TargetAttributes = {
|
||||
8BACBE8222576CAD00266845 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIDeeplink" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 8B39A26221D40F8700DE2643;
|
||||
productRefGroup = 8B39A26C21D40F8700DE2643 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8BACBE8222576CAD00266845 /* TIDeeplink */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8BACBE8122576CAD00266845 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
B00BE0F551706E267B42014F /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-TIDeeplink-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8BACBE7F22576CAD00266845 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8B39A27721D40F8800DE2643 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8B39A27821D40F8800DE2643 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTING_SEARCH_PATHS = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8BACBE8822576CAD00266845 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = A2898C64EB444BA1068CC72C /* Pods-TIDeeplink.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIDeeplink_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIDeeplink/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIDeeplink;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8BACBE8922576CAD00266845 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6BA4C0D578FC5BA8798475E6 /* Pods-TIDeeplink.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_TIDeeplink_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/TIDeeplink/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.47deg.ios.TIDeeplink;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8B39A26621D40F8700DE2643 /* Build configuration list for PBXProject "TIDeeplink" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8B39A27721D40F8800DE2643 /* Debug */,
|
||||
8B39A27821D40F8800DE2643 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8BACBE8A22576CAD00266845 /* Build configuration list for PBXNativeTarget "TIDeeplink" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8BACBE8822576CAD00266845 /* Debug */,
|
||||
8BACBE8922576CAD00266845 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8B39A26321D40F8700DE2643 /* Project object */;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:TIDeeplink.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIDeeplink.framework"
|
||||
BlueprintName = "TIDeeplink"
|
||||
ReferencedContainer = "container:TIDeeplink.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</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 = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIDeeplink.framework"
|
||||
BlueprintName = "TIDeeplink"
|
||||
ReferencedContainer = "container:TIDeeplink.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8BACBE8222576CAD00266845"
|
||||
BuildableName = "TIDeeplink.framework"
|
||||
BlueprintName = "TIDeeplink"
|
||||
ReferencedContainer = "container:TIDeeplink.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
16
TIDeepLink/TIDeeplink.app/Contents/MacOS/TIDeeplink.xcworkspace/contents.xcworkspacedata
generated
Normal file
16
TIDeepLink/TIDeeplink.app/Contents/MacOS/TIDeeplink.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:TIDeeplink.playground">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:TIDeeplink.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:../../../../TIDeepLink/TIDeeplink.app/Contents/MacOS/Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019. The nef authors.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
workspace="TIDeeplink.xcworkspace"
|
||||
workspacePath=$(echo "$0" | rev | cut -f2- -d '/' | rev)
|
||||
|
||||
open "`pwd`/$workspacePath/$workspace"
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
TIDeeplink.app/Contents/MacOS/TIDeeplink.playground
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue