├── Extra
└── XCode Templates
│ ├── Locations.txt
│ ├── View.xctemplate
│ ├── ___FILEBASENAME___UIModel.swift
│ ├── ___FILEBASENAME___Parameters.swift
│ ├── TemplateInfo.plist
│ └── ___FILEBASENAME___.swift
│ ├── Gateway.xctemplate
│ ├── ___FILEBASENAME___Parameters.swift
│ ├── ___FILEBASENAME___GatewayProtocol.swift
│ ├── ___FILEBASENAME___Entity.swift
│ ├── ___FILEBASENAME___Gateway.swift
│ ├── Mock___FILEBASENAME___Gateway.swift
│ └── TemplateInfo.plist
│ └── ContentView (MV).xctemplate
│ ├── ___FILEBASENAME___Parameters.swift
│ └── TemplateInfo.plist
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── Documentation
└── Swift Style Guide
│ ├── 1. Intro
│ └── 1.1. Intro.md
│ ├── 3. Spacing and Brackets
│ ├── 3.4. Container Types.md
│ ├── 3.1. Intro.md
│ ├── 3.5. Subclasses and Protocol Conformances.md
│ ├── 3.2. Tabs.md
│ ├── 3.6. Opening Brackets.md
│ ├── 3.10. Conditional Compilation Flags.md
│ ├── 3.13. Multi-Line String Literals.md
│ └── 3.7. Closing Brackets.md
│ ├── 4. Context and Clarity
│ ├── 4.1. Intro.md
│ ├── 4.15. Extension Access Control.md
│ └── 4.14. Name Shadowing.md
│ ├── 2. Code Organization
│ └── 2.1. Intro.md
│ ├── 6. Performance and Optimization
│ ├── 6.2. Private Members.md
│ ├── 6.1. Intro.md
│ └── 6.3. Final Declarations.md
│ └── 5. API Design
│ └── 5.2. Compose, Don't Enumerate.md
├── Sources
├── VCore
│ ├── Views
│ │ ├── SwiftUI
│ │ │ ├── Base Button (SwiftUI)
│ │ │ │ ├── SwiftUIBaseButtonState.swift
│ │ │ │ └── SwiftUIBaseButtonUIModel.swift
│ │ │ ├── Touch Sensitive Container
│ │ │ │ ├── TouchSensitiveContainerState.swift
│ │ │ │ └── TouchSensitiveContainerContent.swift
│ │ │ ├── View Resetting Container
│ │ │ │ ├── EnvironmentValues+ViewResetter.swift
│ │ │ │ └── ViewResetter.swift
│ │ │ ├── View Resetting Container (Observable Object)
│ │ │ │ ├── EnvironmentValues+ViewResetterOO.swift
│ │ │ │ └── ViewResetterOO.swift
│ │ │ └── Plain List
│ │ │ │ ├── PlainListUIModel.swift
│ │ │ │ └── View+PlainListDecoration.swift
│ │ └── UIKit
│ │ │ ├── Infinite Scrolling UI Views
│ │ │ ├── Infinite Scrolling UI Table View
│ │ │ │ ├── InfiniteScrollingUITableView+PaginationState.swift
│ │ │ │ ├── InfiniteScrollingUITableViewActivityIndicatorViewUIModel.swift
│ │ │ │ └── InfiniteScrollingUITableViewDelegate.swift
│ │ │ ├── Infinite Scrolling UI Collection View
│ │ │ │ ├── InfiniteScrollingUICollectionViewActivityIndicatorViewUIModel.swift
│ │ │ │ ├── InfiniteScrollingUICollectionView+PaginationState.swift
│ │ │ │ └── InfiniteScrollingUICollectionViewDelegate.swift
│ │ │ └── Common
│ │ │ │ ├── InfiniteScrollingPaginationState.swift
│ │ │ │ └── InfiniteScrollingHelpers.swift
│ │ │ ├── Scrollable UI View
│ │ │ └── ScrollableUIView+ScrollDirection.swift
│ │ │ ├── Inner Shadow UI View
│ │ │ └── InnerShadowUIViewUIModel.swift
│ │ │ └── InteractivePoppingUINavigationController.swift
│ ├── Global Functions
│ │ ├── LogicalExclusiveOr.swift
│ │ ├── Debugging
│ │ │ ├── TODO.swift
│ │ │ └── FIXME.swift
│ │ └── AreEqualWithinTolerance.swift
│ ├── Services and Managers
│ │ └── Presentation Host
│ │ │ ├── Geometry
│ │ │ ├── EnvironmentValue+PresentationHostContainerSize.swift
│ │ │ └── EnvironmentValue+PresentationHostSafeAreaInsets.swift
│ │ │ ├── Presentation Mode
│ │ │ └── EnvironmentValues+PresentationHostPresentationMode.swift
│ │ │ ├── Host
│ │ │ └── PresentationHostUIModel.swift
│ │ │ └── Presentation Mode (Internal)
│ │ │ └── PresentationHostInternalPresentationMode.swift
│ ├── Extensions
│ │ ├── Foundation
│ │ │ ├── Range and Closed Range
│ │ │ │ ├── Range+InitWithBounds.swift
│ │ │ │ ├── ClosedRange+BoundRange.swift
│ │ │ │ ├── ClosedRange+InitWithBounds.swift
│ │ │ │ ├── Range+ReversedArrayOnCondition.swift
│ │ │ │ └── ClosedRange+ReversedArrayOnCondition.swift
│ │ │ ├── ProcessInfo
│ │ │ │ └── ProcessInfo+IsPreview.swift
│ │ │ ├── Numeric
│ │ │ │ ├── Numeric+NonZero.swift
│ │ │ │ └── Numeric+WithOppositeSign.swift
│ │ │ ├── String
│ │ │ │ └── String+IsUserDefaultsKey.swift
│ │ │ ├── Data
│ │ │ │ └── Data+NonEmpty.swift
│ │ │ ├── Character Set
│ │ │ │ └── CharacterSet+Unified.swift
│ │ │ ├── Collection
│ │ │ │ ├── Collection+NonEmpty.swift
│ │ │ │ ├── Collection+RandomElements.swift
│ │ │ │ ├── Collection+SafeSubscript.swift
│ │ │ │ ├── Collection+EnumeratedArray.swift
│ │ │ │ ├── Collection+ContainsAnyItem.swift
│ │ │ │ └── Collection+AllMatch.swift
│ │ │ ├── Set
│ │ │ │ ├── Set+InsertSequence.swift
│ │ │ │ └── Set+TogglingElement.swift
│ │ │ ├── Date
│ │ │ │ └── Date+Age.swift
│ │ │ ├── Sequence
│ │ │ │ ├── Sequence+IsUnique.swift
│ │ │ │ └── Sequence+ContainsDuplicates.swift
│ │ │ ├── Bool
│ │ │ │ └── Bool+Toggled.swift
│ │ │ ├── String Protocol
│ │ │ │ ├── StringProtocol+DiacriticInsensitiveString.swift
│ │ │ │ ├── StringProtocol+Subscript.swift
│ │ │ │ └── StringProtocol+Replaced.swift
│ │ │ ├── Int
│ │ │ │ └── Int+BlockNTimes.swift
│ │ │ ├── URL Response
│ │ │ │ └── URLResponse+IsSuccessHTTPStatusCode.swift
│ │ │ ├── Array
│ │ │ │ ├── Array+Appending.swift
│ │ │ │ ├── Array+ReversedOnCondition.swift
│ │ │ │ └── Array+RemoveIfPresent.swift
│ │ │ └── Bundle
│ │ │ │ └── Bundle+Info.swift
│ │ ├── UIKit
│ │ │ ├── UI View
│ │ │ │ ├── UIView+RemoveSubviews.swift
│ │ │ │ ├── UIView+WithTranslatesAutoresizingMaskIntoConstraints.swift
│ │ │ │ └── UIView+DismissKeyboardOnOutsideTap.swift
│ │ │ ├── UI Device
│ │ │ │ ├── UIDevice+IsSimulator.swift
│ │ │ │ ├── UIDevice+SafeAreaInsets.swift
│ │ │ │ └── UIDevice+HasNoPhysicalHomeButton.swift
│ │ │ ├── UI Image
│ │ │ │ ├── UIImage+Compressed.swift
│ │ │ │ └── UIImage+InitWithUIColor.swift
│ │ │ ├── UI NavigationBar
│ │ │ │ └── UINavigationBar+Height.swift
│ │ │ ├── UI Application
│ │ │ │ ├── UIApplication+SendResignFirstResponderAction.swift
│ │ │ │ └── UIApplication+TopmostViewController.swift
│ │ │ ├── UI Screen
│ │ │ │ └── UIScreen+DisplayCornerRadius.swift
│ │ │ ├── UI View Controller
│ │ │ │ └── UIViewController+WithTabBarItem.swift
│ │ │ ├── UI StackView
│ │ │ │ └── UIStackView+RemoveArrangedSubviews.swift
│ │ │ └── UI RectCorner
│ │ │ │ └── UIRectCorner+AdditionalOptions.swift
│ │ ├── Core Graphics
│ │ │ ├── CG Size
│ │ │ │ ├── CGSize+InitWithDimension.swift
│ │ │ │ ├── CGSize+WithReversedDimensions.swift
│ │ │ │ └── CGSize+MinAndMaxDimensions.swift
│ │ │ └── CG Point
│ │ │ │ └── CGPoint+WithReversedCoordinates.swift
│ │ ├── SwiftUI
│ │ │ ├── Color
│ │ │ │ ├── Color+InitWithRGBA.swift
│ │ │ │ ├── Color+PrimaryInverted.swift
│ │ │ │ ├── Color+Dynamic.swift
│ │ │ │ ├── Color+PlatformDynamic.swift
│ │ │ │ └── Color+LightenAndDarken.swift
│ │ │ ├── View
│ │ │ │ ├── View+EraseToAnyView.swift
│ │ │ │ ├── View+GetSize.swift
│ │ │ │ ├── View+OnSimultaneousTapGesture.swift
│ │ │ │ ├── View+ShadowWithPoint.swift
│ │ │ │ ├── View+GetBounds.swift
│ │ │ │ ├── View+BlocksHitTesting.swift
│ │ │ │ ├── View+ApplyModifier.swift
│ │ │ │ └── View+GetSafeAreaInsets.swift
│ │ │ ├── Binding
│ │ │ │ └── Binding+UnwrappedBinding.swift
│ │ │ ├── Navigation Path
│ │ │ │ └── NavigationPath+Methods.swift
│ │ │ └── Image
│ │ │ │ └── Image+InitWithData.swift
│ │ ├── WatchKit
│ │ │ └── WK Interface Device
│ │ │ │ └── WKInterfaceDevice+IsSimulator.swift
│ │ └── UIKit and AppKit
│ │ │ └── NS Layout Constraint
│ │ │ ├── NSLayoutConstraint+Storing.swift
│ │ │ └── NSLayoutConstraint+WithMultiplier.swift
│ ├── Resources
│ │ └── PrivacyInfo.xcprivacy
│ ├── Macros
│ │ ├── Freestanding (Expression)
│ │ │ ├── URL_InitWithString.swift
│ │ │ ├── Color_InitWithHexUInt.swift
│ │ │ └── Color_InitWithHexString.swift
│ │ └── Attached
│ │ │ ├── Uninitializable.swift
│ │ │ └── OptionSetRepresentation.swift
│ ├── Helpers
│ │ ├── Architectural Pattern Helpers
│ │ │ ├── SwiftUI
│ │ │ │ ├── Coordinating Navigation Stack
│ │ │ │ │ └── EnvironmentValues+NavigationStackCoordinator.swift
│ │ │ │ ├── Coordinating Navigation Stack (Observable Object)
│ │ │ │ │ └── EnvironmentValues+NavigationStackCoordinatorOO.swift
│ │ │ │ ├── Alert
│ │ │ │ │ ├── AlertButtonProtocol.swift
│ │ │ │ │ └── AlertButtonConvertible.swift
│ │ │ │ └── Confirmation Dialog
│ │ │ │ │ ├── ConfirmationDialogButtonProtocol.swift
│ │ │ │ │ └── ConfirmationDialogButtonConvertible.swift
│ │ │ └── UIKit
│ │ │ │ ├── UI Alert
│ │ │ │ ├── UIAlertButtonProtocol.swift
│ │ │ │ └── UIAlertButtonConvertible.swift
│ │ │ │ └── UI Action Sheet
│ │ │ │ ├── UIActionSheetButtonProtocol.swift
│ │ │ │ └── UIActionSheetButtonConvertible.swift
│ │ └── Logging.swift
│ └── Models
│ │ ├── AccessLevelModifierKeyword.swift
│ │ ├── Custom Results
│ │ └── ResultNoSuccessNoFailure.swift
│ │ ├── Atomics
│ │ └── AtomicContainer.swift
│ │ ├── ObservableContainerOO.swift
│ │ ├── CastingError.swift
│ │ ├── HTTP Header Fields
│ │ └── JSONRequestHeaderFields.swift
│ │ ├── PointPixelMeasurement.swift
│ │ ├── AbsoluteFractionMeasurement.swift
│ │ └── VCoreError.swift
├── VCoreMacrosImplementation
│ ├── Macros
│ │ └── Attached
│ │ │ ├── Memberwise Initializable Macro
│ │ │ └── MemberwiseInitializableParameterDefaultValue.swift
│ │ │ ├── Coding Keys Generation Macro
│ │ │ └── CKGPropertyMacro.swift
│ │ │ └── Uninitializable Macro
│ │ │ └── UninitializableMacro.swift
│ ├── Extensions
│ │ ├── String+RemovingReservedKeywordBackticks.swift
│ │ ├── TokenKind+TypeCasts.swift
│ │ ├── AttributeSyntax_Arguments+TypeCasts.swift
│ │ └── LabeledExprListSyntax+ElementAtIndex.swift
│ ├── Models
│ │ ├── RawStringError.swift
│ │ └── AccessLevelModifierKeyword.swift
│ └── Plugin.swift
└── VCoreShared
│ └── Extensions
│ └── Foundation
│ ├── String
│ ├── String+RemovingCharacterSet.swift
│ └── String+HexColorRGBValues.swift
│ └── U Int
│ └── UInt+HexColorRGBAValues.swift
├── Package.resolved
├── Tests
└── VCoreTests
│ ├── Tests
│ ├── Extensions
│ │ ├── Foundation
│ │ │ ├── Numeric
│ │ │ │ ├── NumericNonZeroTests.swift
│ │ │ │ └── NumericWithOppositeSignTests.swift
│ │ │ ├── Bool
│ │ │ │ └── BoolToggledTests.swift
│ │ │ ├── Range and ClosedRange
│ │ │ │ ├── ClosedRangeBoundRangeTests.swift
│ │ │ │ ├── RangeInitWithBoundsTests.swift
│ │ │ │ ├── ClosedRangeInitWithBoundsTests.swift
│ │ │ │ ├── RangeReversedArrayOnConditionTests.swift
│ │ │ │ └── ClosedRangeReversedArrayOnConditionTests.swift
│ │ │ ├── Sequence
│ │ │ │ ├── SequenceIsUniqueTests.swift
│ │ │ │ ├── SequenceContainsDuplicatesTests.swift
│ │ │ │ ├── SequenceMinAndMaxByKeyPathTests.swift
│ │ │ │ ├── SequenceConditionalGroupingTests.swift
│ │ │ │ └── SequenceSortedByKeyPathTests.swift
│ │ │ ├── Collection
│ │ │ │ ├── CollectionNonEmptyTests.swift
│ │ │ │ ├── CollectionContainsAnyItemTests.swift
│ │ │ │ ├── CollectionSafeSubscriptTests.swift
│ │ │ │ ├── CollectionEnumeratedArrayTests.swift
│ │ │ │ ├── CollectionRandomElementsTests.swift
│ │ │ │ └── CollectionAllMatchTests.swift
│ │ │ ├── Set
│ │ │ │ ├── SetInsertSequenceTests.swift
│ │ │ │ └── SetTogglingElementTests.swift
│ │ │ ├── StringProtocol
│ │ │ │ ├── StringProtocolDiacriticInsensitiveStringTests.swift
│ │ │ │ ├── StringProtocolSubscriptTests.swift
│ │ │ │ ├── StringProtocolReplacedTests.swift
│ │ │ │ └── StringProtocolContainsOnlyCharacterSetTests.swift
│ │ │ ├── CharacterSet
│ │ │ │ └── CharacterSetUnifiedTests.swift
│ │ │ ├── String
│ │ │ │ ├── StringIsUserDefaultsKeyTests.swift
│ │ │ │ ├── StringRemovingCharacterSetTests.swift
│ │ │ │ └── StringKeepingOnlyCharacterSetTests.swift
│ │ │ ├── Data
│ │ │ │ └── DataNonEmptyTests.swift
│ │ │ ├── Array
│ │ │ │ ├── ArrayBinaryAppendTests.swift
│ │ │ │ ├── ArrayAppendingTests.swift
│ │ │ │ ├── ArrayPrependingTests.swift
│ │ │ │ ├── ArrayRemovingDuplicatesTests.swift
│ │ │ │ ├── ArrayRemoveIfPresentTests.swift
│ │ │ │ ├── ArrayReversedOnConditionTests.swift
│ │ │ │ └── ArrayAsyncSortedTests.swift
│ │ │ ├── Int
│ │ │ │ └── IntRunBlockNTimesTests.swift
│ │ │ ├── Date
│ │ │ │ ├── DateAgeTests.swift
│ │ │ │ └── DateComponentTests.swift
│ │ │ └── OptionSet
│ │ │ │ └── OptionSetElementsTests.swift
│ │ ├── UIKit
│ │ │ ├── UINavigationBar
│ │ │ │ └── UINavigationBarHeightTests.swift
│ │ │ ├── UIColor
│ │ │ │ ├── UIColorMixTests.swift
│ │ │ │ ├── UIColorLightenAndDarkenTests.swift
│ │ │ │ └── UIColorRGBAValuesTests.swift
│ │ │ ├── UIView
│ │ │ │ ├── UIViewRoundCornersTests.swift
│ │ │ │ ├── UIViewControllerWithTabBarItemTests.swift
│ │ │ │ └── UIViewWithTranslatesAutoresizingMaskIntoConstraintsTests.swift
│ │ │ ├── UIRectCorner
│ │ │ │ └── UIRectCornerAdditionalOptionsTests.swift
│ │ │ ├── UIFont
│ │ │ │ └── UIFontStylingTests.swift
│ │ │ └── UIImage
│ │ │ │ └── UIImageCompressedTests.swift
│ │ ├── CoreGraphics
│ │ │ ├── CGSIze
│ │ │ │ ├── CGSizeInitWithDimensionTests.swift
│ │ │ │ ├── CGSizeMinAndMaxDimensionsTests.swift
│ │ │ │ └── CGSizeWithReversedDimensionsTests.swift
│ │ │ └── CGPoint
│ │ │ │ └── CGPointWithReversedCoordinatesTests.swift
│ │ ├── SwiftUI
│ │ │ └── Color
│ │ │ │ └── ColorInitWithHexTests.swift
│ │ ├── QuartzCore
│ │ │ └── CACornerMask
│ │ │ │ └── CACornerMaskAdditionalOptionsTests.swift
│ │ ├── AppKit
│ │ │ ├── NSColor
│ │ │ │ ├── NSColorMixTests.swift
│ │ │ │ ├── NSColorLightenAndDarkenTests.swift
│ │ │ │ └── NSColorRGBAValuesTests.swift
│ │ │ └── NSFont
│ │ │ │ └── NSFontStylingTests.swift
│ │ ├── UIKit and AppKit
│ │ │ └── NSLayoutConstraint
│ │ │ │ ├── NSLayoutConstraintStoringTests.swift
│ │ │ │ └── NSLayoutConstraintInitWithPriorityTests.swift
│ │ └── Combine
│ │ │ └── Publisher
│ │ │ └── PublisherAsignWeakTests.swift
│ ├── Global Functions
│ │ ├── LogicalExclusiveOrTests.swift
│ │ └── AreEqualWithinToleranceTests.swift
│ ├── Models
│ │ ├── PointPixelMeasurementTests.swift
│ │ ├── AbsoluteFractionMeasurementTests.swift
│ │ ├── Custom Results
│ │ │ └── ResultNoSuccessNoFailureTests.swift
│ │ └── KeyPathInitializableEnumerationTests.swift
│ └── Services and Managers
│ │ ├── SessionManagerTests.swift
│ │ └── AutoPrecisionNumberFormatterTests.swift
│ └── Extensions
│ ├── XCTestCase+AssertInstanceIsDeallocated.swift
│ ├── UIImage+MergeHorizontally.swift
│ └── String+CombiningDebugItems.swift
├── LICENSE.md
└── .gitignore
/Extra/XCode Templates/Locations.txt:
--------------------------------------------------------------------------------
1 | ~/Library/Developer/Xcode/Templates/File Templates
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/View.xctemplate/___FILEBASENAME___UIModel.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import SwiftUI
4 |
5 | // MARK: - ___VARIABLE_productName___ UI Model
6 | struct ___VARIABLE_productName___UIModel {
7 | var backgroundColor: Color = .init(uiColor: .systemBackground)
8 | }
9 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/1. Intro/1.1. Intro.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | This style guide aims to improve code readability by standardizing code organization and formatting rules. A consistent codebase is easier to read, understand, maintain, and modify, as it fosters familiarity and predictability.
4 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/___FILEBASENAME___Parameters.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 | import VCore
5 |
6 | // MARK: - ___VARIABLE_productName___ Parameters
7 | @CodingKeysGeneration
8 | struct ___VARIABLE_productName___Parameters: Encodable {
9 | // ...
10 | }
11 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/___FILEBASENAME___GatewayProtocol.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 |
5 | // MARK: - ___VARIABLE_productName___ Gateway Protocol
6 | protocol ___VARIABLE_productName___GatewayProtocol {
7 | func fetch(with parameters: ___VARIABLE_productName___Parameters) async throws -> ___VARIABLE_productName___Entity
8 | }
9 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.4. Container Types.md:
--------------------------------------------------------------------------------
1 | # Container Types
2 |
3 | Container types, such as variables, properties, and dictionary element types, should be spaced out with a single space.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | let number:Int = 10
9 | ```
10 |
11 | Preferred:
12 |
13 | ```swift
14 | let number: Int = 10
15 | ```
16 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.1. Intro.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | Developers often have their own personal style when it comes to using spacing and brackets in their code. However, these personal styles tend to be inconsistent and lack fundamental rules. This document aims to standardize the use of spacing and brackets through simple rules to improve code readability and consistency.
4 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Base Button (SwiftUI)/SwiftUIBaseButtonState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIBaseButtonState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 29.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Swift UI Base Button State
11 | /// Enumeration that represents state.
12 | public typealias SwiftUIBaseButtonState = GenericState_EnabledPressedDisabled
13 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/View.xctemplate/___FILEBASENAME___Parameters.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 |
5 | // MARK: - ___VARIABLE_productName___ Parameters
6 | struct ___VARIABLE_productName___Parameters {
7 | // MARK: Properties
8 | // ...
9 |
10 | // MARK: Mock
11 | #if DEBUG
12 | static var mock: Self {
13 | .init()
14 | }
15 | #endif
16 | }
17 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/ContentView (MV).xctemplate/___FILEBASENAME___Parameters.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 |
5 | // MARK: - ___VARIABLE_productName___ Parameters
6 | struct ___VARIABLE_productName___Parameters: Hashable {
7 | // MARK: Properties
8 | // ...
9 |
10 | // MARK: Mock
11 | #if DEBUG
12 | static var mock: Self {
13 | .init()
14 | }
15 | #endif
16 | }
17 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/4. Context and Clarity/4.1. Intro.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | The modern software industry has seen a rise in the complexity of codebases, making them harder to read and understand. As a result, readability and clarity have become the most important qualities in code. It is essential to adopt styles or approaches that provide more context and clarity at the call site, rather than those that hide or infer them.
4 |
--------------------------------------------------------------------------------
/Sources/VCore/Global Functions/LogicalExclusiveOr.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LogicalExclusiveOr.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/13/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Logical Exclusive Or
11 | /// Exclusive "or" logical operator.
12 | infix operator ^^
13 |
14 | /// Exclusive "or" logical operator.
15 | public func ^^(lhs: Bool, rhs: Bool) -> Bool {
16 | lhs != rhs
17 | }
18 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.5. Subclasses and Protocol Conformances.md:
--------------------------------------------------------------------------------
1 | # Subclasses and Protocol Conformances
2 |
3 | Subclasses and protocol conformances should be spaced out with a single space.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | struct SomeStruct : SomeProtocol {
9 | ...
10 | }
11 | ```
12 |
13 | Preferred:
14 |
15 | ```swift
16 | struct SomeStruct: SomeProtocol {
17 | ...
18 | }
19 | ```
20 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/___FILEBASENAME___Entity.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 | import VCore
5 |
6 | // MARK: - ___VARIABLE_productName___ Entity
7 | @CodingKeysGeneration
8 | struct ___VARIABLE_productName___Entity: Decodable {
9 | // MARK: Properties
10 | // ...
11 |
12 | // MARK: Mock
13 | #if DEBUG
14 | static var mock: Self {
15 | .init()
16 | }
17 | #endif
18 | }
19 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/___FILEBASENAME___Gateway.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import Foundation
4 | import VCore
5 |
6 | // MARK: - ___VARIABLE_productName___ Gateway
7 | struct ___VARIABLE_productName___Gateway: ___VARIABLE_productName___GatewayProtocol {
8 | func fetch(with parameters: ___VARIABLE_productName___Parameters) async throws -> ___VARIABLE_productName___Entity {
9 | FIXME()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.2. Tabs.md:
--------------------------------------------------------------------------------
1 | # Tabs
2 |
3 | The use of tabs or spaces for indentation is a matter of personal preference. However, it's important to maintain consistency within a codebase. For this style guide, we recommend using 4 spaces for indentation, as it helps to visualize large and nested blocks of code. Additionally, 4 spaces is a widely accepted standard in the programming community. No need to reinvent a wheel here.
4 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/2. Code Organization/2.1. Intro.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | Software has become increasingly bloated, making codebases more difficult to comprehend. Collaborating on and modifying code can become arduous, as there is often no pre-established style within the project, leading to inconsistent functionality placement and a lack of standardized layout and sections. This can result in a loss of productivity, increased risk of errors, and longer development cycles.
4 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/Mock___FILEBASENAME___Gateway.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | #if DEBUG
4 |
5 | import Foundation
6 |
7 | // MARK: - Mock ___VARIABLE_productName___ Gateway
8 | struct Mock___VARIABLE_productName___Gateway: ___VARIABLE_productName___GatewayProtocol {
9 | func fetch(with parameters: ___VARIABLE_productName___Parameters) async throws -> ___VARIABLE_productName___Entity {
10 | .mock
11 | }
12 | }
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "85697bd024780b859eafcfa2729311340ca3cc818603b0e6417291683d0f9de7",
3 | "pins" : [
4 | {
5 | "identity" : "swift-syntax",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/swiftlang/swift-syntax.git",
8 | "state" : {
9 | "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2",
10 | "version" : "601.0.1"
11 | }
12 | }
13 | ],
14 | "version" : 3
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Touch Sensitive Container/TouchSensitiveContainerState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchSensitiveContainerState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 17.09.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Touch Sensitive Container Internal State
11 | /// Enumeration that represents state.
12 | @available(tvOS, unavailable)
13 | public typealias TouchSensitiveContainerInternalState = GenericState_EnabledPressedDisabled
14 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Numeric/NumericNonZeroTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumericNonZeroTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 26.11.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct NumericNonZeroTests {
15 | @Test
16 | func test() {
17 | #expect(0.nonZero == nil)
18 | #expect(1.nonZero == 1)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Bool/BoolToggledTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BoolToggledTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 03.09.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct BoolToggledTests {
15 | @Test
16 | func test() {
17 | #expect(false.toggled() == true)
18 | #expect(true.toggled() == false)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Range and ClosedRange/ClosedRangeBoundRangeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClosedRangeBoundRangeTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 05.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ClosedRangeBoundRangeTests {
15 | @Test
16 | func test() {
17 | #expect((3...10).boundRange == 7)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Range and ClosedRange/RangeInitWithBoundsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RangeInitWithBoundsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 02.07.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct RangeInitWithBoundsTests {
15 | @Test
16 | func test() {
17 | #expect(Range(lower: 1, upper: 10) == 1..<10)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Sequence/SequenceIsUniqueTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceIsUniqueTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SequenceIsUniqueTests {
15 | @Test
16 | func test() {
17 | #expect([1, 3, 5].isUnique)
18 | #expect(![1, 1, 3, 5].isUnique)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Macros/Attached/Memberwise Initializable Macro/MemberwiseInitializableParameterDefaultValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MemberwiseInitializableParameterDefaultValue.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 23.05.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - _ Memberwise Initializable Parameter Default Value
11 | enum MemberwiselnitializableParameterDefaultValue {
12 | case value(String)
13 | case omit
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Extensions/String+RemovingReservedKeywordBackticks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+RemovingReservedKeywordBackticks.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 | import VCoreShared
10 |
11 | // MARK: - String + Removing Reserved Keyword Backticks
12 | extension String {
13 | func removingReservedKeywordBackticks() -> String {
14 | _removing(CharacterSet(arrayLiteral: "`"))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionNonEmptyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionNonEmptyTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 17.11.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionNonEmptyTests {
15 | @Test
16 | func test() {
17 | #expect(Array().nonEmpty == nil)
18 | #expect([1, 2, 3].nonEmpty == [1, 2, 3])
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Touch Sensitive Container/TouchSensitiveContainerContent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchSensitiveContainerContent.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 17.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Touch Sensitive Container Content
11 | @available(tvOS, unavailable)
12 | enum TouchSensitiveContainerContent where Content: View {
13 | case content(() -> Content)
14 | case contentWithState((TouchSensitiveContainerInternalState) -> Content)
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/View Resetting Container/EnvironmentValues+ViewResetter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+ViewResetter.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 26.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + View Resetter
11 | @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
12 | extension EnvironmentValues {
13 | /// `ViewResetter` of the `View` associated with the environment.
14 | @Entry public var viewResetter: ViewResetter?
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Extensions/TokenKind+TypeCasts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TokenKind+TypeCasts.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 19.04.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 |
11 | // MARK: - Token Kind + Type Casts
12 | extension TokenKind {
13 | func toKeywordAssociatedValue() -> Keyword? {
14 | if case .keyword(let keyword) = self {
15 | keyword
16 | } else {
17 | nil
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Set/SetInsertSequenceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetInsertSequenceTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 18.12.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SetInsertSequenceTests {
15 | @Test
16 | func test() {
17 | var set: Set = [1, 2]
18 | set.insert(contentsOf: [3, 4])
19 |
20 | #expect(set == [1, 2, 3, 4])
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Models/RawStringError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RawStringError.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 08.02.25.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Raw String Error
11 | struct RawStringError: Error, CustomStringConvertible {
12 | // MARK: Properties
13 | let description: String
14 |
15 | // MARK: Initializers
16 | init(
17 | _ description: String
18 | ) {
19 | self.description = description
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Sequence/SequenceContainsDuplicatesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceContainsDuplicatesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 23.01.24.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SequenceContainsDuplicatesTests {
15 | @Test
16 | func test() {
17 | #expect(![1, 3, 5].containsDuplicates)
18 | #expect([1, 1, 3, 5].containsDuplicates)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/VCore/Services and Managers/Presentation Host/Geometry/EnvironmentValue+PresentationHostContainerSize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+PresentationHostContainerSize.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 14.07.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + Presentation Host Container Size
11 | extension EnvironmentValues {
12 | /// Presentation Host's container size associated with the environment.
13 | @Entry public var presentationHostContainerSize: CGSize = .zero
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/StringProtocol/StringProtocolDiacriticInsensitiveStringTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocolDiacriticInsensitiveStringTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 13.07.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringProtocolDiacriticInsensitiveStringTests {
15 | @Test
16 | func test() {
17 | #expect("À".diacriticInsensitiveString() == "A")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UINavigationBar/UINavigationBarHeightTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UINavigationBarHeightTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct UINavigationBarHeightTests {
18 | @Test
19 | func test() {
20 | #expect(UINavigationBar.height > 0)
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Global Functions/LogicalExclusiveOrTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LogicalExclusiveOrTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct LogicalExclusiveOrTests {
15 | @Test
16 | func test() {
17 | #expect(!(false ^^ false))
18 | #expect(false ^^ true)
19 | #expect(true ^^ false)
20 | #expect(!(true ^^ true))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Range and Closed Range/Range+InitWithBounds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Range+InitWithBounds.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 02.07.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Range + Init with Bounds
11 | extension Range {
12 | /// Initializes `Range` with lower and upper bounds.
13 | ///
14 | /// let range: Range = .init(lower: 1, upper: 10)
15 | ///
16 | public init(lower: Bound, upper: Bound) {
17 | self = lower..
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryUserDefaults
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | C56D.1
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Sources/VCore/Global Functions/Debugging/TODO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TODO.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/10/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - TODO
11 | /// Calls `fatalError` because feature is not implemented.
12 | ///
13 | /// func didTapContinueButton() {
14 | /// TODO()
15 | /// }
16 | ///
17 | public func TODO(
18 | _ message: String? = nil
19 | ) -> Never {
20 | var string: String = "TODO not implemented"
21 | message.map { string += ": \($0)" }
22 |
23 | fatalError(string)
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Numeric/Numeric+NonZero.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Numeric+NonZero.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 26.11.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Numeric + Non-Zero
11 | extension Numeric {
12 | /// Returns non-zero sized `Numeric`, or `nil`.
13 | ///
14 | /// let number1: Int? = 0.nonZero // nil
15 | /// let number2: Int? = 1.nonZero // 1
16 | ///
17 | public var nonZero: Self? {
18 | guard self != 0 else { return nil }
19 | return self
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/VCore/Global Functions/Debugging/FIXME.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FIXME.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 31.12.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - FIXME
11 | /// Calls `fatalError` because feature is not implemented.
12 | ///
13 | /// func didTapContinueButton() {
14 | /// FIXME()
15 | /// }
16 | ///
17 | public func FIXME(
18 | _ message: String? = nil
19 | ) -> Never {
20 | var string: String = "FIXME not implemented"
21 | message.map { string += ": \($0)" }
22 |
23 | fatalError(string)
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Range and Closed Range/ClosedRange+BoundRange.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClosedRange+BoundRange.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 12/9/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Closed Range + Bound Range
11 | extension ClosedRange where Bound: AdditiveArithmetic {
12 | /// Difference between `upperBound` and `lowerBound`.
13 | ///
14 | /// let boundRange: Double = (3...10).boundRange // 7.0
15 | ///
16 | public var boundRange: Bound {
17 | upperBound - lowerBound
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Range and Closed Range/ClosedRange+InitWithBounds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClosedRange+InitWithBounds.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 05.08.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Closed Range + Init with Bounds
11 | extension ClosedRange {
12 | /// Initializes `ClosedRange` with lower and upper bounds.
13 | ///
14 | /// let range: ClosedRange = .init(lower: 1, upper: 10)
15 | ///
16 | public init(lower: Bound, upper: Bound) {
17 | self = lower...upper
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Numeric/NumericWithOppositeSignTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumericWithOppositeSignTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct NumericWithOppositeSignTests {
15 | @Test
16 | func test() {
17 | #expect(10.withOppositeSign(false) == 10)
18 | #expect(10.withOppositeSign(true) == -10)
19 | #expect(10.withOppositeSign() == -10)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI View/UIView+RemoveSubviews.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+RemoveSubviews.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 09.07.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI View + Remove Subviews
13 | extension UIView {
14 | /// Removes all `UIView`s from superview.
15 | ///
16 | /// view.removeSubviewsFromSuperview()
17 | ///
18 | public func removeSubviewsFromSuperview() {
19 | subviews.forEach { $0.removeFromSuperview() }
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Core Graphics/CG Size/CGSize+InitWithDimension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize+InitWithDimension.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | // MARK: - CG Size + Init with Dimension
11 | extension CGSize {
12 | /// Initializes `CGSize` with dimension.
13 | ///
14 | /// let size: CGSize = .init(dimension: 100)
15 | ///
16 | public init(dimension: CGFloat) {
17 | self.init(
18 | width: dimension,
19 | height: dimension
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/String/String+IsUserDefaultsKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+IsUserDefaultsKey.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String + Is User Defaults Key
11 | extension String {
12 | /// Checks if value of this key is stored in `UserDefaults`.
13 | ///
14 | /// "key".isUserDefaultsKey()
15 | ///
16 | public func isUserDefaultsKey(
17 | in store: UserDefaults = .standard
18 | ) -> Bool {
19 | store.object(forKey: self) != nil
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/CharacterSet/CharacterSetUnifiedTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CharacterSetUnifiedTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.06.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CharacterSetUnifiedTests {
15 | @Test
16 | func test() {
17 | #expect(
18 | [CharacterSet(charactersIn: "A"), CharacterSet(charactersIn: "B")].unified ==
19 | CharacterSet(charactersIn: "AB")
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Data/Data+NonEmpty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+NonEmpty.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 03.09.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Data + Non-Empty
11 | extension Data {
12 | /// Returns non-zero sized `Data`, or `nil`.
13 | ///
14 | /// let data1: Data? = .init().nonEmpty // nil
15 | /// let data2: Data? = .init("data".data(using: .utf8)!).nonEmpty // ...
16 | ///
17 | public var nonEmpty: Data? {
18 | guard !isEmpty else { return nil }
19 | return self
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/VCore/Services and Managers/Presentation Host/Presentation Mode/EnvironmentValues+PresentationHostPresentationMode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+PresentationHostPresentationMode.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + Presentation Host Presentation Mode
11 | extension EnvironmentValues {
12 | /// Presentation Host's presentation mode of the `View` associated with the environment.
13 | @Entry public var presentationHostPresentationMode: PresentationHostPresentationMode?
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Character Set/CharacterSet+Unified.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CharacterSet+Unified.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10.06.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Character Set + Unified
11 | extension Array where Element == CharacterSet {
12 | /// Returns a union of the CharacterSet `Array`s.
13 | ///
14 | /// let unifiedCharacterSet: CharacterSet = [.decimalDigits, .letters, .symbols].unified
15 | ///
16 | public var unified: CharacterSet {
17 | reduce(CharacterSet(), { $0.union($1) })
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+NonEmpty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+NonEmpty.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 17.11.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + Non-Empty
11 | extension Collection {
12 | /// Returns non-empty `Collection`, or `nil`.
13 | ///
14 | /// let array1: [Int]? = [].nonEmpty // nil
15 | /// let array2: [Int]? = [1, 2, 3].nonEmpty // [1, 2, 3]
16 | ///
17 | public var nonEmpty: Self? {
18 | guard !isEmpty else { return nil }
19 | return self
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/VCore/Global Functions/AreEqualWithinTolerance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AreEqualWithinTolerance.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 22.01.25.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Are Equal Within Tolerance
11 | /// Returns `Bool` indicating if values are equal within a given tolerance.
12 | ///
13 | /// areEqual(3.14, 3.1415, tolerance: pow(10, -2)) // true
14 | ///
15 | public func areEqual(
16 | _ lhs: T,
17 | _ rhs: T,
18 | tolerance: T
19 | ) -> Bool
20 | where T: SignedNumeric & Comparable
21 | {
22 | abs(lhs - rhs) <= tolerance
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/View Resetting Container (Observable Object)/EnvironmentValues+ViewResetterOO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+ViewResetterOO.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 23.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + View Resetter (Observable Object)
11 | extension EnvironmentValues {
12 | /// `ViewResetterOO` of the `View` associated with the environment.
13 | ///
14 | /// Since `ViewResetterOO` is a reference type, `View` updates won't be triggered.
15 | @Entry public var viewResetterOO: ViewResetterOO?
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Table View/InfiniteScrollingUITableView+PaginationState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingTableView+PaginationState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Infinite Scrolling UI Table View + Pagination State
13 | extension InfiniteScrollingUITableView {
14 | /// Enumeration that represents state.
15 | public typealias PaginationState = InfiniteScrollingPaginationState
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Table View/InfiniteScrollingUITableViewActivityIndicatorViewUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingUITableViewActivityIndicatorViewUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/10/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - Infinite Scrolling UI Table View Activity Indicator View UI Model
13 | @Uninitializable
14 | struct InfiniteScrollingUITableViewActivityIndicatorViewUIModel {
15 | static var height: CGFloat { 100 }
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/VCoreShared/Extensions/Foundation/String/String+RemovingCharacterSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+RemovingCharacterSet.swift
3 | // VCoreShared
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String + Removing Character Set
11 | extension String {
12 | package func _removing(_ characterSet: CharacterSet) -> String {
13 | filter { char in
14 | guard let unicodeScalar: Unicode.Scalar = char.unicodeScalars.first else { return false }
15 | return !characterSet.contains(unicodeScalar)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/SwiftUI/Color/ColorInitWithHexTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorInitWithHexTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 20.05.23.
6 | //
7 |
8 | // Other test cases are covered under `UIColorInitWithHexTests`
9 |
10 | import SwiftUI
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct ColorInitWithHexTests {
17 | @Test
18 | func test() {
19 | #expect(
20 | Color(hex: "#007AFF") ==
21 | Color(red: 0/255, green: 122/255, blue: 255/255)
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Plain List/PlainListUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlainListUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 27.11.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Plain List UI Model
11 | /// Model that describes UI.
12 | @available(tvOS, unavailable)
13 | @available(watchOS, unavailable)
14 | public struct PlainListUIModel: Sendable {
15 | // MARK: Properties
16 | /// Row background color.
17 | public var rowBackgroundColor: Color = .clear
18 |
19 | // MARK: Initializers
20 | /// Initializes UI model with default values.
21 | public init() {}
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Collection View/InfiniteScrollingUICollectionViewActivityIndicatorViewUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingUICollectionViewActivityIndicatorViewUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Infinite Scrolling UI Collection View Activity Indicator View UI Model
13 | typealias InfiniteScrollingUICollectionViewActivityIndicatorViewUIModel = InfiniteScrollingUITableViewActivityIndicatorViewUIModel
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/CoreGraphics/CGSIze/CGSizeMinAndMaxDimensionsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSizeMinAndMaxDimensionsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.09.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CGSizeMinMaxDimensionsTests {
15 | @Test
16 | func testMin() {
17 | #expect(CGSize(width: 3, height: 4).minDimension() == 3)
18 | }
19 |
20 | @Test
21 | func testMax() {
22 | #expect(CGSize(width: 3, height: 4).maxDimension() == 4)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Device/UIDevice+IsSimulator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+IsSimulator.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Device + Is Simulator
13 | extension UIDevice {
14 | /// Indicates if device is simulator.
15 | ///
16 | /// let isSimulator: Bool = UIDevice.isSimulator
17 | ///
18 | public static var isSimulator: Bool {
19 | #if targetEnvironment(simulator)
20 | true
21 | #else
22 | false
23 | #endif
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Macros/Freestanding (Expression)/URL_InitWithString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL_InitWithString.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 07.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - URL (Init with String)
11 | /// Returns non-optional `URL` from `String`, checked during the compile time.
12 | ///
13 | /// let url: URL = #url("https://example.com")
14 | ///
15 | @freestanding(expression)
16 | public macro url(
17 | _ urlString: String
18 | ) -> URL = #externalMacro(
19 | module: "VCoreMacrosImplementation",
20 | type: "URLMacro_InitWithString"
21 | )
22 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Collection View/InfiniteScrollingUICollectionView+PaginationState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingCollectionView+PaginationState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - Infinite Scrolling UI Collection View + Pagination State
13 | extension InfiniteScrollingUICollectionView {
14 | /// Enumeration that represents state.
15 | public typealias PaginationState = InfiniteScrollingPaginationState
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Color/Color+InitWithRGBA.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+InitWithRGBA.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 14.07.24.
6 | //
7 |
8 | // Internal use only
9 |
10 | import SwiftUI
11 |
12 | // MARK: - Color + Init with RGBA
13 | extension Color {
14 | init(
15 | _ red: CGFloat,
16 | _ green: CGFloat,
17 | _ blue: CGFloat,
18 | _ opacity: CGFloat = 1
19 | ) {
20 | self.init(
21 | red: red/255,
22 | green: green/255,
23 | blue: blue/255,
24 | opacity: opacity
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Range and ClosedRange/RangeReversedArrayOnConditionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RangeReversedArrayOnConditionTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct RangeReversedArrayOnConditionTests {
15 | @Test
16 | func test() {
17 | #expect((1..<4).reversedArray(false) == [1, 2, 3])
18 | #expect((1..<4).reversedArray(true) == [3, 2, 1])
19 | #expect((1..<4).reversedArray() == [3, 2, 1])
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/String/StringIsUserDefaultsKeyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringIsUserDefaultsKeyTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringIsUserDefaultsKeyTests { // Can't be properly tested
15 | @Test
16 | func test() {
17 | let store: UserDefaults = .mock
18 | let key: String = "Key"
19 |
20 | store.set(1, forKey: key)
21 |
22 | #expect(key.isUserDefaultsKey(in: store))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Coordinating Navigation Stack/EnvironmentValues+NavigationStackCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+NavigationStackCoordinator.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 26.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + Navigation Stack Coordinator
11 | @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
12 | extension EnvironmentValues {
13 | /// `NavigationStackCoordinator` of the `View` associated with the environment.
14 | @Entry public var navigationStackCoordinator: NavigationStackCoordinator?
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Scrollable UI View/ScrollableUIView+ScrollDirection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollableUIView+ScrollDirection.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 2/25/22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Scrollable UI View + Scroll Direction
13 | extension ScrollableUIView {
14 | /// Scroll Direction.
15 | @OptionSetRepresentation
16 | public struct ScrollDirection: Sendable {
17 | private enum Options: Int {
18 | case horizontal
19 | case vertical
20 | }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Extensions/AttributeSyntax_Arguments+TypeCasts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AttributeSyntax_Arguments+TypeCasts.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 |
11 | // MARK: - Attribute Syntax Arguments + Type Casts
12 | extension AttributeSyntax.Arguments {
13 | func toArgumentListGetAssociatedValue() -> LabeledExprListSyntax? {
14 | if case .argumentList(let labeledExprListSyntax) = self {
15 | labeledExprListSyntax
16 | } else {
17 | nil
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Extensions/LabeledExprListSyntax+ElementAtIndex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LabeledExprListSyntax+ElementAtIndex.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 |
11 | // MARK: - Labeled Expr List Syntax + Element at Index
12 | extension LabeledExprListSyntax {
13 | func element(at index: Int) -> Element? {
14 | let index: SyntaxChildrenIndex = self.index(startIndex, offsetBy: index)
15 |
16 | guard indices.contains(index) else { return nil }
17 |
18 | return self[index]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Models/PointPixelMeasurementTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PointPixelMeasurementTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.08.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct PointPixelMeasurementTests {
15 | @Test
16 | func testConvertPixelsToPoints() {
17 | #expect(PointPixelMeasurement.pixels(3).toPoints(scale: 3) == 1)
18 | }
19 |
20 | @Test
21 | func testConvertPointsToPixels() {
22 | #expect(PointPixelMeasurement.points(1).toPixels(scale: 3) == 3)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Data/DataNonEmptyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataNonEmptyTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 03.09.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct DataNonEmptyTests {
15 | @Test
16 | func testNil() {
17 | #expect(Data().nonEmpty == nil)
18 | }
19 |
20 | @Test
21 | func testNotNil() throws {
22 | let data: Data = try #require(
23 | "data".data(using: .utf8)
24 | )
25 |
26 | #expect(data.nonEmpty != nil)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Set/Set+InsertSequence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Set+InsertSequence.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 18.12.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Set + Insert Sequence
11 | extension Set {
12 | /// Inserts the given `Sequence` in the `Set` if they are not already present.
13 | ///
14 | /// var set: Set = [1, 2]
15 | /// set.insert(contentsOf: [3, 4]) // [1, 2, 3, 4]
16 | ///
17 | mutating public func insert(
18 | contentsOf newElements: some Sequence
19 | ) {
20 | newElements.forEach { insert($0) }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCoreShared/Extensions/Foundation/U Int/UInt+HexColorRGBAValues.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UInt+HexColorRGBAValues.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 19.08.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - U Int + Hex Color RGB Values
11 | extension UInt {
12 | package func _hexColorRGBValues() -> (red: CGFloat, green: CGFloat, blue: CGFloat)? {
13 | let red: CGFloat = CGFloat((self & 0xFF0000) >> 16) / 255
14 | let green: CGFloat = CGFloat((self & 0x00FF00) >> 8) / 255
15 | let blue: CGFloat = CGFloat(self & 0x0000FF) / 255
16 |
17 | return (red, green, blue)
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Range and ClosedRange/ClosedRangeReversedArrayOnConditionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClosedRangeReversedArrayOnConditionTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 05.08.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ClosedRangeReversedArrayOnConditionTests {
15 | @Test
16 | func test() {
17 | #expect((1...3).reversedArray(false) == [1, 2, 3])
18 | #expect((1...3).reversedArray(true) == [3, 2, 1])
19 | #expect((1...3).reversedArray() == [3, 2, 1])
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/4. Context and Clarity/4.15. Extension Access Control.md:
--------------------------------------------------------------------------------
1 | # Extension Access Control
2 |
3 | It's a better practice to annotate members with access control modifiers, rather than annotating extensions themselves. By annotating members with access control modifiers, we prevent possible access level clashes, and we can add meaningful modifiers that convey clear and intentional access levels to the members.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | public extension SomeStruct {
9 | func doSomething()
10 | }
11 | ```
12 |
13 | Preferred:
14 |
15 | ```swift
16 | extension SomeStruct {
17 | public func doSomething()
18 | }
19 | ```
20 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Macros/Attached/Coding Keys Generation Macro/CKGPropertyMacro.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CKGPropertyMacro.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 02.03.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 | import SwiftSyntaxMacros
11 |
12 | // MARK: - Coding Keys Generation Property Macro
13 | struct CKGPropertyMacro: PeerMacro {
14 | static func expansion(
15 | of node: AttributeSyntax,
16 | providingPeersOf declaration: some DeclSyntaxProtocol,
17 | in context: some MacroExpansionContext
18 | ) throws -> [DeclSyntax] {
19 | []
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/StringProtocol/StringProtocolSubscriptTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocolSubscriptTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringProtocolSubscriptTests {
15 | @Test
16 | func testGet() {
17 | #expect("Lorem Ipsum"[0] == "L")
18 | }
19 |
20 | @Test
21 | func testSet() {
22 | var string: String = "Lorem ipsum"
23 | string[0] = "l"
24 |
25 | #expect(string == "lorem ipsum")
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Date/Date+Age.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+Age.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Date + Age
11 | extension Date {
12 | /// Returns age as integer from given date to current time.
13 | ///
14 | /// let age: Int? = birthDate.age()
15 | ///
16 | public func age(
17 | inCalendar calendar: Calendar = .current
18 | ) -> Int? {
19 | let now: Date = .init()
20 |
21 | guard now >= self else { return nil }
22 |
23 | return calendar.dateComponents([.year], from: self, to: now).year
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Sequence/Sequence+IsUnique.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sequence+IsUnique.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Sequence + Is Unique
11 | extension Sequence where Element: Hashable {
12 | /// Indicates if sequence doesn't contain duplicate elements.
13 | ///
14 | /// let numbers: [Int] = [1, 3, 5]
15 | /// let isUnique: Bool = numbers.isUnique // true
16 | ///
17 | public var isUnique: Bool {
18 | var encountered: Set = []
19 | return allSatisfy { encountered.insert($0).inserted }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayBinaryAppendTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayBinaryAppendTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 20.05.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayBinaryAppendTests {
15 | @Test
16 | func test() {
17 | let array: [Int] = [1, 2, 4, 5, 6]
18 |
19 | var appendedArray: [Int] = array
20 | let index: Int = appendedArray.binaryAppend(3, by: { $0 < $1 })
21 |
22 | #expect(appendedArray == [1, 2, 3, 4, 5, 6])
23 | #expect(index == 2)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/WatchKit/WK Interface Device/WKInterfaceDevice+IsSimulator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WKInterfaceDevice+IsSimulator.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 19.08.24.
6 | //
7 |
8 | #if canImport(WatchKit)
9 |
10 | import WatchKit
11 |
12 | // MARK: - WK Interface Device + Is Simulator
13 | extension WKInterfaceDevice {
14 | /// Indicates if device is simulator.
15 | ///
16 | /// let isSimulator: Bool = WKInterfaceDevice.isSimulator
17 | ///
18 | public static var isSimulator: Bool {
19 | #if targetEnvironment(simulator)
20 | true
21 | #else
22 | false
23 | #endif
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Device/UIDevice+SafeAreaInsets.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+SafeAreaInsets.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 29.04.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Device + Safe Area Insets
13 | extension UIDevice {
14 | /// Safe area insets in `UIApplication.shared.firstWindowInSingleSceneApp`.
15 | ///
16 | /// let insets: UIEdgeInsets = UIDevice.safeAreaInsets
17 | ///
18 | public static var safeAreaInsets: UIEdgeInsets {
19 | UIApplication.shared.firstWindowInSingleSceneApp?.safeAreaInsets ?? .zero
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/QuartzCore/CACornerMask/CACornerMaskAdditionalOptionsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CACornerMaskAdditionalOptionsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 13.06.22.
6 | //
7 |
8 | #if canImport(QuartzCore)
9 |
10 | import QuartzCore
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct CACornerMaskAdditionalOptionsTests {
17 | @Test
18 | func test() {
19 | #expect(CACornerMask.layerMinXCorners.union(.layerMaxXCorners) == .layerAllCorners)
20 | #expect(CACornerMask.layerMinYCorners.union(.layerMaxYCorners) == .layerAllCorners)
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Range and Closed Range/Range+ReversedArrayOnCondition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Range+ReversedArrayOnCondition.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Range + Reversed Array on Condition
11 | extension Range where Bound: FixedWidthInteger {
12 | /// Returns reversed `Array` from `Range` if condition is met.
13 | ///
14 | /// let number: [Int] = 1..<3 // [2, 1]
15 | /// .reversedArray()
16 | ///
17 | public func reversedArray(
18 | _ condition: Bool = true
19 | ) -> [Bound] {
20 | Array(self).reversed(condition)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Bool/Bool+Toggled.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bool+Toggled.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 03.09.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Bool + Toggled
11 | extension Bool {
12 | /// Returns `Bool` with a toggled value.
13 | ///
14 | /// let value: Bool = true.toggled() // false
15 | ///
16 | /// Can be useful when dealing with `Optional` values.
17 | ///
18 | /// let value: Bool? = ...
19 | ///
20 | /// let value1: Bool? = value.map { !$0 }
21 | /// let value2: Bool? = value?.toggled()
22 | ///
23 | public func toggled() -> Bool {
24 | !self
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Numeric/Numeric+WithOppositeSign.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Numeric+WithOppositeSign.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Numeric + With Opposite Sign
11 | extension Numeric {
12 | /// Returns `Numeric` value with opposite sign if condition is met.
13 | ///
14 | /// let number: Int = 10 // -10
15 | /// .withOppositeSign()
16 | ///
17 | public func withOppositeSign(
18 | _ condition: Bool = true
19 | ) -> Self {
20 | if condition {
21 | self * -1
22 | } else {
23 | self
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/String Protocol/StringProtocol+DiacriticInsensitiveString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocol+DiacriticInsensitiveString.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.07.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String Protocol + Diacritic Insensitive String
11 | extension StringProtocol {
12 | /// Returns a diacritic-insensitive `String`.
13 | ///
14 | /// let string: String = "À".diacriticInsensitiveString() // "A"
15 | ///
16 | public func diacriticInsensitiveString(
17 | locale: Locale? = nil
18 | ) -> String {
19 | folding(options: .diacriticInsensitive, locale: locale)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayAppendingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayAppendingTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 11.11.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayAppendingTests {
15 | @Test
16 | func testAppendingElement() {
17 | #expect(
18 | [1, 2].appending(3) ==
19 | [1, 2, 3]
20 | )
21 | }
22 |
23 | @Test
24 | func testAppendingElements() {
25 | #expect(
26 | [1, 2].appending(contentsOf: [3, 4]) ==
27 | [1, 2, 3, 4]
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionContainsAnyItemTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionContainsAnyItemTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 03.07.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionContainsAnyItemTests {
15 | @Test
16 | func test() {
17 | #expect([1, 2, 3].containsAnyItem(fromCollection: [3, 4, 5]))
18 | #expect(![1, 2, 3].containsAnyItem(fromCollection: [4, 5, 6]))
19 | #expect([1].containsAnyItem(fromCollection: []))
20 | #expect(![].containsAnyItem(fromCollection: [1]))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionSafeSubscriptTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionSafeSubscriptTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 05.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionSafeSubscriptTests {
15 | @Test
16 | func test() {
17 | let numbers: [Int] = [1, 3, 5]
18 |
19 | #expect(numbers[safe: -1] == nil)
20 |
21 | for i in numbers.indices {
22 | #expect(numbers[safe: i] == numbers[i])
23 | }
24 |
25 | #expect(numbers[safe: 3] == nil)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/View.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.IDEKit.TextSubstitutionFileTemplateKind
7 | Options
8 |
9 |
10 | Identifier
11 | productName
12 | Required
13 |
14 | Type
15 | text
16 | Name
17 | Title (eg "ContentView"):
18 |
19 |
20 | SupportsSwiftPackage
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+EraseToAnyView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+EraseToAnyView.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11.11.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Erase to Any View
11 | extension View {
12 | /// Wraps `View` with a type eraser.
13 | ///
14 | /// Color.accentColor.eraseToAnyView()
15 | ///
16 | public func eraseToAnyView() -> AnyView {
17 | .init(self)
18 | }
19 |
20 | /// Wraps `View` with a type eraser.
21 | ///
22 | /// Color.accentColor.eraseToAnyView()
23 | ///
24 | public func eraseToAnyViewErasing() -> AnyView {
25 | .init(erasing: self)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Image/UIImage+Compressed.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Compressed.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/8/21.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Image + Compressed
13 | extension UIImage {
14 | /// Returns `UIImage` compressed with specified quality.
15 | ///
16 | /// let image: UIImage = .init(named: "Image")!
17 | /// let compressedImage: UIImage? = image.jpegCompressed(quality: 0.75)
18 | ///
19 | public func jpegCompressed(quality: CGFloat) -> UIImage? {
20 | jpegData(compressionQuality: quality).flatMap { UIImage(data: $0) }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayPrependingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayPrependingTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 04.07.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayPrependingTests {
15 | @Test
16 | func testPrependingElement() {
17 | #expect(
18 | [2, 3].prepending(1) ==
19 | [1, 2, 3]
20 | )
21 | }
22 |
23 | @Test
24 | func testPrependingElements() {
25 | #expect(
26 | [3, 4].prepending(contentsOf: [1, 2]) ==
27 | [1, 2, 3, 4]
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Models/AbsoluteFractionMeasurementTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AbsoluteFractionMeasurementTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 16.07.24.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct AbsoluteFractionMeasurementTests {
15 | @Test
16 | func testConvertAbsoluteToFraction() {
17 | #expect(AbsoluteFractionMeasurement.absolute(300).toFraction(dimension: 600) == 0.5)
18 | }
19 |
20 | @Test
21 | func testConvertFractionToAbsolute() {
22 | #expect(AbsoluteFractionMeasurement.fraction(0.5).toAbsolute(dimension: 600) == 300)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/ContentView (MV).xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.IDEKit.TextSubstitutionFileTemplateKind
7 | Options
8 |
9 |
10 | Identifier
11 | productName
12 | Required
13 |
14 | Type
15 | text
16 | Name
17 | Title (eg "Home"):
18 |
19 |
20 | SupportsSwiftPackage
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/Gateway.xctemplate/TemplateInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kind
6 | Xcode.IDEKit.TextSubstitutionFileTemplateKind
7 | Options
8 |
9 |
10 | Identifier
11 | productName
12 | Required
13 |
14 | Type
15 | text
16 | Name
17 | Title (eg "FetchPosts"):
18 |
19 |
20 | SupportsSwiftPackage
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/6. Performance and Optimization/6.2. Private Members.md:
--------------------------------------------------------------------------------
1 | # Private Members
2 |
3 | To improve performance inside a `class`, `struct`, and `enum`, it's important to declare all members that are not part of an interface as `private`. This allows the compiler to check for polymorphism at compile time, which can significantly improve performance.
4 |
5 | Using the `private` access modifier also ensures that these members are not called unintentionally from other parts of the code, which can help prevent bugs and improve code maintainability. By keeping the implementation details hidden from other parts of the code, it becomes easier to change or refactor the declaration without affecting other parts of the program.
6 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Range and Closed Range/ClosedRange+ReversedArrayOnCondition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClosedRange+ReversedArrayOnCondition.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 05.08.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Closed Range + Reversed Array on Condition
11 | extension ClosedRange where Bound: FixedWidthInteger {
12 | /// Returns reversed `Array` from `ClosedRange` if condition is met.
13 | ///
14 | /// let number: [Int] = 1...3 // [3, 2, 1]
15 | /// .reversedArray()
16 | ///
17 | public func reversedArray(
18 | _ condition: Bool = true
19 | ) -> [Bound] {
20 | Array(self).reversed(condition)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayRemovingDuplicatesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayRemovingDuplicatesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayRemovingDuplicatesTests {
15 | @Test
16 | func test() {
17 | #expect(
18 | [1, 1, 3, 5, 5].removingDuplicates() ==
19 | [1, 3, 5]
20 | )
21 |
22 | do {
23 | var array: [Int] = [1, 1, 3, 5, 5]
24 | array.removeDuplicates()
25 |
26 | #expect(array == [1, 3, 5])
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/String Protocol/StringProtocol+Subscript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocol+Subscript.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String Protocol + Subscript
11 | extension StringProtocol {
12 | /// Accesses the element at the specified position.
13 | ///
14 | /// let string: String = "Lorem Ipsum"
15 | /// let firstChar: Character = string[0] // "L"
16 | ///
17 | public subscript(_ i: Int) -> Element {
18 | get {
19 | self[index(startIndex, offsetBy: i)]
20 | }
21 | set {
22 | replace(at: i, with: newValue)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/UIKit/UI Alert/UIAlertButtonProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertButtonProtocol.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Alert Button Protocol
13 | /// `UIAlertController` button protocol.
14 | public protocol UIAlertButtonProtocol: UIAlertButtonConvertible {
15 | /// Converts `UIAlertButtonProtocol` to `UIAlertAction`.
16 | @MainActor
17 | func makeBody() -> UIAlertAction
18 | }
19 |
20 | extension UIAlertButtonProtocol {
21 | public func toButtons() -> [any UIAlertButtonProtocol] {
22 | [self]
23 | }
24 | }
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/VCore/Macros/Attached/Uninitializable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Uninitializable.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Uninitializable
11 | /// Adds `private` initializer to declaration, preventing object creation.
12 | ///
13 | /// Can be used to simplify declarations of simple objects containing various constants.
14 | ///
15 | /// @Uninitializable
16 | /// struct AppConstants {
17 | /// static let apiKey: String = "..."
18 | /// }
19 | ///
20 | @attached(member, names: named(init))
21 | public macro Uninitializable() = #externalMacro(
22 | module: "VCoreMacrosImplementation",
23 | type: "UninitializableMacro"
24 | )
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Base Button (SwiftUI)/SwiftUIBaseButtonUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIBaseButtonUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 29.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Swift UI Base Button UI Model
11 | /// Model that describes UI.
12 | public struct SwiftUIBaseButtonUIModel: Sendable {
13 | // MARK: Properties
14 | /// Indicates if button animates state change. Set to `true`.
15 | ///
16 | /// Changing this property conditionally will cause view state to be reset.
17 | public var animatesStateChange: Bool = true
18 |
19 | // MARK: Initializers
20 | /// Initializes UI model with default values.
21 | public init() {}
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Coordinating Navigation Stack (Observable Object)/EnvironmentValues+NavigationStackCoordinatorOO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnvironmentValues+NavigationStackCoordinatorOO.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 07.08.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Environment Values + Navigation Stack Coordinator (Observable Object)
11 | extension EnvironmentValues {
12 | /// `NavigationStackCoordinatorOO` of the `View` associated with the environment.
13 | ///
14 | /// Since `NavigationStackCoordinatorOO` is a reference type, `View` updates won't be triggered.
15 | @Entry public var navigationStackCoordinatorOO: NavigationStackCoordinatorOO?
16 | }
17 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.6. Opening Brackets.md:
--------------------------------------------------------------------------------
1 | # Opening Brackets
2 |
3 | The opening bracket of a function or control flow statement should be placed on the same line as the declaration or statement (K&R style), rather than on a new line . This helps to conserve vertical space and makes the code easier to read by keeping related code elements closer together.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | func doSomething()
9 | {
10 | ...
11 | }
12 | ```
13 |
14 | Preferred:
15 |
16 | ```swift
17 | func doSomething() {
18 | ...
19 | }
20 | ```
21 |
22 | There are some exceptions to this rule. For additional info, refer to "Functions with Generic Type Constraints" section in "Functions and Methods" documentation.
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Table View/InfiniteScrollingUITableViewDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingUITableViewDelegate.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/10/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Infinite Scrolling UI Table View Delegate
13 | /// Allows the adopting delegate to respond to messages from `InfiniteScrollingUITableView`.
14 | @MainActor
15 | public protocol InfiniteScrollingUITableViewDelegate: AnyObject {
16 | /// Indicates that pagination did occur.
17 | func tableViewDidScrollToBottom(sender infiniteScrollingTableView: InfiniteScrollingUITableView)
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/StringProtocol/StringProtocolReplacedTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocolReplacedTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 11.12.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringProtocolReplacedTests {
15 | @Test
16 | func test() {
17 | #expect(
18 | "Lorem ipsum".replaced(at: 0, with: "l") ==
19 | "lorem ipsum"
20 | )
21 |
22 | do {
23 | var string: String = "Lorem ipsum"
24 | string.replace(at: 0, with: "l")
25 |
26 | #expect(string == "lorem ipsum")
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI NavigationBar/UINavigationBar+Height.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UINavigationBar+Height.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Navigation Bar + Height
13 | extension UINavigationBar {
14 | /// Navigation bar height.
15 | ///
16 | /// let height: CGFloat = UINavigationBar.height
17 | ///
18 | public static var height: CGFloat {
19 | UINavigationController(
20 | rootViewController: UIViewController(nibName: nil, bundle: nil)
21 | )
22 | .navigationBar
23 | .frame
24 | .size
25 | .height
26 | }
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIColor/UIColorMixTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorBlendTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct UIColorMixTests {
17 | @Test
18 | func test() {
19 | let color1: UIColor = .init(red: 1/3, green: 1/3, blue: 1/3, alpha: 1)
20 | let color2: UIColor = .init(red: 2/3, green: 2/3, blue: 2/3, alpha: 1)
21 |
22 | #expect(
23 | color1.mix(with: color2, by: 0.2) ==
24 | UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
25 | )
26 | }
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.10. Conditional Compilation Flags.md:
--------------------------------------------------------------------------------
1 | # Conditional Compilation Flags
2 |
3 | Conditional compilation flags should be placed at the top-level of the code and indented to the same level as the other top-level code, regardless of the current indentation level. This ensures that conditional compilation flags are easy to find and modify, and helps maintain consistency across the codebase.
4 |
5 | ```swift
6 | #if canImport(UIKit)
7 |
8 | struct SomeStruct {
9 | ...
10 | }
11 |
12 | #endif
13 | ```
14 |
15 | ```swift
16 | struct SomeStruct {
17 | let sdk: String = {
18 | #if canImport(UIKit)
19 | "UIKit"
20 | #elseif canImport(AppKit)
21 | "AppKit"
22 | #endif
23 | }()
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+RandomElements.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+RandomElements.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 30.04.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + Random Elements
11 | extension Collection {
12 | /// Returns random elements from the collection up to specified count.
13 | ///
14 | /// let numbers: [Int] = [10, 20, 30, 40, 50, 60]
15 | /// let randomNumbers: [Int?] = numbers.randomElements(3) // [10, 60, 40]
16 | ///
17 | public func randomElements(_ n: Int) -> [Element]? {
18 | guard 0 <= n, n <= count else { return nil }
19 |
20 | return indices.shuffled().prefix(upTo: n).map { self[$0] }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/VCore/Macros/Freestanding (Expression)/Color_InitWithHexUInt.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color_InitWithHexUInt.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color (Init with Hex U Int)
11 | /// Returns non-optional `Color` from `UInt`, checked during the compile time.
12 | ///
13 | /// `hex` parameter must have `6` characters.
14 | ///
15 | /// let color: Color = #color(hex: 0x007AFF)
16 | ///
17 | @freestanding(expression)
18 | public macro color(
19 | _ colorSpace: Color.RGBColorSpace = .sRGB,
20 | hex uInt: UInt,
21 | opacity: CGFloat = 1
22 | ) -> Color = #externalMacro(
23 | module: "VCoreMacrosImplementation",
24 | type: "ColorMacro_InitWithHexUInt"
25 | )
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/AccessLevelModifierKeyword.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AccessLevelModifierKeyword.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 23.05.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Access Level Modifier Keyword
11 | /// Enumeration that represent access level modifier.
12 | public enum AccessLevelModifierKeyword: String, Sendable, CaseIterable {
13 | /// Open.
14 | case `open` = "open"
15 |
16 | /// Public.
17 | case `public` = "public"
18 |
19 | /// Package.
20 | case `package` = "package"
21 |
22 | /// Internal.
23 | case `internal` = "internal"
24 |
25 | /// Fileprivate.
26 | case `fileprivate` = "fileprivate"
27 |
28 | /// Private.
29 | case `private` = "private"
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Core Graphics/CG Point/CGPoint+WithReversedCoordinates.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+WithReversedCoordinates.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 02.04.23.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | // MARK: - CG Point + With Reversed Coordinates
11 | extension CGPoint {
12 | /// Returns `CGPoint` with reversed `x` and `y`.
13 | ///
14 | /// let coordinates: CGPoint = .init(x: 100, y: 200)
15 | /// .withReversedCoordinates() // 200x100
16 | ///
17 | public func withReversedCoordinates(
18 | _ condition: Bool = true
19 | ) -> Self {
20 | if condition {
21 | CGPoint(x: y, y: x)
22 | } else {
23 | self
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Global Functions/AreEqualWithinToleranceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AreEqualWithinToleranceTests.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 22.01.25.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct AreEqualWithinToleranceTests {
15 | @Test
16 | func test() {
17 | #expect(areEqual(3.14, 3.1415, tolerance: pow(10, 0))) // 1
18 | #expect(areEqual(3.14, 3.1415, tolerance: pow(10, -1))) // 0.1
19 | #expect(areEqual(3.14, 3.1415, tolerance: pow(10, -2))) // 0.01
20 | #expect(!areEqual(3.14, 3.1415, tolerance: pow(10, -3))) // 0.001
21 | #expect(!areEqual(3.14, 3.1415, tolerance: 0)) // 0
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+SafeSubscript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+SafeSubscript.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + Safe Subscript
11 | extension Collection {
12 | /// Accesses the element at the specified position, but returns `nil` if out of bounds.
13 | ///
14 | /// let numbers: [Int] = [1, 3, 5]
15 | ///
16 | /// let firstNum: Int? = numbers[safe: 0] // 1
17 | /// let fourthNum: Int? = numbers[safe: 4] // nil
18 | ///
19 | public subscript(safe index: Index) -> Element? {
20 | guard indices.contains(index) else { return nil }
21 |
22 | return self[index]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Macros/Freestanding (Expression)/Color_InitWithHexString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color_InitWithHexString.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color (Init with Hex String)
11 | /// Returns non-optional `Color` from `String`, checked during the compile time.
12 | ///
13 | /// `hex` parameter must have `6` characters.
14 | ///
15 | /// let color: Color = #color(hex: "#007AFF")
16 | ///
17 | @freestanding(expression)
18 | public macro color(
19 | _ colorSpace: Color.RGBColorSpace = .sRGB,
20 | hex string: String,
21 | opacity: CGFloat = 1
22 | ) -> Color = #externalMacro(
23 | module: "VCoreMacrosImplementation",
24 | type: "ColorMacro_InitWithHexString"
25 | )
26 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Models/Custom Results/ResultNoSuccessNoFailureTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResultNoSuccessNoFailureTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 08.06.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ResultNoSuccessNoFailureTests {
15 | // MARK: Test Data
16 | private let resultS: ResultNoSuccessNoFailure = .success
17 | private let resultF: ResultNoSuccessNoFailure = .failure
18 |
19 | // MARK: Tests
20 | @Test
21 | func testEquality() {
22 | #expect(resultF == resultF)
23 | #expect(resultF != resultS)
24 | #expect(resultS != resultF)
25 | #expect(resultS == resultS)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Alert/AlertButtonProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlertButtonProtocol.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Alert Button Protocol
11 | /// `Alert` button protocol.
12 | public protocol AlertButtonProtocol: AlertButtonConvertible {
13 | /// Body type.
14 | typealias Body = AnyView
15 |
16 | /// Creates a `View` that represents the body of a button.
17 | @MainActor
18 | func makeBody(
19 | animateOutHandler: @escaping (/*completion*/ (() -> Void)?) -> Void
20 | ) -> Body
21 | }
22 |
23 | extension AlertButtonProtocol {
24 | public func toButtons() -> [any AlertButtonProtocol] {
25 | [self]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/UIKit/UI Action Sheet/UIActionSheetButtonProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIActionSheetButtonProtocol.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Action Sheet Button Protocol
13 | /// `UIActionSheetController` button protocol.
14 | public protocol UIActionSheetButtonProtocol: UIActionSheetButtonConvertible {
15 | /// Converts `UIActionSheetButtonProtocol` to `UIAlertAction`.
16 | @MainActor
17 | func makeBody() -> UIAlertAction
18 | }
19 |
20 | extension UIActionSheetButtonProtocol {
21 | public func toButtons() -> [any UIActionSheetButtonProtocol] {
22 | [self]
23 | }
24 | }
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Macros/Attached/Uninitializable Macro/UninitializableMacro.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UninitializableMacro.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 | import SwiftSyntaxMacros
11 |
12 | // MARK: - Uninitializable Macro
13 | struct UninitializableMacro: MemberMacro {
14 | static func expansion(
15 | of node: AttributeSyntax,
16 | providingMembersOf declaration: some DeclGroupSyntax,
17 | conformingTo protocols: [TypeSyntax],
18 | in context: some MacroExpansionContext
19 | ) throws -> [DeclSyntax] {
20 | [
21 | """
22 | private init() {}
23 | """
24 | ]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionEnumeratedArrayTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionEnumeratedArrayTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 05.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionEnumeratedArrayTests {
15 | @Test
16 | func test() {
17 | let characters: [String] = ["A", "B", "C"]
18 | let enumeratedCharacters: [(offset: Int, element: String)] = characters.enumeratedArray()
19 |
20 | for (i, element) in characters.enumerated() {
21 | #expect(enumeratedCharacters[i].offset == i)
22 | #expect(enumeratedCharacters[i].element == element)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Int/IntRunBlockNTimesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntRunBlockNTimesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct IntRunBlockNTimesTests {
15 | @Test
16 | func testBlock() {
17 | let number: Int = 5
18 |
19 | var sum: Int = 0
20 | number.times { sum += 1 }
21 |
22 | #expect(sum == 5)
23 | }
24 |
25 | @Test
26 | func testBlockNumbered() {
27 | let number: Int = 5
28 |
29 | var sum: Int = 0
30 | number.times { sum += $0 }
31 |
32 | #expect(sum == 10)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Core Graphics/CG Size/CGSize+WithReversedDimensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize+WithReversedDimensions.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | // MARK: - CG Size + With Reversed Dimensions
11 | extension CGSize {
12 | /// Returns `CGSize` with reversed `width` and `height` if condition is met..
13 | ///
14 | /// let size: CGSize = .init(width: 3, height: 4)
15 | /// .withReversedDimensions() // 4x3
16 | ///
17 | public func withReversedDimensions(
18 | _ condition: Bool = true
19 | ) -> Self {
20 | if condition {
21 | CGSize(width: height, height: width)
22 | } else {
23 | self
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Common/InfiniteScrollingPaginationState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingPaginationState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/10/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Infinite Scrolling Pagination State
13 | /// Enumeration that represents state.
14 | public enum InfiniteScrollingPaginationState: Int, Sendable, CaseIterable {
15 | /// Indicates that `UIActivityIndicator` is visible and additional pagination cannot occur.
16 | case loading
17 |
18 | /// Indicates that pagination can occur.
19 | case canPaginate
20 |
21 | /// Indicates that pagination cannot occur.
22 | case cannotPaginate
23 | }
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/AppKit/NSColor/NSColorMixTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSColorBlendTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 |
10 | import AppKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct NSColorMixTests {
17 | @Test
18 | func test() {
19 | let color1: NSColor = .init(red: 1/3, green: 1/3, blue: 1/3, alpha: 1)
20 | let color2: NSColor = .init(red: 2/3, green: 2/3, blue: 2/3, alpha: 1)
21 |
22 | #expect(
23 | color1.mix(with: color2, by: 0.2) ==
24 | NSColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
25 | )
26 | }
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Infinite Scrolling UI Collection View/InfiniteScrollingUICollectionViewDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingUICollectionViewPaginationState.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import Foundation
11 |
12 | // MARK: - Infinite Scrolling UI Collection View Delegate
13 | /// Allows the adopting delegate to respond to messages from `InfiniteScrollingUICollectionView`.
14 | @MainActor
15 | public protocol InfiniteScrollingUICollectionViewDelegate: AnyObject {
16 | /// Indicates that pagination did occur.
17 | func collectionViewDidScrollToBottom(sender infiniteScrollingCollectionView: InfiniteScrollingUICollectionView)
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Application/UIApplication+SendResignFirstResponderAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication+SendResignFirstResponderAction.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 16.08.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Application + Send Resign First Responder Action
13 | extension UIApplication {
14 | /// Sends `UIResponder.resignFirstResponder` selector to `UIApplication`.
15 | ///
16 | /// UIApplication.shared.sendResignFirstResponderAction()
17 | ///
18 | public func sendResignFirstResponderAction() {
19 | let resign: Selector = #selector(UIResponder.resignFirstResponder)
20 | _ = sendAction(resign, to: nil, from: nil, for: nil)
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/Plain List/View+PlainListDecoration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+PlainListDecoration.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 27.11.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Plain List Decoration
11 | @available(tvOS, unavailable)
12 | @available(watchOS, unavailable)
13 | extension View {
14 | /// Removes `List` decoration for `PlainList`.
15 | public func removingListDecoration() -> some View {
16 | self
17 | .environment(\.defaultMinListRowHeight, 0)
18 | }
19 |
20 | /// Removes `List` content decoration for `PlainList`.
21 | public func removingListContentDecoration() -> some View {
22 | self
23 | .listRowInsets(EdgeInsets())
24 | .listRowSeparator(.hidden)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+GetSize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+GetSize.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10/28/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Get Size
11 | extension View {
12 | /// Retrieves size from `View`.
13 | ///
14 | /// @State private var size: CGSize = .zero
15 | ///
16 | /// var body: some View {
17 | /// Color.accentColor
18 | /// .getSize({ size = $0 })
19 | /// }
20 | ///
21 | public func getSize(
22 | _ action: @escaping (CGSize) -> Void
23 | ) -> some View {
24 | self
25 | .onGeometryChange(
26 | for: CGSize.self,
27 | of: { $0.size },
28 | action: action
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Device/UIDevice+HasNoPhysicalHomeButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+HasNoPhysicalHomeButton.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Device + Has No Physical Home Button
13 | @available(tvOS, unavailable)
14 | extension UIDevice {
15 | /// Indicates if device has no physical home button.
16 | ///
17 | /// Check is made against bottom safe are inset. If they are more than `0`, expression evaluates to`true`.
18 | ///
19 | /// let hasNoPhysicalHomeButton: Bool = UIDevice.hasNoPhysicalHomeButton
20 | ///
21 | public static var hasNoPhysicalHomeButton: Bool {
22 | UIDevice.safeAreaInsets.bottom > 0
23 | }
24 | }
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Sequence/SequenceMinAndMaxByKeyPathTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceMinAndMaxByKeyPathTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.10.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SequenceMinAndMaxByKeyPathTests {
15 | // MARK: Test Data
16 | private struct Object: Equatable {
17 | let value: Int
18 | }
19 |
20 | private let array: [Object] = [
21 | Object(value: 1),
22 | Object(value: 2),
23 | Object(value: 3)
24 | ]
25 |
26 | // MARK: Tests
27 | @Test
28 | func test() {
29 | #expect(array.min(by: \.value)?.value == 1)
30 | #expect(array.max(by: \.value)?.value == 3)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit and AppKit/NSLayoutConstraint/NSLayoutConstraintStoringTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSLayoutConstraintStoringTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct NSLayoutConstraintStoringTests {
18 | @Test
19 | func test() {
20 | let view: UIView = .init()
21 |
22 | var constraint: NSLayoutConstraint?
23 | NSLayoutConstraint.activate([
24 | view.widthAnchor.constraint(equalToConstant: 100)
25 | .storing(in: &constraint)
26 | ])
27 |
28 | #expect(constraint?.constant == 100)
29 | }
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIView/UIViewRoundCornersTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewRoundCornersTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct UIViewRoundCornersTests {
18 | @Test
19 | func test() {
20 | let view: UIView = .init()
21 |
22 | view.roundCorners(
23 | .layerAllCorners,
24 | by: 10,
25 | curve: .circular
26 | )
27 |
28 | #expect(view.layer.maskedCorners == .layerAllCorners)
29 | #expect(view.layer.cornerRadius == 10)
30 | #expect(view.layer.cornerCurve == .circular)
31 | }
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Screen/UIScreen+DisplayCornerRadius.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIScreen+DisplayCornerRadius.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/9/21.
6 | //
7 |
8 | #if canImport(UIKit) && !(os(watchOS) || os(visionOS))
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Screen + Display Corner Radius
13 | extension UIScreen {
14 | /// The corner radius of the display.
15 | ///
16 | /// Can be used to round corner of half modal component.
17 | /// Accessed private API and may be deprecated.
18 | ///
19 | /// view.window?.screen.displayCornerRadius.map {
20 | /// view.roundCorners(.layerMinYCorners, by: $0)
21 | /// }
22 | ///
23 | public var displayCornerRadius: CGFloat? {
24 | value(forKey: "_displayCornerRadius") as? CGFloat
25 | }
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/StringProtocol/StringProtocolContainsOnlyCharacterSetTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocolContainsOnlyCharacterSetTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 17.06.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringContainsOnlyCharacterSetTests {
15 | @Test
16 | func testContainsOnlyCharacterSet() {
17 | #expect("0123456789".contains(only: .decimalDigits))
18 | #expect(!"+0123456789".contains(only: .decimalDigits))
19 | }
20 |
21 | @Test
22 | func testContainsOnlyCharacterSets() {
23 | #expect("0123456789A".contains(only: [.decimalDigits, .letters]))
24 | #expect(!"+0123456789A".contains(only: [.decimalDigits, .letters]))
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Plugin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Plugin.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 07.01.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftCompilerPlugin
10 | import SwiftSyntaxMacros
11 |
12 | // MARK: - Plugin
13 | @main
14 | struct Plugin: CompilerPlugin {
15 | let providingMacros: [Macro.Type] = [
16 | // Attached
17 | CodingKeysGenerationMacro.self, CKGPropertyMacro.self,
18 | MemberwiseInitializableMacro.self,
19 | OptionSetRepresentationMacro.self,
20 | UninitializableMacro.self,
21 |
22 | // Freestanding (Declaration)
23 | // ...
24 |
25 | // Freestanding (Expression)
26 | ColorMacro_InitWithHexString.self,
27 | ColorMacro_InitWithHexUInt.self,
28 | URLMacro_InitWithString.self
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayRemoveIfPresentTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayRemoveIfPresentTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 26.11.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayRemoveIfPresentTests {
15 | @Test
16 | func testRemoveElement() {
17 | let array: [Int] = [1, 2, 3]
18 |
19 | var filteredArray = array
20 | filteredArray.removeIfPresent(3)
21 |
22 | #expect(filteredArray == [1, 2])
23 | }
24 |
25 | @Test
26 | func testRemoveElements() {
27 | let array: [Int] = [1, 2, 3]
28 |
29 | var filteredArray = array
30 | filteredArray.removeIfPresent(contentsOf: [2, 3])
31 |
32 | #expect(filteredArray == [1])
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/4. Context and Clarity/4.14. Name Shadowing.md:
--------------------------------------------------------------------------------
1 | # Name Shadowing
2 |
3 | When casting optionals, it's recommended to use the same name for the casted value and the optional value. This way, you can make use of name shadowing and avoid creating a new variable with a different name, which could lead to confusion and errors. Using the same name for both values can also make the code more concise and easier to read.
4 |
5 | Not Preferred
6 |
7 | ```swift
8 | if let context: NSManagedObjectContext = managedObjectContext {
9 | ...
10 | }
11 | ```
12 |
13 | Not Preferred
14 |
15 | ```swift
16 | if let managedObjectContext = managedObjectContext {
17 | ...
18 | }
19 | ```
20 |
21 | Ever since the release of `Swift` `5.7`, you can utilize shorthand `if-let` syntax.
22 |
23 | ```swift
24 | guard let managedObjectContext else { return }
25 | ```
26 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIRectCorner/UIRectCornerAdditionalOptionsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIRectCornerAdditionalOptionsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 13.06.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct UIRectCornerAdditionalOptionsTests {
17 | @Test
18 | func test() {
19 | // `rawValue` of `allCorners` is `18446744073709551615`
20 | let allCornersRawValue: UInt = UIRectCorner([.topLeft, .topRight, .bottomRight, .bottomLeft]).rawValue
21 |
22 | #expect(UIRectCorner.leftCorners.union(.rightCorners).rawValue == allCornersRawValue)
23 | #expect(UIRectCorner.topCorners.union(.bottomCorners).rawValue == allCornersRawValue)
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI View/UIView+WithTranslatesAutoresizingMaskIntoConstraints.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+WithTranslatesAutoresizingMaskIntoConstraints.swift
3 | //
4 | // VCore
5 | // Created by Vakhtang Kontridze on 10.06.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI View + With Translates Autoresizing Mask into Constraints
13 | extension UIView {
14 | /// Applies flag to `translatesAutoresizingMaskIntoConstraints` property of `UIView`, and returns it.
15 | ///
16 | /// let button: UILabel = .init()
17 | /// .withTranslatesAutoresizingMaskIntoConstraints(false)
18 | ///
19 | public func withTranslatesAutoresizingMaskIntoConstraints(_ flag: Bool) -> Self {
20 | translatesAutoresizingMaskIntoConstraints = flag
21 | return self
22 | }
23 | }
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+EnumeratedArray.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+EnumeratedArray.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 29.04.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + Enumerated Array
11 | extension Collection {
12 | /// Enumerated a `Collection` as a tuple of offset and element.
13 | ///
14 | /// Element can be used as id in `ForEach` without use of an explicit identifier.
15 | ///
16 | /// var body: some View {
17 | /// ForEach(
18 | /// data.enumeratedArray(),
19 | /// id: \.element,
20 | /// content: { (i, element) in ... }
21 | /// )
22 | /// }
23 | ///
24 | public func enumeratedArray() -> Array<(offset: Int, element: Element)> {
25 | .init(enumerated())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Set/SetTogglingElementTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetTogglingElementTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SetTogglingElementTests {
15 | @Test
16 | func testToggling() {
17 | let set1: Set = [1, 3, 5]
18 |
19 | let set2: Set = set1.toggling(1)
20 | #expect(set2 == [3, 5])
21 |
22 | let set3: Set = set2.toggling(1)
23 | #expect(set3 == [1, 3, 5])
24 | }
25 |
26 | @Test
27 | func testToggle() {
28 | var set: Set = [1, 3, 5]
29 |
30 | set.toggle(1)
31 | #expect(set == [3, 5])
32 |
33 | set.toggle(1)
34 | #expect(set == [1, 3, 5])
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Date/DateAgeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateAgeTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct DateAgeTests {
15 | @Test
16 | func testInvalidDate() throws {
17 | let birthDate: Date = try #require(
18 | Calendar.current.date(byAdding: .second, value: 1, to: Date())
19 | )
20 |
21 | #expect(birthDate.age(inCalendar: .current) == nil)
22 | }
23 |
24 | @Test
25 | func testValidDate() throws {
26 | let birthDate: Date = try #require(
27 | Calendar.current.date(from: DateComponents(year: 1970, month: 1, day: 1))
28 | )
29 |
30 | #expect(birthDate.age(inCalendar: .current) != nil)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Inner Shadow UI View/InnerShadowUIViewUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InnerShadowUIViewUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.07.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - Inner Shadow UI View UI Model
13 | /// Model that describes UI.
14 | public struct InnerShadowUIViewUIModel: Sendable {
15 | // MARK: Properties
16 | /// Shadow color.
17 | public var shadowColor: UIColor = .black.withAlphaComponent(0.1)
18 |
19 | /// Shadow color. Set to `5`.
20 | public var shadowRadius: CGFloat = 5
21 |
22 | /// Shadow color. Set to `5` width and `5` height.
23 | public var shadowOffset: CGSize = .init(width: 5, height: 5)
24 |
25 | // MARK: Initializers
26 | /// Initializes UI model with default values.
27 | public init() {}
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/CoreGraphics/CGPoint/CGPointWithReversedCoordinatesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPointWithReversedCoordinatesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 02.04.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CGPointWithReversedCoordinatesTests {
15 | @Test
16 | func test() {
17 | #expect(
18 | CGPoint(x: 3, y: 4).withReversedCoordinates(false) ==
19 | CGPoint(x: 3, y: 4)
20 | )
21 |
22 | #expect(
23 | CGPoint(x: 3, y: 4).withReversedCoordinates(true) ==
24 | CGPoint(x: 4, y: 3)
25 | )
26 |
27 | #expect(
28 | CGPoint(x: 3, y: 4).withReversedCoordinates() ==
29 | CGPoint(x: 4, y: 3)
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Sequence/SequenceConditionalGroupingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceConditionalGroupingTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SequenceConditionalGroupingTests {
15 | @Test
16 | func testPredicate() {
17 | #expect(
18 | ["Kofi", "Abena", "Efua", "Kweku", "Akosua"].grouped(by: { $0.first == $1.first }) ==
19 | [["Kofi", "Kweku"], ["Abena", "Akosua"], ["Efua"]]
20 | )
21 | }
22 |
23 | @Test
24 | func testKeyPath() {
25 | #expect(
26 | ["Kofi", "Abena", "Efua", "Kweku", "Akosua"].grouped(by: \.first) ==
27 | [["Kofi", "Kweku"], ["Abena", "Akosua"], ["Efua"]]
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Confirmation Dialog/ConfirmationDialogButtonProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConfirmationDialogButtonProtocol.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Confirmation Dialog Button Protocol
11 | /// `ConfirmationDialog` button protocol.
12 | public protocol ConfirmationDialogButtonProtocol: ConfirmationDialogButtonConvertible {
13 | /// Body type.
14 | typealias Body = AnyView
15 |
16 | /// Creates a `View` that represents the body of a button.
17 | @MainActor
18 | func makeBody(
19 | animateOutHandler: @escaping (/*completion*/ (() -> Void)?) -> Void
20 | ) -> Body
21 | }
22 |
23 | extension ConfirmationDialogButtonProtocol {
24 | public func toButtons() -> [any ConfirmationDialogButtonProtocol] {
25 | [self]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/OptionSet/OptionSetElementsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionSetElementsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 20.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct OptionSetElementsTests {
15 | // MARK: Test Data
16 | private struct Gender: OptionSet {
17 | static let male: Self = .init(rawValue: 1 << 0)
18 | static let female: Self = .init(rawValue: 1 << 1)
19 |
20 | static var all: Self { [.male, .female] }
21 |
22 | let rawValue: Int
23 | }
24 |
25 | // MARK: Tests
26 | @Test
27 | func test() {
28 | #expect(Gender.male.elements == [.male])
29 | #expect(Gender.female.elements == [.female])
30 | #expect(Gender.all.elements == [.male, .female])
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIColor/UIColorLightenAndDarkenTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorLightenAndDarkenTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct UIColorLightenAndDarkenTests {
17 | @Test
18 | func testLighten() {
19 | #expect(
20 | UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).lighten(by: 0.1) ==
21 | UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
22 | )
23 | }
24 |
25 | @Test
26 | func testDarken() {
27 | #expect(
28 | UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).darken(by: 0.1) ==
29 | UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
30 | )
31 | }
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI View Controller/UIViewController+WithTabBarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+WithTabBarItem.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/9/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI View Controller + With Tab Bar Item
13 | extension UIViewController {
14 | /// Adds `UITabBarItem` to `UIViewController` and returns it.
15 | ///
16 | /// let tabBarController: UITabBarController = .init()
17 | ///
18 | /// tabBarController.viewControllers = [
19 | /// HomeViewController().withTabBarItem(UITabBarItem(title: "Home", image: nil, tag: 1))
20 | /// ]
21 | ///
22 | public func withTabBarItem(_ tabBarItem: UITabBarItem) -> UIViewController {
23 | self.tabBarItem = tabBarItem
24 | return self
25 | }
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Sequence/Sequence+ContainsDuplicates.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sequence+ContainsDuplicates.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 23.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Sequence + Contains Duplicates
11 | extension Sequence where Element: Hashable {
12 | /// Indicates if sequence contains duplicate elements.
13 | ///
14 | /// let numbers: [Int] = [1, 1, 3, 5, 5]
15 | /// let containsDuplicates: Bool = numbers.containsDuplicates // true
16 | ///
17 | public var containsDuplicates: Bool {
18 | var encountered: Set = []
19 |
20 | for element in self {
21 | if encountered.contains(element) {
22 | return true
23 | } else {
24 | encountered.insert(element)
25 | }
26 | }
27 |
28 | return false
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/Custom Results/ResultNoSuccessNoFailure.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResultNoSuccessNoFailure.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 08.06.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Result with No Success and No Failure
11 | /// Represents either Success or Failure.
12 | ///
13 | /// Can be used to represent a result type that has no associated Success or Failure types to it.
14 | /// For instance, a network request that doesn't return an object or an error.
15 | ///
16 | /// struct UpdateUserDataGateway {
17 | /// func fetch(
18 | /// parameters: UpdateUserDataParameters,
19 | /// completion: (ResultNoSuccessNoFailure) -> Void
20 | /// )
21 | /// }
22 | ///
23 | public enum ResultNoSuccessNoFailure: Equatable, Sendable {
24 | /// Success.
25 | case success
26 |
27 | /// Failure.
28 | case failure
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+OnSimultaneousTapGesture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+OnSimultaneousTapGesture.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10.04.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + On Simultaneous Tap Gesture
11 | extension View {
12 | /// Attaches a gesture to the `View` to process simultaneously with tap gesture defined by the `View`.
13 | ///
14 | /// var body: some View {
15 | /// SomeViewWithExistingGestures()
16 | /// .onSimultaneousTapGesture(perform: ...)
17 | /// }
18 | ///
19 | public func onSimultaneousTapGesture(
20 | count: Int = 1,
21 | perform action: @escaping () -> Void
22 | ) -> some View {
23 | self
24 | .simultaneousGesture(
25 | TapGesture(count: count)
26 | .onEnded(action)
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/CoreGraphics/CGSIze/CGSizeWithReversedDimensionsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSizeWithReversedDimensionsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CGSizeWithReversedDimensionsTests {
15 | @Test
16 | func test() {
17 | #expect(
18 | CGSize(width: 3, height: 4).withReversedDimensions(false) ==
19 | CGSize(width: 3, height: 4)
20 | )
21 |
22 | #expect(
23 | CGSize(width: 3, height: 4).withReversedDimensions(true) ==
24 | CGSize(width: 4, height: 3)
25 | )
26 |
27 | #expect(
28 | CGSize(width: 3, height: 4).withReversedDimensions() ==
29 | CGSize(width: 4, height: 3)
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/UIKit/UI Alert/UIAlertButtonConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertButtonConvertible.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Alert Button Convertible
13 | /// Type that allows for conversion to `UIAlertButtonProtocol`.
14 | public protocol UIAlertButtonConvertible {
15 | /// Converts self to `UIAlertButtonProtocol` `Array`.
16 | func toButtons() -> [any UIAlertButtonProtocol]
17 | }
18 |
19 | extension Array: UIAlertButtonConvertible where Element == any UIAlertButtonProtocol {
20 | public func toButtons() -> [any UIAlertButtonProtocol] {
21 | self
22 | }
23 | }
24 |
25 | extension Never: UIAlertButtonConvertible {
26 | public func toButtons() -> [any UIAlertButtonProtocol] {
27 | fatalError()
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/AppKit/NSColor/NSColorLightenAndDarkenTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSColorLightenAndDarkenTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 |
10 | import AppKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct NSColorLightenAndDarkenTests {
17 | @Test
18 | func testLighten() {
19 | #expect(
20 | NSColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).lighten(by: 0.1) ==
21 | NSColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
22 | )
23 | }
24 |
25 | @Test
26 | func testDarken() {
27 | #expect(
28 | NSColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).darken(by: 0.1) ==
29 | NSColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
30 | )
31 | }
32 | }
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+ShadowWithPoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+ShadowWithPoint.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 18.08.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Shadow with Point
11 | extension View {
12 | /// Adds a shadow to this view.
13 | ///
14 | /// someView
15 | /// .shadow(
16 | /// color: Color.black.opacity(0.3),
17 | /// radius: 10,
18 | /// offset: CGPoint(x: 0, y: 10)
19 | /// )
20 | ///
21 | public func shadow(
22 | color: Color = .init(.sRGBLinear, white: 0, opacity: 0.33),
23 | radius: CGFloat,
24 | offset: CGPoint
25 | ) -> some View {
26 | self
27 | .shadow(
28 | color: color,
29 | radius: radius,
30 | x: offset.x,
31 | y: offset.y
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIView/UIViewControllerWithTabBarItemTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewControllerWithTabBarItemTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct UIViewControllerWithTabBarItemTests {
18 | @Test
19 | func test() {
20 | let tabBarItem: UITabBarItem = .init(
21 | title: "Lorem Ipsum",
22 | image: UIImage(
23 | size: CGSize(dimension: 24),
24 | color: UIColor.systemBlue
25 | ),
26 | tag: 0
27 | )
28 |
29 | let viewController: UIViewController = .init().withTabBarItem(tabBarItem)
30 |
31 | #expect(viewController.tabBarItem == tabBarItem)
32 | }
33 | }
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/5. API Design/5.2. Compose, Don't Enumerate.md:
--------------------------------------------------------------------------------
1 | # Compose, Don't Enumerate
2 |
3 | In some cases, an API can be greatly improved or made more flexible by choosing to use `structs` instead of `enums`. Generally, `enums` should only be used over `structs` when the `case`s represent closely related values or states, such as cardinal directions or device orientation. Using an `enum` simply to enforce coupling can introduce code bloat or other issues in the future.
4 |
5 | We can observe this practice in recent versions of `SwiftUI`, where many types that seem to be `enum`s, such as `Alignment`, are actually `struct`s.
6 |
7 | A simple mechanism that allows for seamless exchange between `struct`s and `enum`s is the fact that, API-wise, there is no difference between an `enum` and a `struct` with static factory initializers.
8 |
9 | In many cases, composition is preferred over enumeration. They will be covered over next several chapters.
10 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Int/Int+BlockNTimes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+RunBlockNTimes.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Int + Run Block "n" Times
11 | extension Int {
12 | /// Runs block by specified times.
13 | ///
14 | /// 5.times { print("Hello, World!") }
15 | ///
16 | public func times(
17 | _ block: () throws -> Void
18 | ) rethrows {
19 | guard self > 0 else { return }
20 |
21 | for _ in 0.. Void
30 | ) rethrows {
31 | guard self > 0 else { return }
32 |
33 | for i in 0.. CGFloat {
20 | min(width, height)
21 | }
22 |
23 | /// Returns maximum of `width` and `height`.
24 | ///
25 | /// var body: some View {
26 | /// someView
27 | /// .frame(dimension: screenSize.maxDimension() * 0.5)
28 | /// }
29 | ///
30 | public func maxDimension() -> CGFloat {
31 | max(width, height)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/Infinite Scrolling UI Views/Common/InfiniteScrollingHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfiniteScrollingHelpers.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/21/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - Infinte Scrolling Helpers
13 | extension UIScrollView {
14 | func didScrollToBottom(offset: CGFloat) -> Bool {
15 | guard contentOffset.y > 0 else { return false }
16 |
17 | let didScrollToBottom: Bool =
18 | contentOffset.y + frame.size.height >=
19 | contentSize.height - offset
20 |
21 | return didScrollToBottom
22 | }
23 |
24 | var contentHeightExceedsTableViewHeight: Bool {
25 | contentSize.height > frame.size.height
26 | }
27 |
28 | var contentHeightExceedsCollectionViewHeight: Bool {
29 | contentSize.height > frame.size.height
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Extensions/XCTestCase+AssertInstanceIsDeallocated.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XCTestCase.AssertInstanceIsDeallocated.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 21.02.24.
6 | //
7 |
8 | import Foundation
9 | import XCTest
10 |
11 | // MARK: - XC Test + Assert Instance Is Deallocated
12 | extension XCTestCase {
13 | @MainActor
14 | func assertInstanceIsDeallocated(
15 | _ instance: AnyObject,
16 | _ message: @escaping @autoclosure () -> String = "",
17 | file: StaticString = #filePath,
18 | line: UInt = #line
19 | ) {
20 | addTeardownBlock({ [weak instance] in
21 | XCTAssertNil(
22 | instance,
23 | String.combiningDebugItems(
24 | "Memory leak detected",
25 | message()
26 | ),
27 | file: file,
28 | line: line
29 | )
30 | })
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI StackView/UIStackView+RemoveArrangedSubviews.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIStackView+RemoveArrangedSubviews.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/5/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Stack View + Remove Arranged Subviews
13 | extension UIStackView {
14 | /// Removes all `UIView`s from arranged subviews, but keeps as subviews.
15 | ///
16 | /// stackView.removeArrangedSubviews()
17 | ///
18 | public func removeArrangedSubviews() {
19 | arrangedSubviews.forEach { removeArrangedSubview($0) }
20 | }
21 |
22 | /// Removes all `UIView`s from arranged subviews and from superview.
23 | ///
24 | /// stackView.removeArrangedSubviewsFromSuperview()
25 | ///
26 | public func removeArrangedSubviewsFromSuperview() {
27 | arrangedSubviews.forEach { $0.removeFromSuperview() }
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIView/UIViewWithTranslatesAutoresizingMaskIntoConstraintsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewWithTranslatesAutoresizingMaskIntoConstraintsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.06.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct UIViewWithTranslatesAutoresizingMaskIntoConstraintsTests {
18 | @Test
19 | func test() {
20 | do {
21 | let view: UILabel = .init().withTranslatesAutoresizingMaskIntoConstraints(false)
22 | #expect(!view.translatesAutoresizingMaskIntoConstraints)
23 | }
24 |
25 | do {
26 | let view: UILabel = .init().withTranslatesAutoresizingMaskIntoConstraints(true)
27 | #expect(view.translatesAutoresizingMaskIntoConstraints)
28 | }
29 | }
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/URL Response/URLResponse+IsSuccessHTTPStatusCode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLResponse+IsSuccessHTTPStatusCode.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/19/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - URL Response + Is Success HTTP Status Code
11 | extension URLResponse {
12 | /// Checks that response `HTTP` code is successful.
13 | ///
14 | /// Compares status code against `200...299`.
15 | ///
16 | /// let (data, response): (Data, URLResponse) = try await URLSession.shared.data(for: request)
17 | /// let isSuccess: Bool = response.isSuccessHTTPStatusCode
18 | ///
19 | public var isSuccessHTTPStatusCode: Bool {
20 | guard
21 | let httpResponse: HTTPURLResponse = self as? HTTPURLResponse,
22 | (200...299).contains(httpResponse.statusCode)
23 | else {
24 | return false
25 | }
26 |
27 | return true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+GetBounds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+GetBounds.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 23.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Get Bounds
11 | extension View {
12 | /// Retrieves bounds from `View`.
13 | ///
14 | /// @State private var bounds: CGRect?
15 | ///
16 | /// var body: some View {
17 | /// Color.accentColor
18 | /// .getBounds(of: .global, { bounds = $0 })
19 | /// }
20 | ///
21 | @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
22 | public func getBounds(
23 | of coordinateSpace: NamedCoordinateSpace,
24 | _ action: @escaping (CGRect?) -> Void
25 | ) -> some View {
26 | self
27 | .onGeometryChange(
28 | for: CGRect?.self,
29 | of: { $0.bounds(of: coordinateSpace) },
30 | action: action
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Application/UIApplication+TopmostViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication+TopmostViewController.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 06.03.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Application + Topmost ViewController
13 | extension UIApplication {
14 | /// Returns topmost `UIViewController` in a `UIWindow`.
15 | public func topmostViewController(
16 | inWindow window: UIWindow
17 | ) -> UIViewController? {
18 | guard
19 | var topmostViewController: UIViewController = window.rootViewController
20 | else {
21 | return nil
22 | }
23 |
24 | while let presentedViewController = topmostViewController.presentedViewController {
25 | topmostViewController = presentedViewController
26 | }
27 |
28 | return topmostViewController
29 | }
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/Atomics/AtomicContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AtomicContainer.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.08.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Atomic Container
11 | /// Thread-safe container.
12 | ///
13 | /// let accountBalance: AtomicContainer = .init(value: 100)
14 | ///
15 | /// func deposit(_ amount: Double) async {
16 | /// await accountBalance.setValue(to: accountBalance.value + amount)
17 | /// }
18 | ///
19 | public actor AtomicContainer {
20 | // MARK: Properties
21 | /// Value.
22 | public var value: Value
23 |
24 | // MARK: Initializers
25 | /// Initializes `AtomicContainer` with an initial value.
26 | public init(value: Value) {
27 | self.value = value
28 | }
29 |
30 | // MARK: Mutators
31 | /// Sets wrapped value to specified value.
32 | public func setValue(to value: Value) async {
33 | self.value = value
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Services and Managers/SessionManagerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionManagerTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 17.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SessionManagerTests {
15 | @Test
16 | func testValidID() async {
17 | let sessionsManger: SessionManager = .init()
18 |
19 | let id: Int = await sessionsManger.generateNewID()
20 | let isValid: Bool = await sessionsManger.isValidID(id)
21 |
22 | #expect(isValid)
23 | }
24 |
25 | @Test
26 | func testInvalidID() async {
27 | let sessionsManger: SessionManager = .init()
28 |
29 | let id: Int = await sessionsManger.generateNewID()
30 | _ = await sessionsManger.generateNewID()
31 | let isValid: Bool = await sessionsManger.isValidID(id)
32 |
33 | #expect(!isValid)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/6. Performance and Optimization/6.1. Intro.md:
--------------------------------------------------------------------------------
1 | # Intro
2 |
3 | Performance and optimization are important, because they directly affect the user experience of an app. In today's fast-paced world, users expect apps to be responsive, fast, and efficient. If an app is slow or unresponsive, users may become frustrated and abandon the app, leading to a loss of customers.
4 |
5 | In addition to user experience, performance and optimization also affect the scalability and cost of running an app. If an app is not optimized, it may consume more resources than necessary, leading to higher hosting costs and potentially making it difficult to scale the app.
6 |
7 | `Swift` is designed to be a high-performance language, and it offers a number of features and optimizations that can help developers write efficient code. By taking advantage of these features and writing performant code, developers can ensure that their apps provide a great user experience and are scalable and cost-effective to run.
8 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/View Resetting Container (Observable Object)/ViewResetterOO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewResetterOO.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.04.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - View Resetter (Observable Object)
11 | /// Object inside the environment of `ViewResettingContainerOO` to trigger view resets on demand.
12 | ///
13 | /// For additional info, refer to `ViewResettingContainerOO`.
14 | public final class ViewResetterOO: ObservableObject {
15 | // MARK: Properties
16 | @Published private(set) var value: Int = 0
17 |
18 | // MARK: Initializers
19 | init() {}
20 |
21 | // MARK: Trigger
22 | /// Triggers reset inside `ViewResettingContainerOO`.
23 | public func trigger() {
24 | value += 1
25 | }
26 |
27 | // MARK: Callable Object
28 | /// Triggers reset inside `ViewResettingContainerOO`.
29 | public func callAsFunction() {
30 | trigger()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/SwiftUI/View Resetting Container/ViewResetter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewResetter.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 26.09.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - View Resetter
11 | /// Object inside the environment of `ViewResettingContainer` to trigger view resets on demand.
12 | ///
13 | /// For additional info, refer to `ViewResettingContainer`.
14 | @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
15 | @Observable
16 | @MainActor
17 | public final class ViewResetter {
18 | // MARK: Properties
19 | private(set) var value: Int = 0
20 |
21 | // MARK: Initializers
22 | init() {}
23 |
24 | // MARK: Trigger
25 | /// Triggers reset inside `ViewResettingContainer`.
26 | public func trigger() {
27 | value += 1
28 | }
29 |
30 | // MARK: Callable Object
31 | /// Triggers reset inside `ViewResettingContainer`.
32 | public func callAsFunction() {
33 | trigger()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/String Protocol/StringProtocol+Replaced.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringProtocol+Replaced.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11.12.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String Protocol + Replaced
11 | extension StringProtocol {
12 | /// Returns `String` with an element replaced at index.
13 | ///
14 | /// let string: String = "Lorem ipsum"
15 | /// let replacedString: String = string.replaced(at: 0, with: "l") // "lorem ipsum"
16 | ///
17 | public func replaced(at i: Int, with element: Element) -> Self {
18 | "\(prefix(i))\(element)\(dropFirst(i+1))"
19 | }
20 |
21 | /// Replaces an element at index.
22 | ///
23 | /// var string: String = "Lorem ipsum"
24 | /// string.replacing(at: 0, with: "l") // "lorem ipsum"
25 | ///
26 | mutating public func replace(at i: Int, with element: Element) {
27 | self = replaced(at: i, with: element)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Binding/Binding+UnwrappedBinding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Binding+UnwrappedBinding.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.09.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Binding + Unwrapped Binding
11 | extension Binding {
12 | /// Returns `Binding` wrapper for an `Optional` `Binding` with a default value for getter.
13 | ///
14 | /// @State private var text: String?
15 | ///
16 | /// var body: some View{
17 | /// TextField("", text: $text.unwrappedBinding(default: ""))
18 | /// .textFieldStyle(.roundedBorder)
19 | /// }
20 | ///
21 | public func unwrappedBinding(
22 | default defaultValue: T
23 | ) -> Binding
24 | where
25 | Value == Optional,
26 | T: Sendable
27 | {
28 | .init(
29 | get: { wrappedValue ?? defaultValue },
30 | set: { wrappedValue = $0 }
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Alert/AlertButtonConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlertButtonConvertible.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Alert Button Convertible
11 | /// Type that allows for conversion to `AlertButtonProtocol`.
12 | public protocol AlertButtonConvertible {
13 | /// Converts self to `AlertButtonProtocol` `Array`.
14 | func toButtons() -> [any AlertButtonProtocol]
15 | }
16 |
17 | extension Array: AlertButtonConvertible where Element == any AlertButtonProtocol {
18 | public func toButtons() -> [any AlertButtonProtocol] {
19 | self
20 | }
21 | }
22 |
23 | extension Never: AlertButtonConvertible {
24 | public func toButtons() -> [any AlertButtonProtocol] {
25 | fatalError()
26 | }
27 | }
28 |
29 | extension EmptyView: AlertButtonConvertible {
30 | public func toButtons() -> [any AlertButtonProtocol] {
31 | []
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/ObservableContainerOO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ObservableContainerOO.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.08.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Observable Container (Observable Object)
11 | /// Container that wraps value, and conforms to `ObservableObject`.
12 | ///
13 | /// Can be used to wrap a value in `ObservableObject` without a dedicated `class`.
14 | ///
15 | /// @StateObject private var model: ObservableContainerOO = .init(value: 0)
16 | ///
17 | /// var body: some View {
18 | /// ...
19 | /// }
20 | ///
21 | open class ObservableContainerOO: ObservableObject { // TODO: iOS 17.0 - Remove, as it's obsoleted by Observation
22 | // MARK: Properties
23 | /// Wrapped value.
24 | @Published open var value: Value
25 |
26 | // MARK: Initializers
27 | /// Initializes `ObservableContainerOO` with value.
28 | public init(value: Value) {
29 | self.value = value
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Navigation Path/NavigationPath+Methods.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationPath+Methods.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 19.06.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Navigation Path + Methods
11 | extension NavigationPath {
12 | /// Adds the elements of a sequence or collection to the end of the path.
13 | ///
14 | /// var path: NavigationPath = .init()
15 | /// path.append(contentsOf: ["Destination1", "Destination2"])
16 | ///
17 | mutating public func append(
18 | contentsOf newElements: some Sequence
19 | ) {
20 | newElements.forEach { append($0) }
21 | }
22 |
23 | /// Removes all elements from the path.
24 | ///
25 | /// Can be used to pop too root.
26 | ///
27 | /// var path: NavigationPath = .init(...)
28 | /// path.removeAll()
29 | ///
30 | mutating public func removeAll() {
31 | removeLast(count)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Array/Array+Appending.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+Appending.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11.11.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Array + Appending Element
11 | extension Array {
12 | /// Returns `Array` with new element appended at the end.
13 | ///
14 | /// [1, 2].appending(3) // [1, 2, 3]
15 | ///
16 | public func appending(_ newElement: Element) -> Self {
17 | self + CollectionOfOne(newElement)
18 | }
19 | }
20 |
21 | // MARK: - Array + Appending Elements
22 | extension Array {
23 | /// Returns `Array` with new elements of a `Sequence` appended at the end.
24 | ///
25 | /// [1, 2].appending(contentsOf: [3, 4]) // [1, 2, 3, 4]
26 | ///
27 | public func appending(
28 | contentsOf newElements: S
29 | ) -> Self
30 | where
31 | S: Sequence,
32 | Element == S.Element
33 | {
34 | self + newElements
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Models/KeyPathInitializableEnumerationTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyPathInitializableEnumerationTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct KeyPathInitializableEnumerationTests {
15 | // MARK: Test Data
16 | private enum Gender: KeyPathInitializableEnumeration {
17 | case male
18 | case female
19 |
20 | var value: Int {
21 | switch self {
22 | case .male: 1
23 | case .female: 2
24 | }
25 | }
26 | }
27 |
28 | // MARK: Tests
29 | @Test
30 | func test() {
31 | #expect(Gender(key: \.value, value: 0) == nil)
32 | #expect(Gender(key: \.value, value: 1) == Gender.male)
33 | #expect(Gender(key: \.value, value: 2) == Gender.female)
34 | #expect(Gender(key: \.value, value: 3) == nil)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/UIKit/UI Action Sheet/UIActionSheetButtonConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIActionSheetButtonConvertible.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Action Sheet Button Convertible
13 | /// Type that allows for conversion to `UIActionSheetButtonProtocol`.
14 | public protocol UIActionSheetButtonConvertible {
15 | /// Converts self to `UIActionSheetButtonProtocol` `Array`.
16 | func toButtons() -> [any UIActionSheetButtonProtocol]
17 | }
18 |
19 | extension Array: UIActionSheetButtonConvertible where Element == any UIActionSheetButtonProtocol {
20 | public func toButtons() -> [any UIActionSheetButtonProtocol] {
21 | self
22 | }
23 | }
24 |
25 | extension Never: UIActionSheetButtonConvertible {
26 | public func toButtons() -> [any UIActionSheetButtonProtocol] {
27 | fatalError()
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIColor/UIColorRGBAValuesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorRGBAValuesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct UIColorRGBAValuesTests {
17 | @Test
18 | func testValues() {
19 | #expect(
20 | UIColor(red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4).rgbaValues ==
21 | (0.1, 0.2, 0.3, 0.4)
22 | )
23 | }
24 |
25 | @Test
26 | func testComponents() {
27 | #expect(
28 | UIColor(red: 10.0/255, green: 20.0/255, blue: 30.0/255, alpha: 0.5).rgbaComponents ==
29 | (10, 20, 30, 0.5)
30 | )
31 | }
32 |
33 | @Test
34 | func testISRGBAEqual() {
35 | #expect(UIColor.red.isRGBAEqual(to: .red))
36 | #expect(!UIColor.red.isRGBAEqual(to: .blue))
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIFont/UIFontStylingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIFontStylingTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct NSFontStylingTests {
17 | @Test
18 | func testItalic() throws {
19 | let font: UIFont = .systemFont(ofSize: 17)
20 |
21 | let italicFont: UIFont = try #require(
22 | font.withItalicStyling()
23 | )
24 |
25 | #expect(italicFont.fontDescriptor.symbolicTraits.contains(.traitItalic))
26 | }
27 |
28 | @Test
29 | func testBold() throws {
30 | let font: UIFont = .systemFont(ofSize: 17)
31 |
32 | let boldFont: UIFont = try #require(
33 | font.withBoldStyling()
34 | )
35 |
36 | #expect(boldFont.fontDescriptor.symbolicTraits.contains(.traitBold))
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/6. Performance and Optimization/6.3. Final Declarations.md:
--------------------------------------------------------------------------------
1 | # Final Declarations
2 |
3 | It's a good practice to mark all declarations as `final`, unless there is a specific need to override them. The `final` keyword allows the compiler to use `static` dispatch, which improves performance.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | class NonParentClass {
9 | ...
10 | }
11 | ```
12 |
13 | Preferred:
14 |
15 | ```swift
16 | final class NonParentClass {
17 | ...
18 | }
19 | ```
20 |
21 | Furthermore, marking a `class` as `final` ensures that it cannot be subclassed, preventing unintended changes and enhancing the stability of the codebase.
22 |
23 | Not Preferred:
24 |
25 |
26 | ```swift
27 | class Parent {
28 | func overridableMethod() { ... }
29 |
30 | func nonOverridableMethod() { ... }
31 | }
32 | ```
33 |
34 | Preferred:
35 |
36 |
37 | ```swift
38 | class Parent {
39 | func overridableMethod() { ... }
40 |
41 | final func nonOverridableMethod() { ... }
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Array/Array+ReversedOnCondition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+ReversedOnCondition.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Array + Reversed on Condition
11 | extension Array {
12 | /// Returns reversed `Array` if condition is met.
13 | ///
14 | /// ["London", "Paris", "New York"]
15 | /// .reversed(reversesOrder)
16 | ///
17 | public func reversed(
18 | _ condition: Bool
19 | ) -> Self {
20 | if condition {
21 | self.reversed()
22 | } else {
23 | self
24 | }
25 | }
26 |
27 | /// Reverses `Array` if condition is met.
28 | ///
29 | /// var array: [String] = ["London", "Paris", "New York"]
30 | /// array.reversed(reversesOrder)
31 | ///
32 | mutating public func reverse(
33 | _ condition: Bool
34 | ) {
35 | if condition {
36 | reverse()
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit and AppKit/NSLayoutConstraint/NSLayoutConstraintInitWithPriorityTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSLayoutConstraintInitWithPriorityTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 13.07.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | @MainActor
17 | struct NSLayoutConstraintInitWithPriorityTests {
18 | @Test
19 | func test() {
20 | let view1: UIView = .init()
21 | let view2: UIView = .init()
22 |
23 | let constraint: NSLayoutConstraint = .init(
24 | item: view1,
25 | attribute: .width,
26 | relatedBy: .equal,
27 | toItem: view2,
28 | attribute: .width,
29 | multiplier: 1,
30 | constant: 0,
31 | priority: .required
32 | )
33 |
34 | #expect(constraint.priority == .required)
35 | }
36 | }
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit and AppKit/NS Layout Constraint/NSLayoutConstraint+Storing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSLayoutConstraint+Storing.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/13/21.
6 | //
7 |
8 | #if !os(watchOS)
9 |
10 | #if canImport(UIKit)
11 | import UIKit
12 | #elseif canImport(AppKit)
13 | import AppKit
14 | #endif
15 |
16 | // MARK: - NS Layout Constraint + Storing
17 | extension NSLayoutConstraint {
18 | /// Allows for the storing of a layout constraint, while using it in `NSLayoutConstraint.activate(_:)`.
19 | ///
20 | /// var tableViewLeadingConstraint: NSLayoutConstraint!
21 | ///
22 | /// NSLayoutConstraint.activate([
23 | /// tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
24 | /// .storing(in: &tableViewLeadingConstraint)
25 | /// ])
26 | ///
27 | public func storing(in container: inout NSLayoutConstraint?) -> NSLayoutConstraint {
28 | container = self
29 | return self
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionRandomElementsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionRandomElementsTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 30.04.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionRandomElementsTests {
15 | @Test
16 | func testValid() throws {
17 | let numbers: [Int] = [1, 3, 5, 7, 9]
18 |
19 | try 10.times {
20 | let randomNumbers: [Int] = try #require(
21 | numbers.randomElements(Int.random(in: 0...numbers.count))
22 | )
23 |
24 | #expect(randomNumbers.isUnique)
25 | #expect(randomNumbers.count <= numbers.count)
26 | }
27 | }
28 |
29 | @Test
30 | func testOutOfBounds() {
31 | let numbers: [Int] = [1, 3, 5, 7, 9]
32 |
33 | #expect(numbers.randomElements(-1) == nil)
34 | #expect(numbers.randomElements(numbers.count+1) == nil)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+BlocksHitTesting.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+BlocksHitTesting.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 03.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Blocks Hit Testing
11 | extension View {
12 | /// Overlays clear `Rectangle` that blocks gestures if condition is met.
13 | ///
14 | /// var body: some View {
15 | /// Button("Lorem Ipsum", action: doSomething)
16 | /// .blocksHitTesting(!isInteractionEnabled)
17 | /// }
18 | ///
19 | public func blocksHitTesting(
20 | _ flag: Bool = true
21 | ) -> some View {
22 | self
23 | .overlay(content: {
24 | if flag {
25 | Color.clear
26 | .contentShape(.rect)
27 | }
28 | })
29 | }
30 | }
31 |
32 | // MARK: - Preview
33 | #if DEBUG
34 |
35 | #Preview(body: {
36 | Button("Press", action: {})
37 | .blocksHitTesting()
38 | })
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/CastingError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CastingError.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 03.09.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Casting Error
11 | /// Error that occurs during casting.
12 | public struct CastingError: VCoreError, Equatable, Sendable {
13 | // MARK: Properties
14 | private let fromType: String
15 | private let toType: String
16 |
17 | // MARK: Initializers
18 | /// Initializes `CastingError` with types.
19 | public init(
20 | from fromType: String,
21 | to toType: String
22 | ) {
23 | self.fromType = fromType
24 | self.toType = toType
25 | }
26 |
27 | // MARK: Properties - VCore Error
28 | public let vCoreErrorCode: Int = 0
29 |
30 | public var vCoreErrorDescription: String { "Failed to cast '\(fromType)' to '\(toType)'" }
31 |
32 | // MARK: Equatable
33 | public static func == (lhs: Self, rhs: Self) -> Bool {
34 | isEqual(lhs, to: rhs, by: \.fromType, \.toType)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/VCoreMacrosImplementation/Models/AccessLevelModifierKeyword.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AccessLevelModifierKeyword.swift
3 | // VCoreMacrosImplementation
4 | //
5 | // Created by Vakhtang Kontridze on 23.05.24.
6 | //
7 |
8 | import Foundation
9 | import SwiftSyntax
10 |
11 | // MARK: - Access Level Modifier Keyword
12 | enum AccessLevelModifierKeyword: String, CaseIterable {
13 | // MARK: Cases
14 | case `open` = "open"
15 | case `public` = "public"
16 | case `package` = "package"
17 | case `internal` = "internal"
18 | case `fileprivate` = "fileprivate"
19 | case `private` = "private"
20 |
21 | // MARK: Properties
22 | var swiftSyntaxKeyword: Keyword {
23 | switch self {
24 | case .open: .open
25 | case .public: .public
26 | case .package: .package
27 | case .internal: .internal
28 | case .fileprivate: .fileprivate
29 | case .private: .private
30 | }
31 | }
32 |
33 | // MARK: Initializers
34 | static var `default`: Self { .internal }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/VCore/Services and Managers/Presentation Host/Host/PresentationHostUIModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PresentationHostUIModel.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11.07.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Presentation Host UIModel
11 | /// Model that describes UI.
12 | public struct PresentationHostUIModel: Sendable {
13 | // MARK: Properties - Frame
14 | /// Alignment of modal in the layer. Set to `center`.
15 | public var alignment: Alignment = .center
16 |
17 | // MARK: Properties - Dimming View
18 | /// Preferred dimming color, that overrides a shared color from `PresentationHostLayerUIModel`, when only this modal is presented.
19 | public var preferredDimmingViewColor: Color?
20 |
21 | // MARK: Properties - Misc
22 | /// Indicates if modal is dismissed when host disappears. Set to `true`.
23 | public var dismissesModalWhenHostDisappears: Bool = true
24 |
25 | // MARK: Initializers
26 | /// Initializes UI model with default values.
27 | public init() {}
28 | }
29 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/AppKit/NSColor/NSColorRGBAValuesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSColorRGBAValuesTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 04.07.22.
6 | //
7 |
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 |
10 | import AppKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct NSColorRGBAValuesTests {
17 | @Test
18 | func testValues() {
19 | #expect(
20 | NSColor(red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4).rgbaValues ==
21 | (0.1, 0.2, 0.3, 0.4)
22 | )
23 | }
24 |
25 | @Test
26 | func testComponents() {
27 | #expect(
28 | NSColor(red: 10.0/255, green: 20.0/255, blue: 30.0/255, alpha: 0.5).rgbaComponents ==
29 | (10, 20, 30, 0.5)
30 | )
31 | }
32 |
33 | @Test
34 | func testISRGBAEqual() {
35 | #expect(NSColor.red.isRGBAEqual(to: .red))
36 | #expect(!NSColor.red.isRGBAEqual(to: .blue))
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/AppKit/NSFont/NSFontStylingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSFontStylingTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 04.07.22.
6 | //
7 |
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 |
10 | import AppKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct NSFontStylingTests {
17 | @Test
18 | func testItalic() throws {
19 | let font: NSFont = .systemFont(ofSize: 13)
20 |
21 | let italicFont: NSFont = try #require(
22 | font.withItalicStyling()
23 | )
24 |
25 | #expect(italicFont.fontDescriptor.symbolicTraits.contains(.italic))
26 | }
27 |
28 | @Test
29 | func testBold() throws {
30 | let font: NSFont = .systemFont(ofSize: 13)
31 |
32 | let boldFont: NSFont = try #require(
33 | font.withBoldStyling()
34 | )
35 |
36 | #expect(boldFont.fontDescriptor.symbolicTraits.contains(.bold))
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Array/Array+RemoveIfPresent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+RemoveIfPresent.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 26.11.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Array + Remove If Present
11 | extension Array where Element: Equatable {
12 | /// Removes the element if it is present.
13 | ///
14 | /// var array: [Int] = [1, 2, 3]
15 | /// array.removeIfPresent(3) // [1, 2]
16 | ///
17 | mutating public func removeIfPresent(_ element: Element) {
18 | removeAll(where: { $0 == element })
19 | }
20 |
21 | /// Removes all the elements if they are present.
22 | ///
23 | /// var array: [Int] = [1, 2, 3]
24 | /// array.removeIfPresent(contentsOf: [2, 3]) // [1]
25 | ///
26 | mutating public func removeIfPresent(
27 | contentsOf elements: S
28 | )
29 | where
30 | S: Sequence,
31 | S.Element == Element
32 | {
33 | removeAll(where: { elements.contains($0) })
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+ApplyModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+ApplyModifier.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 23.03.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Apply Modifier
11 | extension View {
12 | /// Applies a block modifier to a `View` and returns a new `View`.
13 | ///
14 | /// This method should be used with caution, since any changes to the condition will cause view state to be reset.
15 | ///
16 | /// var body: some View {
17 | /// SomeView()
18 | /// .applyModifier({
19 | /// if #available(iOS 99.0, *) {
20 | /// $0.someModifier()
21 | /// } else {
22 | /// $0
23 | /// }
24 | /// })
25 | /// }
26 | ///
27 | public func applyModifier(
28 | @ViewBuilder _ block: (Self) -> Content
29 | ) -> some View
30 | where Content: View
31 | {
32 | block(self)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/VCore/Views/UIKit/InteractivePoppingUINavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InteractivePoppingUINavigationController.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.07.22.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - Interactive Popping UI Navigation Controller
13 | /// `UINavigationController` that conforms to `UIGestureRecognizerDelegate` via `interactivePopGestureRecognizer`,
14 | /// and handles interactive popping.
15 | @available(tvOS, unavailable)
16 | open class InteractivePoppingUINavigationController: UINavigationController, Sendable, UIGestureRecognizerDelegate {
17 | // MARK: Lifecycle
18 | open override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | interactivePopGestureRecognizer?.delegate = self
22 | }
23 |
24 | // MARK: Gesture Recognizer Delegate
25 | open func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
26 | viewControllers.count > 1
27 | }
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Color/Color+PrimaryInverted.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+PrimaryInverted.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 24.01.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color + Primary Inverted
11 | extension Color {
12 | /// `Color` that can be used for inverted primary content.
13 | ///
14 | /// Text("Lorem ipsum")
15 | /// .foregroundStyle(Color.primaryInverted)
16 | /// .padding()
17 | /// .background(content: { Color.primary })
18 | ///
19 | public static let primaryInverted: Color = .platformDynamic(Color.white, Color.black)
20 | }
21 |
22 | // MARK: - Preview
23 | #if DEBUG
24 |
25 | #Preview(body: {
26 | VStack(content: {
27 | Text("Lorem ipsum")
28 | .foregroundStyle(Color.primary)
29 | .padding()
30 |
31 | Text("Lorem ipsum")
32 | .foregroundStyle(Color.primaryInverted)
33 | .padding()
34 | .background(content: { Color.primary })
35 | })
36 | })
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/HTTP Header Fields/JSONRequestHeaderFields.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSONRequestHeaderFields.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 28.04.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - JSON Request Header Fields
11 | /// JSON request header fields that pass `application/json` as `accept` and `application/json` as `contentType`.
12 | ///
13 | /// Can be used in `URLRequest`.
14 | ///
15 | /// var request: URLRequest = ...
16 | /// try request.addHTTPHeaderFields(object: JSONRequestHeaderFields())
17 | ///
18 | @CodingKeysGeneration
19 | public struct JSONRequestHeaderFields: Sendable, Encodable {
20 | // MARK: Properties
21 | /// Accept. Set to `application/json`.
22 | @CKGProperty("Accept") public let accept: String = "application/json"
23 |
24 | /// Content type. Set to `application/json`.
25 | @CKGProperty("Content-Type") public let contentType: String = "application/json"
26 |
27 | // MARK: Initializers
28 | /// initializes `JSONRequestHeaderFields`.
29 | public init() {}
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI View/UIView+DismissKeyboardOnOutsideTap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+DismissKeyboardOnOutsideTap.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/12/21.
6 | //
7 |
8 | #if canImport(UIKit) && !os(watchOS)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI View + Dismiss Keyboard on Outside Tap
13 | extension UIView {
14 | /// Dismisses keyboard on outside tap, by resigning the first responder.
15 | ///
16 | /// override func viewDidLoad() {
17 | /// super.viewDidLoad()
18 | /// view.dismissKeyboardOnOutsideTap()
19 | /// }
20 | ///
21 | public func dismissKeyboardOnOutsideTap() {
22 | addGestureRecognizer({
23 | let gesture: UITapGestureRecognizer = .init(target: self, action: #selector(endEditingOnOutsideTap))
24 | gesture.cancelsTouchesInView = false
25 | return gesture
26 | }())
27 | }
28 |
29 | @objc
30 | private func endEditingOnOutsideTap() {
31 | endEditing(true)
32 | }
33 | }
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Services and Managers/AutoPrecisionNumberFormatterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AutoPrecisionNumberFormatterTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 17.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct AutoPrecisionNumberFormatterTests {
15 | @Test
16 | func testValidFormatting() {
17 | let formatter: AutoPrecisionNumberFormatter = .init(
18 | minFractions: 0,
19 | maxFractions: 2
20 | )
21 |
22 | #expect(formatter.string(from: 3) == "3")
23 | #expect(formatter.string(from: 3.1) == "3.1")
24 | #expect(formatter.string(from: 3.14) == "3.14")
25 | #expect(formatter.string(from: 3.141) == "3.14")
26 | }
27 |
28 | @Test
29 | func testExtension() {
30 | let value: String? = 3.1415.rounded(
31 | minFractions: 0,
32 | maxFractions: 2
33 | )
34 |
35 | #expect(value == "3.14")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI Image/UIImage+InitWithUIColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+InitWithUIColor.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Image + Init with UI Color
13 | extension UIImage {
14 | /// Initializes `UIImage` with `CGSize` and `UIColor`.
15 | ///
16 | /// let image: UIImage = .init(
17 | /// size: CGSize(dimension: 100),
18 | /// color: UIColor.systemBlue
19 | /// )!
20 | ///
21 | convenience public init?(
22 | size: CGSize,
23 | color: UIColor
24 | ) {
25 | UIGraphicsBeginImageContextWithOptions(size, false, 1)
26 | defer { UIGraphicsEndImageContext() }
27 |
28 | color.setFill()
29 | UIRectFill(CGRect(origin: .zero, size: size))
30 |
31 | guard let cgImage: CGImage = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { return nil }
32 | self.init(cgImage: cgImage)
33 | }
34 | }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Combine/Publisher/PublisherAsignWeakTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PublisherAssignWeakTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 21.02.24.
6 | //
7 |
8 | import Foundation
9 | import Combine
10 | import XCTest
11 | @testable import VCore
12 |
13 | // MARK: - Tests
14 | @MainActor
15 | final class PublisherAssignWeakTests: XCTestCase {
16 | // MARK: Test Data
17 | private final class SomeClass {
18 | var value: Int = 0
19 |
20 | private let publisher: PassthroughSubject = .init()
21 | private var subscriptions: Set = []
22 |
23 | init() {
24 | addSubscriptions()
25 | }
26 |
27 | private func addSubscriptions() {
28 | publisher
29 | .assignWeak(to: \.value, on: self)
30 | .store(in: &subscriptions)
31 | }
32 | }
33 |
34 | // MARK: Tests
35 | func test() async {
36 | let object: SomeClass = .init()
37 | assertInstanceIsDeallocated(object)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Collection/CollectionAllMatchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionAllMatchTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 21.02.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct CollectionAllMatchTests {
15 | @Test
16 | func testArray() {
17 | let array: [Int] = [1, 2, 3]
18 |
19 | #expect(!array.allMatch({ abs($0 - $1) <= 1 }))
20 | #expect(array.allMatch({ abs($0 - $1) <= 2 }))
21 | }
22 |
23 | @Test
24 | func testSet() {
25 | let set: Set = [1, 2, 3]
26 |
27 | #expect(!set.allMatch({ abs($0 - $1) <= 1 }))
28 | #expect(set.allMatch({ abs($0 - $1) <= 2 }))
29 | }
30 |
31 | @Test
32 | func testDictionary() {
33 | let dictionary: [Int: Int] = [1: 1, 2: 2, 3: 3]
34 |
35 | #expect(!dictionary.allMatch({ abs($0.value - $1.value) <= 1 }))
36 | #expect(dictionary.allMatch({ abs($0.value - $1.value) <= 2 }))
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Color/Color+Dynamic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Dynamic.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 24.01.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color + Dynamic
11 | @available(watchOS, unavailable) // Light/dark mode and `UIColor(dynamicProvider:)` API do not exist. OS selects light color from the assets catalogue.
12 | @available(visionOS, unavailable) // Light/dark mode do not exist. OS selects dark color from the assets catalogue.
13 | extension Color {
14 | /// Creates `Color` that generates it's color data dynamically.
15 | ///
16 | /// let color: Color = .dynamic(Color.black, Color.white)
17 | ///
18 | public static func dynamic(
19 | _ light: Color,
20 | _ dark: Color
21 | ) -> Self {
22 | #if canImport(UIKit)
23 | Color(
24 | uiColor: UIColor.dynamic(UIColor(light), UIColor(dark))
25 | )
26 | #elseif canImport(AppKit)
27 | Color(
28 | nsColor: NSColor.dynamic(NSColor(light), NSColor(dark))
29 | )
30 | #endif
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Color/Color+PlatformDynamic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+PlatformDynamic.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 16.02.24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color + Platform Dynamic
11 | extension Color {
12 | /// Creates `Color` that generates it's color data dynamically for each platform.
13 | ///
14 | /// let color: Color = .dynamic(Color.black, Color.white)
15 | ///
16 | public static func platformDynamic(
17 | _ light: Color,
18 | _ dark: Color
19 | ) -> Self {
20 | #if os(iOS)
21 | Color.dynamic(light, dark)
22 | #elseif os(macOS)
23 | Color.dynamic(light, dark)
24 | #elseif os(tvOS)
25 | Color.dynamic(light, dark)
26 | #elseif os(watchOS)
27 | dark
28 | #elseif os(visionOS)
29 | dark
30 | #endif
31 | }
32 | }
33 |
34 | // MARK: - Preview
35 | #if DEBUG
36 |
37 | #Preview(body: {
38 | Text("Lorem ipsum")
39 | .foregroundStyle(Color.platformDynamic(Color.black, Color.white))
40 | .padding()
41 | })
42 |
43 | #endif
44 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayReversedOnConditionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayReversedOnConditionTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 25.02.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayReversedOnConditionTests {
15 | @Test
16 | func testReversedFalse() {
17 | #expect(
18 | [1, 2, 3].reversed(false) ==
19 | [1, 2, 3]
20 | )
21 |
22 | do {
23 | var array: [Int] = [1, 2, 3]
24 | array.reverse(false)
25 |
26 | #expect(array == [1, 2, 3])
27 | }
28 | }
29 |
30 | @Test
31 | func testReversedTrue() {
32 | #expect(
33 | [1, 2, 3].reversed(true) ==
34 | [3, 2, 1]
35 | )
36 |
37 | do {
38 | var array: [Int] = [1, 2, 3]
39 | array.reverse(true)
40 |
41 | #expect(array == [3, 2, 1])
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+ContainsAnyItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+ContainsAnyItem.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 03.07.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + Contains Any Item
11 | extension Collection where Element: Hashable {
12 | /// Returns a `Bool` indicating whether `Collection` contains any element from other `Collection`.
13 | ///
14 | /// let collection: [Int] = [1, 2, 3]
15 | /// let otherCollection: [Int] = [3, 4, 5]
16 | /// collection.containsAnyItem(fromCollection: otherCollection) // true
17 | ///
18 | public func containsAnyItem(
19 | fromCollection other: C
20 | ) -> Bool
21 | where
22 | C: Collection,
23 | Element == C.Element
24 | {
25 | guard !other.isEmpty else {
26 | return true
27 | }
28 |
29 | for element in other {
30 | if contains(element) {
31 | return true
32 | }
33 | }
34 |
35 | return false
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit/UI RectCorner/UIRectCorner+AdditionalOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIRectCorner+AdditionalOptions.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 13.06.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 |
12 | // MARK: - UI Rect Corner + Additional Options
13 | extension UIRectCorner {
14 | /// Top corners of the rectangle.
15 | ///
16 | /// Includes `topLeft` and `topRight`.
17 | public static var topCorners: Self { [.topLeft, .topRight] }
18 |
19 | /// Right corners of the rectangle.
20 | ///
21 | /// Includes `topRight` and `bottomRight`.
22 | public static var rightCorners: Self { [.topRight, .bottomRight] }
23 |
24 | /// Bottom corners of the rectangle.
25 | ///
26 | /// Includes `bottomLeft` and `bottomRight`.
27 | public static var bottomCorners: Self { [.bottomLeft, .bottomRight] }
28 |
29 | /// Left corners of the rectangle.
30 | ///
31 | /// Includes `topLeft` and `bottomLeft`.
32 | public static var leftCorners: Self { [.topLeft, .bottomLeft] }
33 | }
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/UIKit/UIImage/UIImageCompressedTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageCompressedTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 | import Testing
12 | @testable import VCore
13 |
14 | // MARK: - Tests
15 | @Suite
16 | struct UIImageCompressedTests {
17 | @Test
18 | func test() throws {
19 | let image: UIImage = try #require(
20 | UIImage(
21 | size: CGSize(dimension: 100),
22 | color: UIColor.red
23 | )
24 | )
25 |
26 | let imageData: Data = try #require(
27 | image.jpegData(compressionQuality: 1)
28 | )
29 |
30 | let compressedImage: UIImage = try #require(
31 | image.jpegCompressed(quality: 0.75)
32 | )
33 |
34 | let compressedImageData: Data = try #require(
35 | compressedImage.jpegData(compressionQuality: 1)
36 | )
37 |
38 | #expect(compressedImageData.count < imageData.count)
39 | }
40 | }
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Extensions/UIImage+MergeHorizontally.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage.MergeHorizontally.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 10.05.22.
6 | //
7 |
8 | #if canImport(UIKit)
9 |
10 | import UIKit
11 |
12 | // MARK: - Image + Merge Horizontally
13 | extension UIImage {
14 | // Doesn't consider positioning. Only used for testing purposes,
15 | // when `lhs.size` equals `rhs.size`.
16 | static func mergeHorizontally(_ lhs: UIImage, with rhs: UIImage) -> UIImage? {
17 | let newSize: CGSize = .init(
18 | width: lhs.size.width + rhs.size.width,
19 | height: max(lhs.size.height, rhs.size.height)
20 | )
21 |
22 | UIGraphicsBeginImageContext(newSize)
23 | defer { UIGraphicsEndImageContext() }
24 |
25 | lhs.draw(in: CGRect(origin: .zero, size: newSize))
26 | rhs.draw(in: CGRect(origin: CGPoint(x: lhs.size.width, y: 0), size: newSize))
27 | let mergedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
28 |
29 | return mergedImage
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Extra/XCode Templates/View.xctemplate/___FILEBASENAME___.swift:
--------------------------------------------------------------------------------
1 | // ___FILEHEADER___
2 |
3 | import SwiftUI
4 |
5 | // MARK: - ___VARIABLE_productName___
6 | struct ___VARIABLE_productName___: View {
7 | // MARK: Properties - Model Injection
8 | // ...
9 |
10 | // MARK: Properties - UI Model
11 | private let uiModel: ___VARIABLE_productName___UIModel
12 |
13 | // MARK: Properties - Parameters
14 | private let parameters: ___VARIABLE_productName___Parameters
15 |
16 | // MARK: Properties - ???
17 | // ...
18 |
19 | // MARK: Initializers
20 | init(
21 | uiModel: ___VARIABLE_productName___UIModel = .init(),
22 | parameters: ___VARIABLE_productName___Parameters
23 | ) {
24 | self.uiModel = uiModel
25 | self.parameters = parameters
26 | }
27 |
28 | // MARK: Body
29 | var body: some View {
30 | EmptyView()
31 | .background(content: { uiModel.backgroundColor })
32 | }
33 | }
34 |
35 | // MARK: - Preview
36 | #if DEBUG
37 |
38 | #Preview(body: {
39 | ___VARIABLE_productName___(parameters: .mock)
40 | })
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Array/ArrayAsyncSortedTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayAsyncSortedTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 20.05.23.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct ArrayAsyncSortedTests {
15 | @Test
16 | func testSorted() async {
17 | let array: [String] = ["London", "Paris", "New York"]
18 |
19 | let sortedArray: [String] = await array.asyncSorted(by: { (lhs, rhs) in
20 | try? await Task.sleep(nanoseconds: 1_000)
21 | return lhs < rhs
22 | })
23 |
24 | #expect(sortedArray == array.sorted())
25 | }
26 |
27 | @Test
28 | func testSorting() async {
29 | let array: [String] = ["London", "Paris", "New York"]
30 |
31 | var sortedArray: [String] = array
32 | await sortedArray.asyncSort(by: { (lhs, rhs) in
33 | try? await Task.sleep(nanoseconds: 1_000)
34 | return lhs < rhs
35 | })
36 |
37 | #expect(sortedArray == array.sorted())
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Sequence/SequenceSortedByKeyPathTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceSortedByKeyPathTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct SequenceSortedByKeyPathTests {
15 | // MARK: Test Data
16 | private struct City: Equatable {
17 | let name: String
18 | }
19 |
20 | private let array: [City] = [
21 | City(name: "London"),
22 | City(name: "Paris"),
23 | City(name: "New York")
24 | ]
25 |
26 | private let sortedArray: [City] = [
27 | City(name: "London"),
28 | City(name: "New York"),
29 | City(name: "Paris")
30 | ]
31 |
32 | // MARK: Tests
33 | @Test
34 | func test() {
35 | #expect(array.sorted(by: \.name) == sortedArray)
36 |
37 | do {
38 | var array = array
39 | array.sort(by: \.name)
40 |
41 | #expect(array == sortedArray)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.13. Multi-Line String Literals.md:
--------------------------------------------------------------------------------
1 | # Multi-Line String Literals
2 |
3 | Multi-line `String` literals should be formatted with indentation that matches the use of brackets and indentation. Proper formatting will improve the readability of the `String` and make it easier to maintain in the future.
4 |
5 | Not Preferred:
6 |
7 | ```swift
8 | let string: String = """
9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
10 | Aenean tempus justo in nibh venenatis semper.
11 | Fusce posuere magna eu magna scelerisque eleifend.
12 | """
13 | ```
14 |
15 | Preferred:
16 |
17 | ```swift
18 | let string: String =
19 | """
20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
21 | Aenean tempus justo in nibh venenatis semper.
22 | Fusce posuere magna eu magna scelerisque eleifend.
23 | """
24 | ```
25 |
26 | or
27 |
28 | ```swift
29 | let string: String = """
30 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
31 | Aenean tempus justo in nibh venenatis semper.
32 | Fusce posuere magna eu magna scelerisque eleifend.
33 | """
34 | ```
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Vakhtang Kontridze
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Documentation/Swift Style Guide/3. Spacing and Brackets/3.7. Closing Brackets.md:
--------------------------------------------------------------------------------
1 | # Closing Brackets
2 |
3 | ## Table of Contents
4 |
5 | - [Intro](#intro)
6 | - [Brackets](#brackets)
7 | - [Parentheses](#parentheses)
8 | - [Square Brackets](#square-brackets)
9 |
10 | ## Intro
11 |
12 | Closing brackets, parentheses, and square brackets should be placed on a separate line and be aligned with the beginning of the line where the opening bracket, parenthesis, or square bracket was placed (K&R style).
13 |
14 | ## Brackets
15 |
16 | ```swift
17 | func doSomething() {
18 | ...
19 | }
20 | ```
21 |
22 | ## Parentheses
23 |
24 | Not Preferred:
25 |
26 | ```swift
27 | let user: User = .init(
28 | firstName: "First",
29 | lastName: "Last")
30 | ```
31 |
32 | Preferred:
33 |
34 | ```swift
35 | let user: User = .init(
36 | firstName: "First",
37 | lastName: "Last"
38 | )
39 | ```
40 |
41 | ## Square Brackets
42 |
43 | Not Preferred:
44 |
45 | ```swift
46 | let numbers: [Int] = [
47 | 1,
48 | 2,
49 | 3]
50 | ```
51 |
52 | Preferred:
53 |
54 | ```swift
55 | let numbers: [Int] = [
56 | 1,
57 | 2,
58 | 3
59 | ]
60 | ```
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### macOS
2 | ## General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | ## Icon must end with two \r
8 | Icon
9 |
10 | ## Thumbnails
11 | ._*
12 |
13 | ## Files that might appear in the root of a volume
14 | .DocumentRevisions-V100
15 | .fseventsd
16 | .Spotlight-V100
17 | .TemporaryItems
18 | .Trashes
19 | .VolumeIcon.icns
20 | .com.apple.timemachine.donotpresent
21 |
22 | ## Directories potentially created on remote AFP share
23 | .AppleDB
24 | .AppleDesktop
25 | Network Trash Folder
26 | Temporary Items
27 | .apdisk
28 |
29 | ## iCloud generated files
30 | *.icloud
31 |
32 |
33 | ### XCode
34 | ## Project
35 | *.xcodeproj/*
36 | !*.xcodeproj/project.pbxproj
37 | !*.xcodeproj/xcshareddata/
38 | !*.xcworkspace/contents.xcworkspacedata
39 | /*.gcno
40 | **/xcshareddata/WorkspaceSettings.xcsettings
41 |
42 | ## User settings
43 | xcuserdata/
44 |
45 | ## Obj-C/Swift specific
46 | *.hmap
47 |
48 | ## App packaging
49 | *.ipa
50 | *.dSYM.zip
51 | *.dSYM
52 |
53 | ## Playgrounds
54 | timeline.xctimeline
55 | playground.xcworkspace
56 |
57 |
58 | ### Swift Package Manager
59 | /.build
60 | /Packages
61 | /*.xcodeproj
62 | xcuserdata/
63 | DerivedData/
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/String/StringRemovingCharacterSetTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringRemovingCharacterSetTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 09.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringRemovingCharacterSetTests {
15 | @Test
16 | func testRemovingCharacterSet() {
17 | #expect(
18 | "+0123456789".removing(.symbols) ==
19 | "0123456789"
20 | )
21 |
22 | do {
23 | var string: String = "+0123456789"
24 | string.remove(.symbols)
25 |
26 | #expect(string == "0123456789")
27 | }
28 | }
29 |
30 | @Test
31 | func testRemovingCharacterSets() {
32 | #expect(
33 | "+0123456789A".removing([.symbols, .letters]) ==
34 | "0123456789"
35 | )
36 |
37 | do {
38 | var string: String = "+0123456789A"
39 | string.remove([.symbols, .letters])
40 |
41 | #expect(string == "0123456789")
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/VCore/Macros/Attached/OptionSetRepresentation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionSetRepresentation.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Option Set Representation
11 | /// Adds an `OptionSet` from `struct` that contains a nested `Options` `enum`.
12 | ///
13 | /// If `accessLevelModifier` is `nil`, it will be inherited from the type.
14 | ///
15 | /// If type is `public`, `Sendable` conformance must be added.
16 | ///
17 | /// @OptionSetRepresentation
18 | /// struct Gender {
19 | /// private enum Options: Int {
20 | /// case male
21 | /// case female
22 | /// }
23 | /// }
24 | ///
25 | /// let genders: Gender = [...]
26 | ///
27 | /// if genders.contains(.male) {
28 | /// // ...
29 | /// }
30 | ///
31 | @attached(member, names: arbitrary)
32 | @attached(extension, conformances: OptionSet)
33 | public macro OptionSetRepresentation(
34 | accessLevelModifier: AccessLevelModifierKeyword? = nil
35 | ) = #externalMacro(
36 | module: "VCoreMacrosImplementation",
37 | type: "OptionSetRepresentationMacro"
38 | )
39 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/PointPixelMeasurement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PointPixelMeasurement.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 06.08.23.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | // MARK: - Point/Pixel Measurement
11 | /// Display measurement represented in points or pixels.
12 | public enum PointPixelMeasurement: Equatable, Hashable, Sendable {
13 | // MARK: Cases
14 | /// Point measurement.
15 | case points(CGFloat)
16 |
17 | /// Pixel measurement.
18 | case pixels(Int)
19 |
20 | // MARK: Mapping
21 | /// Converts pixels to points based on scale.
22 | public func toPoints(scale: CGFloat) -> CGFloat {
23 | switch self {
24 | case .points(let value): value
25 | case .pixels(let value): CGFloat(value) / scale
26 | }
27 | }
28 |
29 | /// Converts value to pixels based on scale.
30 | public func toPixels(
31 | scale: CGFloat,
32 | roundingRule: FloatingPointRoundingRule = .up
33 | ) -> Int {
34 | switch self {
35 | case .points(let value): Int((value * scale).rounded(roundingRule))
36 | case .pixels(let value): value
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Extensions/String+CombiningDebugItems.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+CombiningDebugItems.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 12.02.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String + Combining Debug Items
11 | extension String {
12 | static func combiningDebugItems(
13 | _ items: Any...
14 | ) -> String {
15 | let messages: [String] = items
16 | .map { item in
17 | switch item {
18 | case let string as String:
19 | return string
20 |
21 | case let error as Error:
22 | return (error as NSError).localizedDescription
23 |
24 | default:
25 | return String(describing: item)
26 | }
27 | }
28 | .compactMap { $0.nonEmptyOrWhiteSpace }
29 |
30 | var result: String = messages.joined(separator: ". ")
31 | if messages.count > 1 {
32 | if result.last != "." { result.append(".") }
33 | result = result.replacingOccurrences(of: ".. ", with: ". ")
34 | }
35 |
36 | return result
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/AbsoluteFractionMeasurement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AbsoluteFractionMeasurement.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 16.07.24.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | // MARK: - Absolute/Fraction Measurement
11 | /// Length measurement represented in absolute or fractional values.
12 | public enum AbsoluteFractionMeasurement: Equatable, Hashable, Sendable {
13 | // MARK: Cases
14 | /// Absolute measurement.
15 | case absolute(CGFloat)
16 |
17 | /// Fraction measurement.
18 | case fraction(CGFloat)
19 |
20 | // MARK: Mapping
21 | /// Converts fractional to absolute dimension.
22 | public func toAbsolute(
23 | dimension: CGFloat
24 | ) -> CGFloat {
25 | switch self {
26 | case .absolute(let value): value
27 | case .fraction(let value): value * dimension
28 | }
29 | }
30 |
31 | /// Converts absolute to fractional dimension.
32 | public func toFraction(
33 | dimension: CGFloat
34 | ) -> CGFloat {
35 | switch self {
36 | case .absolute(let value): value / dimension
37 | case .fraction(let value): value
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Collection/Collection+AllMatch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+AllMatch.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 21.02.23.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Collection + All Match
11 | extension Collection {
12 | /// Returns `Bool` indicating whether every element of a sequence satisfies a given comparison predicate.
13 | ///
14 | /// This method is different from `allSatisfy(_:)`, since it passes two elements in a predicate.
15 | ///
16 | /// let array: [Int] = [1, 2, 3]
17 | ///
18 | /// array.allMatch({ abs($0 - $1) <= 1 }) // false
19 | /// array.allMatch({ abs($0 - $1) <= 2 }) // true
20 | ///
21 | public func allMatch(
22 | _ predicate: (Element, Element) throws -> Bool
23 | ) rethrows -> Bool {
24 | for (i, a) in self.enumerated() {
25 | for (j, b) in self.enumerated() {
26 | guard i != j else { continue }
27 |
28 | if try !predicate(a, b) {
29 | return false
30 | }
31 | }
32 | }
33 |
34 | return true
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/View/View+GetSafeAreaInsets.swift:
--------------------------------------------------------------------------------
1 | //
2 | // View+GetSafeAreaInsets.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 06.08.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - View + Get Safe Area Insets
11 | extension View {
12 | /// Retrieves safe are insets from `View`.
13 | ///
14 | /// @State private var safeAreaInsets: EdgeInsets = .init()
15 | ///
16 | /// var body: some View {
17 | /// Color.accentColor
18 | /// .getSafeAreaInsets({ safeAreaInsets = $0 })
19 | /// }
20 | ///
21 | public func getSafeAreaInsets(
22 | ignoredKeyboardSafeAreaEdges: Edge.Set = [],
23 | _ action: @escaping (EdgeInsets) -> Void
24 | ) -> some View {
25 | self
26 | .background(content: {
27 | Color.clear
28 | .ignoresSafeArea(.keyboard, edges: ignoredKeyboardSafeAreaEdges)
29 | .onGeometryChange(
30 | for: EdgeInsets.self,
31 | of: { $0.safeAreaInsets },
32 | action: action
33 | )
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Architectural Pattern Helpers/SwiftUI/Confirmation Dialog/ConfirmationDialogButtonConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConfirmationDialogButtonConvertible.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Confirmation Dialog Button Convertible
11 | /// Type that allows for conversion to `ConfirmationDialogButtonProtocol`.
12 | public protocol ConfirmationDialogButtonConvertible {
13 | /// Converts self to `ConfirmationDialogButtonProtocol` `Array`.
14 | func toButtons() -> [any ConfirmationDialogButtonProtocol]
15 | }
16 |
17 | extension Array: ConfirmationDialogButtonConvertible where Element == any ConfirmationDialogButtonProtocol {
18 | public func toButtons() -> [any ConfirmationDialogButtonProtocol] {
19 | self
20 | }
21 | }
22 |
23 | extension Never: ConfirmationDialogButtonConvertible {
24 | public func toButtons() -> [any ConfirmationDialogButtonProtocol] {
25 | fatalError()
26 | }
27 | }
28 |
29 | extension EmptyView: ConfirmationDialogButtonConvertible {
30 | public func toButtons() -> [any ConfirmationDialogButtonProtocol] {
31 | []
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Color/Color+LightenAndDarken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+LightenAndDarken.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 04.07.22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // MARK: - Color + Lighten and Darken
11 | extension Color {
12 | /// Lightens `Color` by a fraction.
13 | ///
14 | /// let lightBlue: Color = .systemBlue.lighten(by: 0.1)
15 | ///
16 | public func lighten(
17 | by fraction: CGFloat
18 | ) -> Color {
19 | #if canImport(UIKit)
20 | Color(uiColor: UIColor(self).lighten(by: fraction))
21 | #elseif canImport(AppKit)
22 | Color(nsColor: NSColor(self).lighten(by: fraction))
23 | #endif
24 | }
25 |
26 | /// Darkens `Color` by a fraction.
27 | ///
28 | /// let darkBlue: Color = .systemBlue.darken(by: 0.1)
29 | ///
30 | public func darken(
31 | by fraction: CGFloat
32 | ) -> Color {
33 | #if canImport(UIKit)
34 | Color(uiColor: UIColor(self).darken(by: fraction))
35 | #elseif canImport(AppKit)
36 | Color(nsColor: NSColor(self).darken(by: fraction))
37 | #endif
38 | }
39 | }
40 |
41 | // Previews are covered in respective UIKit/Appkit files
42 |
--------------------------------------------------------------------------------
/Sources/VCore/Models/VCoreError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VCoreError.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 9/10/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - VCore Error
11 | /// An error that occurs in `VCore`.
12 | public protocol VCoreError: LocalizedError, CustomNSError {
13 | /// Error domain.
14 | static var vCoreErrorDomain: String { get }
15 |
16 | /// Error code.
17 | var vCoreErrorCode: Int { get }
18 |
19 | /// Error description.
20 | var vCoreErrorDescription: String { get }
21 | }
22 |
23 | extension VCoreError {
24 | // VCoreError
25 | public static var vCoreErrorDomain: String { "com.vcore.\(String(describing: Self.self).lowercased())" }
26 |
27 | // LocalizedError
28 | public var errorDescription: String? { vCoreErrorDescription }
29 | public var failureReason: String? { nil }
30 | public var recoverySuggestion: String? { nil }
31 | public var helpAnchor: String? { nil }
32 | public var localizedDescription: String { vCoreErrorDescription }
33 |
34 | // NSError
35 | public static var errorDomain: String { vCoreErrorDomain }
36 | public var errorCode: Int { vCoreErrorCode }
37 | }
38 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/String/StringKeepingOnlyCharacterSetTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringKeepingOnlyCharacterSetTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct StringKeepingCharacterSetTests {
15 | @Test
16 | func testKeepingCharacterSet() {
17 | #expect(
18 | "+0123456789".keeping(only: .decimalDigits) ==
19 | "0123456789"
20 | )
21 |
22 | do {
23 | var string: String = "+0123456789"
24 | string.keep(only: .decimalDigits)
25 |
26 | #expect(string == "0123456789")
27 | }
28 | }
29 |
30 | @Test
31 | func testKeepingCharacterSets() {
32 | #expect(
33 | "+0123456789A".keeping(only: [.decimalDigits, .symbols]) ==
34 | "+0123456789"
35 | )
36 |
37 | do {
38 | var string: String = "+0123456789A"
39 | string.keep(only: [.decimalDigits, .symbols])
40 |
41 | #expect(string == "+0123456789")
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/SwiftUI/Image/Image+InitWithData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image+InitWithData.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10.03.23.
6 | //
7 |
8 | import SwiftUI
9 | import OSLog
10 |
11 | // MARK: - Image + Init with Data
12 | extension Image {
13 | /// Initializes and returns the `Image` with the specified `Data`.
14 | ///
15 | /// var body: some View {
16 | /// Image(data: data)
17 | /// }
18 | ///
19 | public init(data: Data) {
20 | #if canImport(UIKit)
21 |
22 | guard
23 | let uiImage: UIImage = .init(data: data)
24 | else {
25 | Logger.misc.critical("Failed to initialize 'UIImage' from 'Data' in 'Image.init(data:)'")
26 | fatalError()
27 | }
28 |
29 | self.init(uiImage: uiImage)
30 |
31 | #elseif canImport(AppKit)
32 |
33 | guard
34 | let nsImage: NSImage = .init(data: data)
35 | else {
36 | Logger.misc.critical("Failed to initialize 'NSImage' from 'Data' in 'Image.init(data:)'")
37 | fatalError()
38 | }
39 |
40 | self.init(nsImage: nsImage)
41 |
42 | #endif
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/VCoreShared/Extensions/Foundation/String/String+HexColorRGBValues.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+HexColorRGBValues.swift
3 | // VCoreShared
4 | //
5 | // Created by Vakhtang Kontridze on 08.01.24.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - String + Hex Color RGB Values
11 | extension String {
12 | package func _hexColorRGBValues() -> (red: CGFloat, green: CGFloat, blue: CGFloat)? {
13 | var string: String = trimmingCharacters(in: .whitespacesAndNewlines)
14 | if string.hasPrefix("#") { _ = string.removeFirst() }
15 |
16 | guard string.count == 6 else { return nil }
17 |
18 | let int: UInt64 = string.scanHexColorStringToUInt64()
19 |
20 | let mask: Int = 0x00_00FF
21 |
22 | let red: CGFloat = CGFloat(Int(int >> 16) & mask) / 255
23 | let green: CGFloat = CGFloat(Int(int >> 8) & mask) / 255
24 | let blue: CGFloat = CGFloat(Int(int) & mask) / 255
25 |
26 | return (red, green, blue)
27 | }
28 |
29 | private func scanHexColorStringToUInt64() -> UInt64 {
30 | let scanner: Scanner = .init(string: self)
31 |
32 | var color: UInt64 = 0
33 | scanner.scanHexInt64(&color)
34 |
35 | return color
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/VCore/Helpers/Logging.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logging.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 12.02.24.
6 | //
7 |
8 | import Foundation
9 | import OSLog
10 |
11 | // MARK: - Loggers
12 | extension Logger {
13 | static let baseButtonGestureRecognizer: Self = .init(subsystem: "VCore", category: "BaseButtonGestureRecognizer")
14 | static let carouselCollectionViewFlowLayout: Self = .init(subsystem: "VCore", category: "CarouselCollectionViewFlowLayout")
15 | static let infiniteScrollingUICollectionView: Self = .init(subsystem: "VCore", category: "InfiniteScrollingUICollectionView")
16 |
17 | static let keychainService: Self = .init(subsystem: "VCore", category: "KeychainService")
18 | static let userDefaultsService: Self = .init(subsystem: "VCore", category: "UserDefaultsService")
19 | static let localizationManager: Self = .init(subsystem: "VCore", category: "LocalizationManager")
20 | static let presentationHost: Self = .init(subsystem: "VCore", category: "PresentationHost")
21 | static let networkReachabilityService: Self = .init(subsystem: "VCore", category: "NetworkReachabilityService")
22 |
23 | static let misc: Self = .init(subsystem: "VCore", category: "Misc")
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Bundle/Bundle+Info.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bundle+Info.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/8/21.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Bundle + Info
11 | extension Bundle {
12 | /// App display name.
13 | ///
14 | /// let name: String? = Bundle.main.name
15 | ///
16 | public var name: String? {
17 | object(forInfoDictionaryKey: "CFBundleName") as? String
18 | }
19 |
20 | /// App display name.
21 | ///
22 | /// let displayName: String? = Bundle.main.displayName
23 | ///
24 | public var displayName: String? {
25 | object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
26 | }
27 |
28 | /// App version.
29 | ///
30 | /// let version: String? = Bundle.main.version
31 | ///
32 | public var version: String? {
33 | object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
34 | }
35 |
36 | /// App build number.
37 | ///
38 | /// let buildNumber: String? = Bundle.main.buildNumber
39 | ///
40 | public var buildNumber: String? {
41 | object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Tests/VCoreTests/Tests/Extensions/Foundation/Date/DateComponentTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateComponentTests.swift
3 | // VCoreTests
4 | //
5 | // Created by Vakhtang Kontridze on 06.05.22.
6 | //
7 |
8 | import Foundation
9 | import Testing
10 | @testable import VCore
11 |
12 | // MARK: - Tests
13 | @Suite
14 | struct DateComponentTests {
15 | @Test
16 | func testComponent() throws {
17 | let dateComponents: DateComponents = .init(year: 1970, month: 1, day: 1)
18 |
19 | let date: Date = try #require(
20 | Calendar.current.date(from: dateComponents)
21 | )
22 |
23 | #expect(date.component(.year) == dateComponents.year)
24 | #expect(date.component(.month) == dateComponents.month)
25 | #expect(date.component(.day) == dateComponents.day)
26 | }
27 |
28 | @Test
29 | func testComponents() throws {
30 | let dateComponents: DateComponents = .init(year: 1970, month: 1, day: 1)
31 |
32 | let date: Date = try #require(
33 | Calendar.current.date(from: dateComponents)
34 | )
35 |
36 | #expect(
37 | date.components([.year, .month, .day]) ==
38 | dateComponents
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/Foundation/Set/Set+TogglingElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Set+TogglingElement.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 01.05.22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Set + Toggling Element
11 | extension Set {
12 | /// Returns set with element toggled, i.e., removed if it's present, and inserted if not.
13 | ///
14 | /// let numbers1: Set = [1, 3, 5]
15 | /// let numbers2: Set = numbers1.toggling(1) // [3, 5]
16 | /// let numbers3: Set = numbers2.toggling(1) // [1, 3, 5]
17 | ///
18 | public func toggling(
19 | _ element: Element
20 | ) -> Self {
21 | var set = self
22 | set.toggle(element)
23 | return set
24 | }
25 |
26 | /// Removes element from a set if it's present, and inserts if not.
27 | ///
28 | /// var numbers: Set = [1, 3, 5]
29 | /// numbers.toggle(1) // [3, 5]
30 | /// numbers.toggle(1) // [1, 3, 5]
31 | ///
32 | mutating public func toggle(
33 | _ element: Element
34 | ) {
35 | if contains(element) {
36 | remove(element)
37 | } else {
38 | insert(element)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/VCore/Extensions/UIKit and AppKit/NS Layout Constraint/NSLayoutConstraint+WithMultiplier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSLayoutConstraint+WithMultiplier.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 11/5/21.
6 | //
7 |
8 | #if !os(watchOS)
9 |
10 | #if canImport(UIKit)
11 | import UIKit
12 | #elseif canImport(AppKit)
13 | import AppKit
14 | #endif
15 |
16 | // MARK: - NS Layout Constraint + With Multiplier
17 | extension NSLayoutConstraint {
18 | /// Modifies and returns constraint with a given priority.
19 | ///
20 | /// Must be called after other `NSLayoutConstraint` modifiers.
21 | ///
22 | /// NSLayoutConstraint.activate([
23 | /// view.widthAnchor.constraint(equalTo: someOtherView.widthAnchor)
24 | /// .withMultiplier(0.5)
25 | /// ])
26 | ///
27 | public func withMultiplier(_ multiplier: CGFloat) -> NSLayoutConstraint {
28 | .init(
29 | item: firstItem as Any,
30 | attribute: firstAttribute,
31 | relatedBy: relation,
32 | toItem: secondItem,
33 | attribute: secondAttribute,
34 | multiplier: multiplier,
35 | constant: constant
36 | )
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Sources/VCore/Services and Managers/Presentation Host/Presentation Mode (Internal)/PresentationHostInternalPresentationMode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PresentationHostInternalPresentationMode.swift
3 | // VCore
4 | //
5 | // Created by Vakhtang Kontridze on 10.07.24.
6 | //
7 |
8 | import SwiftUI
9 | import Combine
10 |
11 | // MARK: - Presentation Host Internal Presentation Mode
12 | final class PresentationHostInternalPresentationMode: ObservableObject {
13 | // MARK: Properties
14 | let presentPublisher: PassthroughSubject = .init()
15 | struct PresentationData: Identifiable {
16 | let id: String
17 | let uiModel: PresentationHostUIModel
18 | let view: () -> AnyView
19 | let completion: () -> Void
20 | }
21 |
22 | let updatePublisher: PassthroughSubject = .init()
23 | struct UpdateData {
24 | let id: String
25 | let uiModel: PresentationHostUIModel
26 | let view: () -> AnyView
27 | }
28 |
29 | let dismissPublisher: PassthroughSubject = .init()
30 | struct DismissData {
31 | let id: String
32 | let completion: () -> Void
33 | }
34 |
35 | // MARK: Initializers
36 | init() {}
37 | }
38 |
--------------------------------------------------------------------------------