├── 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 | --------------------------------------------------------------------------------