diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml
index 195abca..55bb603 100644
--- a/.github/workflows/run_tests.yml
+++ b/.github/workflows/run_tests.yml
@@ -1,9 +1,8 @@
name: Tests
on:
- push:
- branches-ignore:
- - master
+ pull_request:
+ types: [opened, synchronize]
jobs:
run_tests:
@@ -12,4 +11,4 @@ jobs:
- name: Prepare branch on runner
uses: actions/checkout@v3
- name: Build & Test
- run: xcodebuild build clean test -scheme "Uptech-iOS-Helpers" -destination "platform=iOS Simulator,name=IPhone 11"
+ run: xcodebuild build clean test -scheme "Uptech-iOS-Helpers-Package" -destination "platform=iOS Simulator,name=IPhone 15"
diff --git a/Package.swift b/Package.swift
index 4213c72..f0a2f7b 100644
--- a/Package.swift
+++ b/Package.swift
@@ -3,13 +3,23 @@
import PackageDescription
let package = Package(
- name: "Uptech-iOS-Helpers",
- platforms: [.iOS(.v11)],
- products: [
- .library(name: "Uptech-iOS-Helpers", targets: ["Uptech-iOS-Helpers"]),
- ],
- targets: [
- .target(name: "Uptech-iOS-Helpers", dependencies: []),
- .testTarget(name: "Uptech-iOS-Helpers-Tests", dependencies: ["Uptech-iOS-Helpers"]),
- ]
+ name: "Uptech-iOS-Helpers",
+ platforms: [.iOS(.v13)],
+ products: [
+ .library(name: "Uptech-iOS-Helpers", targets: ["UptechFoundationHelper", "UptechUIKitHelper", "UptechSwiftUIHelper"]),
+ .library(name: "UptechFoundationHelper", targets: ["UptechFoundationHelper"]),
+ .library(name: "UptechUIKitHelper", targets: ["UptechUIKitHelper"]),
+ .library(name: "UptechSwiftUIHelper", targets: ["UptechSwiftUIHelper"])
+ ],
+ targets: [
+ .target(name: "UptechFoundationHelper", path: "Sources/Uptech-iOS-Helpers/FoundationHelper"),
+ .target(name: "UptechUIKitHelper", path: "Sources/Uptech-iOS-Helpers/UIKitHelper"),
+ .target(name: "UptechSwiftUIHelper", path: "Sources/Uptech-iOS-Helpers/SwiftUIHelper"),
+ .testTarget(name: "FoundationHelper-Test",
+ dependencies: ["UptechFoundationHelper"],
+ path: "Tests/Uptech-iOS-Helpers-Tests/FoundationHelper"),
+ .testTarget(name: "UIKitHelper-Test",
+ dependencies: ["UptechUIKitHelper"],
+ path: "Tests/Uptech-iOS-Helpers-Tests/UIKitHelper")
+ ]
)
diff --git a/README.md b/README.md
index b84d0fe..bfb900b 100644
--- a/README.md
+++ b/README.md
@@ -5,41 +5,79 @@
iOS helper library that contains commonly used code in **Uptech** iOS projects.
## What's included
-- Protocols
- - **ReusableCell**
-
- Protocol that helps with reusable cells.
+
+
+ 🔧 Foundation Helpers
+
+
+ - Array extensions
+ - safe subscript
+ - next, previous, remove *Element* methods
+
+ - Collection:
+ - subscript by indexPath
+
+
+
+
+
+ 🖼️ UIKit Helpers
+
+
+ - Protocols
+ - **ReusableCell**
+
+ Protocol that helps with reusable cells.
+
+ If used on UITableViewCell subclasses provides handy methods for registering and dequeueing cells in UITableView's
- If used on UITableViewCell subclasses provides handy methods for registering and dequeueing cells in UITableView's
-
- - **NibInitializable**
-
- Protocol that helps to initialize view with xib.
-- Table & Collection View helpers
- - *dequeue* and *register* methods for cells that confirm's to **ReusableCell** and optionally **NibInitializable** (for cell's created via xibs)
-- UI-in-code helpers
- - UIView extensions:
- - subview adding, insertions *with* constrains
- - corner radius
- - NSLayoutConstraint:
- - priority changing method
- - constraint activation method for array of constrains
- - UILayoutPriority:
- - frequently used values
-- Array extensions
- - safe subscript
- - next, previous, remove *Element* methods
-- Collection:
- - subscript by indexPath
+ - **NibInitializable**
+
+ Protocol that helps to initialize view with xib.
+ - Table & Collection View helpers
+ - *dequeue* and *register* methods for cells that confirm's to **ReusableCell** and optionally **NibInitializable** (for cell's created via xibs)
+ - UI-in-code helpers
+ - UIView extensions:
+ - subview adding, insertions *with* constrains
+ - corner radius
+ - NSLayoutConstraint:
+ - priority changing method
+ - constraint activation method for array of constrains
+ - UILayoutPriority:
+ - frequently used values
+
+
+
+
+
+ 🕊️ SwiftUI helpers
+
+
+ - Layouts
+ - ProportionalHStack and ProportionalVStack
+ - Layouts that resizes views with given proportions
+
+ - View extensions
+ - conditionalModifiers (if, if/else), regular modifier
+ - customOnChange
+ - size/frame/offset/safeAreaInsets readers
+
+
## Installation
#### Swift Package Manager
*Note: Instructions below are for using SwiftPM without the Xcode UI. It's the easiest to go to your Project Settings -> Swift Packages and add Package from there using link https://github.com/uptechteam/Uptech-iOS-Helpers.git*
+Package contains 4 libraries:
+- UptechFoundationHelper
+- UptechUIKitHelper
+- UptechSwiftUIHelper
+- Uptech-iOS-Helpers (first 3 combined)
+
To integrate using Apple's Swift package manager, without Xcode integration, add the following as a dependency to your Package.swift:
```swift
-.package(url: "https://github.com/uptechteam/Uptech-iOS-Helpers.git", .upToNextMajor(from: "1.0.0"))
+.package(url: "https://github.com/uptechteam/Uptech-iOS-Helpers.git", .upToNextMajor(from: "2.0.0"))
```
#### CocoaPods
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/Foundation/Array+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/Foundation/Array+extensions.swift
deleted file mode 100644
index 27155d0..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/Foundation/Array+extensions.swift
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// Array+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import Foundation
-
-public extension Array {
- /// Returns the element at the specified index if it is within bounds, otherwise nil.
- subscript(safe index: Index) -> Element? {
- indices.contains(index) ? self[index] : nil
- }
-
- /// Returns the elements at the specified range if it is within bounds, otherwise empty array.
- subscript(safe range: Range) -> [Element] {
- guard
- range.startIndex >= startIndex,
- range.endIndex <= endIndex else {
- return []
- }
-
- return Array(self[range])
- }
-
- /// Returns the elements at the specified closed range if it is within bounds, otherwise empty array.
- subscript(safe range: ClosedRange) -> [Element] {
- guard
- range.lowerBound >= 0,
- endIndex > 0,
- range.upperBound <= endIndex else {
- return []
- }
-
- let maxIndex = endIndex - 1
- let upperBound = Swift.min(range.upperBound, maxIndex)
- return Array(self[range.lowerBound...upperBound])
- }
-}
-
-public extension Array where Element: Equatable {
- /// Removes first element in array.
- /// - Parameter item: An element to remove.
- mutating func remove(_ item: Element) {
- guard let index = firstIndex(of: item) else { return }
- remove(at: index)
- }
-
- /// Returns next element in array after a given one.
- /// - Parameter item: An element to search next element.
- /// - Returns: Element after passed item. If passed item is last element in array will return nil.
- func next(item: Element) -> Element? {
- if let index = self.firstIndex(of: item) {
- return self[safe: index + 1]
- }
- return nil
- }
-
- /// Returns previous element in array before a given one.
- /// - Parameter item: An element to search previous element.
- /// - Returns: Element before passed item. If passed item is first element in array will return nil.
- func previous(item: Element) -> Element? {
- if let index = self.firstIndex(of: item) {
- return self[safe: index - 1]
- }
- return nil
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/CACornerMask+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/CACornerMask+extensions.swift
deleted file mode 100644
index 9d8f9ac..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/CACornerMask+extensions.swift
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// CACornerMask+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import UIKit
-
-public extension CACornerMask {
- /// Contains all corner masks (MaxXMaxY, MinXMaxY, MaxXMinY, MinXMinY).
- static let all: CACornerMask = [
- CACornerMask.layerMaxXMaxYCorner,
- CACornerMask.layerMinXMaxYCorner,
- CACornerMask.layerMaxXMinYCorner,
- CACornerMask.layerMinXMinYCorner
- ]
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/Collection+indexPath.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/Collection+indexPath.swift
deleted file mode 100644
index 9e8ce69..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/Collection+indexPath.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// Collection+indexPath.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import UIKit
-
-public extension Collection where Index == Int, Iterator.Element: Collection, Iterator.Element.Index == Int {
- /// Returns Element in two dimensional array by IndexPath.
- /// - Parameter indexPath: An IndexPath to get Element by section and row.
- /// - Returns: Element by given indexPath.
- subscript(indexPath: IndexPath) -> Iterator.Element.Iterator.Element {
- self[indexPath.section][indexPath.row]
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/NSLayoutConstrain+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/NSLayoutConstrain+extensions.swift
deleted file mode 100644
index acc84b6..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/NSLayoutConstrain+extensions.swift
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// NSLayoutConstrain+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import UIKit
-
-public extension NSLayoutConstraint {
- @discardableResult
- /// Returns self with given priority.
- /// - Parameter priority: UILayoutPriority to set.
- /// - Returns: self.
- func withPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
- self.priority = priority
- return self
- }
-}
-
-public extension Array where Element == NSLayoutConstraint {
- /// Activates each constraint in self
- func activate() {
- NSLayoutConstraint.activate(self)
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UICollectionView+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UICollectionView+extensions.swift
deleted file mode 100644
index f608129..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UICollectionView+extensions.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// UICollectionView+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 05.04.2022.
-//
-
-import UIKit
-
-public extension UICollectionView {
- /// Dequeue's reusable cell for index path with given cell type.
- /// - Important: Will crash if cell type for given reuseIdentifier don't match passed cellType!
- /// - Parameters:
- /// - indexPath: The index path specifying the location of the cell.
- /// - cellType: Type of cell that should be dequeued. Must be ReusableCell.
- /// - Returns: UICollectionViewCell dequeued and cased to cellType.
- func dequeueReusableCell(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: ReusableCell {
- guard let cell = self.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
- fatalError("Failed to dequeue a cell with identifier \(cellType.reuseIdentifier) matching type \(cellType.self).")
- }
-
- return cell
- }
-
- /// Registers a class to use in creating new table cells by cellType.reuseIdentifier.
- /// - Parameter cellType: Cell type that will be registered by cellType.reuseIdentifier
- func register(_ cellType: T.Type) where T: ReusableCell {
- self.register(cellType.self, forCellWithReuseIdentifier: cellType.reuseIdentifier)
- }
-
- /// Registers a nib object that contains a cell with the table view under a specified identifier by cellType.reuseIdentifier.
- /// - Parameter cellType: Cell type, nib of witch will be registered by cellType.reuseIdentifier.
- func register(_ cellType: T.Type) where T: ReusableCell & NibInitializable {
- self.register(cellType.nib, forCellWithReuseIdentifier: cellType.reuseIdentifier)
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UILayoutPriority+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UILayoutPriority+extensions.swift
deleted file mode 100644
index 0df979d..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UILayoutPriority+extensions.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// UILayoutPriority+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import UIKit
-
-public extension UILayoutPriority {
- /// rawValue 999
- static let almostRequired = UILayoutPriority(999)
- /// rawValue 751
- static let prioritizedCompressionResistance = UILayoutPriority(751)
- /// rawValue 251
- static let prioritizedHugging = UILayoutPriority(251)
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UITableView+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UITableView+extensions.swift
deleted file mode 100644
index 2349137..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UITableView+extensions.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// UITableView+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 05.04.2022.
-//
-
-import UIKit
-
-public extension UITableView {
- /// Dequeue's reusable cell for index path with given cell type.
- /// - Important: Will crash if cell type for given reuseIdentifier don't match passed cellType!
- /// - Parameters:
- /// - indexPath: The index path specifying the location of the cell.
- /// - cellType: Type of cell that should be dequeued. Must be ReusableCell.
- /// - Returns: UITableViewCell dequeued and cased to cellType.
- func dequeueReusableCell(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: ReusableCell {
- guard let cell = self.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
- fatalError("Failed to dequeue a cell with identifier \(cellType.reuseIdentifier) matching type \(cellType.self).")
- }
-
- return cell
- }
-
- /// Registers a class to use in creating new table cells by cellType.reuseIdentifier.
- /// - Parameter cellType: Cell type that will be registered by cellType.reuseIdentifier
- func register(_ cellType: T.Type) where T: ReusableCell {
- self.register(cellType.self, forCellReuseIdentifier: cellType.reuseIdentifier)
- }
-
- /// Registers a nib object that contains a cell with the table view under a specified identifier by cellType.reuseIdentifier.
- /// - Parameter cellType: Cell type, nib of witch will be registered by cellType.reuseIdentifier.
- func register(_ cellType: T.Type) where T: ReusableCell & NibInitializable {
- self.register(cellType.nib, forCellReuseIdentifier: cellType.reuseIdentifier)
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UIView+extensions.swift b/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UIView+extensions.swift
deleted file mode 100644
index 89c37a3..0000000
--- a/Sources/Uptech-iOS-Helpers/Extensions/UIKit/UIView+extensions.swift
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// UIView+extensions.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-import UIKit
-
-public extension UIView {
- /// Adds a view as subview the self.
- /// Set's view translatesAutoresizingMaskIntoConstraints to false.
- /// Activates given constrains array.
- /// - Parameters:
- /// - view: View to add as subview and disable Autoresizing Mask.
- /// - constraints: Constrains array to activate after adding subview and disabling Autoresizing Mask.
- func addSubview( _ view: UIView, withConstraints constraints: [NSLayoutConstraint]) {
- addSubview(view)
- view.translatesAutoresizingMaskIntoConstraints = false
- constraints.activate()
- }
-
- /// Adds a view as subview the self.
- /// Adds constrains that fill given view to self's bounds.
- /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
- /// - Parameter view: View to add as subview and fill to self's bounds.
- func addSubviewWithEdgeConstraints(_ view: UIView) {
- addSubview(view, withConstraints: [
- view.topAnchor.constraint(equalTo: topAnchor),
- view.leadingAnchor.constraint(equalTo: leadingAnchor),
- view.trailingAnchor.constraint(equalTo: trailingAnchor),
- view.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
- }
-
- /// Inserts a view to self at given index with given constrains.
- /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
- /// - Parameters:
- /// - view: View to insert as subview and disable Autoresizing Mask.
- /// - index: The index in the array of the subviews property at which to insert the view. Default value .zero
- /// - constraints: Constrains array to activate after adding subview and disabling Autoresizing Mask.
- func insertSubview(_ view: UIView, at index: Int = .zero, withConstraints constraints: [NSLayoutConstraint] ) {
- insertSubview(view, at: index)
- view.translatesAutoresizingMaskIntoConstraints = false
- constraints.activate()
- }
-
- /// Inserts a view as subview the self.
- /// Adds constrains that fill given view to self's bounds.
- /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
- /// - Parameters:
- /// - view: View to insert as subview and fill to self's bounds.
- /// - index: The index in the array of the subviews property at which to insert the view. Default value .zero
- func insertSubviewWithEdgeConstraints(_ view: UIView, at index: Int = .zero) {
- insertSubview(view, at: index, withConstraints: [
- view.topAnchor.constraint(equalTo: topAnchor),
- view.leadingAnchor.constraint(equalTo: leadingAnchor),
- view.trailingAnchor.constraint(equalTo: trailingAnchor),
- view.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
- }
-
- /// Adds corner radius to given corners.
- /// - Parameters:
- /// - radius: Corner radius to add.
- /// - corners: Corners to add corner radius to. Default value .all
- func roundCornersContinuously(radius: CGFloat, corners: CACornerMask = .all) {
- layer.maskedCorners = corners
- layer.cornerRadius = radius
-
- if #available(iOS 13.0, *) {
- layer.cornerCurve = .continuous
- }
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/FoundationHelper/Array+extensions.swift b/Sources/Uptech-iOS-Helpers/FoundationHelper/Array+extensions.swift
new file mode 100644
index 0000000..ebd93af
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/FoundationHelper/Array+extensions.swift
@@ -0,0 +1,67 @@
+//
+// Array+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+public extension Array {
+ /// Returns the element at the specified index if it is within bounds, otherwise nil.
+ subscript(safe index: Index) -> Element? {
+ indices.contains(index) ? self[index] : nil
+ }
+
+ /// Returns the elements at the specified range if it is within bounds, otherwise empty array.
+ subscript(safe range: Range) -> [Element] {
+ guard
+ range.startIndex >= startIndex,
+ range.endIndex <= endIndex else {
+ return []
+ }
+
+ return Array(self[range])
+ }
+
+ /// Returns the elements at the specified closed range if it is within bounds, otherwise empty array.
+ subscript(safe range: ClosedRange) -> [Element] {
+ guard
+ range.lowerBound >= 0,
+ endIndex > 0,
+ range.upperBound <= endIndex else {
+ return []
+ }
+
+ let maxIndex = endIndex - 1
+ let upperBound = Swift.min(range.upperBound, maxIndex)
+ return Array(self[range.lowerBound...upperBound])
+ }
+}
+
+public extension Array where Element: Equatable {
+ /// Removes first element in array.
+ /// - Parameter item: An element to remove.
+ mutating func remove(_ item: Element) {
+ guard let index = firstIndex(of: item) else { return }
+ remove(at: index)
+ }
+
+ /// Returns next element in array after a given one.
+ /// - Parameter item: An element to search next element.
+ /// - Returns: Element after passed item. If passed item is last element in array will return nil.
+ func next(item: Element) -> Element? {
+ if let index = self.firstIndex(of: item) {
+ return self[safe: index + 1]
+ }
+ return nil
+ }
+
+ /// Returns previous element in array before a given one.
+ /// - Parameter item: An element to search previous element.
+ /// - Returns: Element before passed item. If passed item is first element in array will return nil.
+ func previous(item: Element) -> Element? {
+ if let index = self.firstIndex(of: item) {
+ return self[safe: index - 1]
+ }
+ return nil
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/Protocols/NibInitializable.swift b/Sources/Uptech-iOS-Helpers/Protocols/NibInitializable.swift
deleted file mode 100644
index bef868c..0000000
--- a/Sources/Uptech-iOS-Helpers/Protocols/NibInitializable.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// NibInitializable.swift
-//
-//
-// Created by Sergey Kletsov on 28.03.2022.
-//
-
-import Foundation
-import UIKit
-
-/// Protocol that helps to initialize view with xib.
-/// All properties and method have default implementation.
-public protocol NibInitializable {
- /// Default implementation returns class name.
- static var nibName: String { get }
- /// Default implementation returns nib with self.nibName in default bundle.
- static var nib: UINib { get }
- /// Returns view initiated from xib
- /// - Returns: Default implementation returns first view in self.nib casted to Self.
- static func initFromNib() -> Self
-}
-
-public extension NibInitializable where Self: UIView {
- static var nibName: String {
- String(describing: Self.self)
- }
-
- static var nib: UINib {
- UINib(nibName: nibName, bundle: nil)
- }
-
- static func initFromNib() -> Self {
- guard let view = nib.instantiate(withOwner: nil, options: nil).first as? Self else {
- fatalError("Could not instantiate view from nib with name \(nibName).")
- }
-
- return view
- }
-}
diff --git a/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+GeometryReader.swift b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+GeometryReader.swift
new file mode 100644
index 0000000..921f9f4
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+GeometryReader.swift
@@ -0,0 +1,79 @@
+//
+// View+GeometryReader.swift
+//
+//
+// Created by Sergey Kletsov on 02.08.2024.
+//
+
+import SwiftUI
+
+private struct SizePreferenceKey: PreferenceKey {
+ static var defaultValue: CGSize = .zero
+
+ static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
+}
+
+private struct GlobalFramePreferenceKey: PreferenceKey {
+ static var defaultValue: CGRect = .zero
+
+ static func reduce(value: inout CGRect, nextValue: () -> CGRect) {}
+}
+
+public extension View {
+ /// Modifier to read view size. Uses GeometryReader
+ /// - Parameter onChange: A closure to run when size changes
+ /// - Returns: A view that calls onChange closure when the view size changes.
+ func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
+ background(
+ GeometryReader { geometryProxy in
+ Color.clear
+ .preference(key: SizePreferenceKey.self, value: geometryProxy.size)
+ }
+ )
+ .onPreferenceChange(SizePreferenceKey.self, perform: onChange)
+ }
+
+ /// Modifier to read view frame. Uses GeometryReader
+ /// - Parameters:
+ /// - coordinateSpace: Coordinate space to get frame
+ /// - onChange: A closure to run when frame changes
+ /// - Returns: A view that calls onChange closure when the view frame changes in given coordinate space.
+ func readFrame(coordinateSpace: CoordinateSpace, onChange: @escaping (CGRect) -> Void) -> some View {
+ background(
+ GeometryReader { geometryProxy in
+ Color.clear
+ .preference(key: GlobalFramePreferenceKey.self, value: geometryProxy.frame(in: coordinateSpace))
+ }
+ )
+ .onPreferenceChange(GlobalFramePreferenceKey.self, perform: onChange)
+ }
+
+ /// Modifier to read view frame in global coordinate space. Uses GeometryReader
+ /// - onChange: A closure to run when frame changes
+ /// - Returns: A view that calls onChange closure when the view frame changes in global coordinate space.
+ func readGlobalFrame(onChange: @escaping (CGRect) -> Void) -> some View {
+ readFrame(coordinateSpace: .global, onChange: onChange)
+ }
+
+ /// Modifier to read view origin in global coordinate space. Uses GeometryReader
+ /// - onChange: A closure to run when frame's origin changes
+ /// - Returns: A view that calls onChange closure when the view frame's origin changes in global coordinate space.
+ func readOffset(onChange: @escaping (CGPoint) -> Void) -> some View {
+ readFrame(coordinateSpace: .global) { frame in
+ onChange(frame.origin)
+ }
+ }
+
+ /// Modifier to read view safeAreaInsets. Embeds view inside GeometryReader
+ /// - Parameter closure: A closure to run when view safeAreaInsets are configured (after view appeared)
+ /// - Returns: GeometryReader that contains view and calls closure after view is appeared with calculated safeAreaInsets
+ func readInsets(_ closure: @escaping (EdgeInsets) -> Void) -> some View {
+ GeometryReader { geometryProxy in
+ self.onAppear {
+ DispatchQueue.main.async {
+ closure(geometryProxy.safeAreaInsets)
+ }
+ }
+ }
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+conditionalModifier.swift b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+conditionalModifier.swift
new file mode 100644
index 0000000..60914b7
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+conditionalModifier.swift
@@ -0,0 +1,65 @@
+//
+// View+conditionalModifier.swift
+//
+//
+// Created by Sergey Kletsov on 02.08.2024.
+//
+
+import SwiftUI
+
+public extension View {
+ typealias ContentTransform = (Self) -> Content
+
+ /// Conditional Modifier method that modifies view depending of input Bool
+ /// - Parameters:
+ /// - condition: Bool condition to check
+ /// - ifTrue: Modifier closure that will be applied if condition is true
+ /// - ifFalse: Modifier closure that will be applied if condition is false
+ /// - Returns: Self modified by ifTrue or ifFalse closure
+ @ViewBuilder
+ func conditionalModifier(
+ _ condition: @autoclosure () -> Bool,
+ ifTrue: ContentTransform,
+ else ifFalse: ContentTransform
+ ) -> some View {
+ if condition() {
+ ifTrue(self)
+ } else {
+ ifFalse(self)
+ }
+ }
+
+ /// Conditional Modifier method that modifies view depending of input Bool
+ /// - Parameters:
+ /// - condition: Bool condition to check
+ /// - ifTrue: Modifier closure that will be applied if condition is true
+ /// - ifFalse: Modifier closure that will be applied if condition is false
+ /// - Returns: Self modified by ifTrue or ifFalse closure
+
+ /// Conditional Modifier method that modifies view depending of input Bool
+ /// - Parameters:
+ /// - condition: Bool condition to check
+ /// - transform: Modifier closure that will be applied if condition is true
+ /// - Returns: Self modified by ifTrue closure if condition is true, Self otherwise
+ @ViewBuilder
+ func `if`(
+ _ condition: @autoclosure () -> Bool,
+ transform: ContentTransform
+ ) -> some View {
+ conditionalModifier(
+ condition(),
+ ifTrue: transform,
+ else: { $0 }
+ )
+ }
+
+ /// Modifier method that modifies view
+ /// - Parameter transform: Modifier closure to apply
+ /// - Returns: Self modified by transform closure
+ @ViewBuilder
+ func modify(
+ @ViewBuilder transform: ContentTransform
+ ) -> some View {
+ transform(self)
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+customOnChange.swift b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+customOnChange.swift
new file mode 100644
index 0000000..1f94722
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Extensions/View+customOnChange.swift
@@ -0,0 +1,31 @@
+//
+// View+customOnChange.swift
+//
+//
+// Created by Sergey Kletsov on 02.08.2024.
+//
+
+import SwiftUI
+
+public extension View {
+ /// Adds a modifier for this view that fires an action when a specific value changes.
+ /// Calls different methods depending of iOS version (new system onChange modifier was introduced in iOS 17)
+ /// - Parameters:
+ /// - value: The value to check against when determining whether to run the closure.
+ /// - initial: Whether the action should be run when this view initially appears. (will be used only on iOS 17)
+ /// - action: A closure to run when the value changes. Will use newValue on iOS 17
+ /// - Returns: A view that fires an action when the specified value changes.
+ @available(iOS 14.0, *)
+ @ViewBuilder
+ func customOnChange(
+ of value: V,
+ initial: Bool = false,
+ _ action: @escaping (V) -> Void
+ ) -> some View where V: Equatable {
+ if #available(iOS 17.0, *) {
+ self.onChange(of: value, initial: initial, { _, newValue in action(newValue) })
+ } else {
+ self.onChange(of: value, perform: action)
+ }
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalHStack.swift b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalHStack.swift
new file mode 100644
index 0000000..033b9ea
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalHStack.swift
@@ -0,0 +1,56 @@
+//
+// ProportionalHStack.swift
+//
+//
+// Created by Sergey Kletsov on 07.08.2024.
+//
+
+import SwiftUI
+
+/// HStack layout that layouts subviews by given proportions
+@available(iOS 16.0, *)
+struct ProportionalHStack: Layout {
+ let proportions: [CGFloat]
+ let spacing: CGFloat
+
+ /// Creates new layout with given proportions and spacing
+ /// - Parameters:
+ /// - proportions: Array of proportions. Numbers in array should add up to **1.0**. Number of proportions should be equal to number of subviews.
+ /// - spacing: Spacing between subviews. Default value is 0
+ init(proportions: [CGFloat], spacing: CGFloat = 0) {
+ self.proportions = proportions
+ self.spacing = spacing
+ }
+
+ func sizeThatFits(
+ proposal: ProposedViewSize,
+ subviews: Subviews,
+ cache: inout Void
+ ) -> CGSize {
+ proposal.replacingUnspecifiedDimensions()
+ }
+
+ func placeSubviews(
+ in bounds: CGRect,
+ proposal: ProposedViewSize,
+ subviews: Subviews,
+ cache: inout Void
+ ) {
+ guard subviews.count == proportions.count else {
+ return
+ }
+
+ let totalWidth = bounds.width - (spacing * CGFloat(subviews.count - 1))
+ var xOffset: CGFloat = bounds.minX
+
+ for (index, subview) in subviews.enumerated() {
+ let width = totalWidth * proportions[index]
+ subview.place(
+ at: CGPoint(x: xOffset, y: bounds.midY),
+ anchor: .leading,
+ proposal: ProposedViewSize(width: width, height: bounds.height)
+ )
+ xOffset += width + spacing
+ }
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalVStack.swift b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalVStack.swift
new file mode 100644
index 0000000..de9a991
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/SwiftUIHelper/Layouts/ProportionalVStack.swift
@@ -0,0 +1,56 @@
+//
+// ProportionalVStack.swift
+//
+//
+// Created by Sergey Kletsov on 07.08.2024.
+//
+
+import SwiftUI
+
+/// VStack layout that layouts subviews by given proportions
+@available(iOS 16.0, *)
+public struct ProportionalVStack: Layout {
+ private let proportions: [CGFloat]
+ private let spacing: CGFloat
+
+ /// Creates new layout with given proportions and spacing
+ /// - Parameters:
+ /// - proportions: Array of proportions. Numbers in array should add up to **1.0**. Number of proportions should be equal to number of subviews.
+ /// - spacing: Spacing between subviews. Default value is 0
+ init(proportions: [CGFloat], spacing: CGFloat = 0) {
+ self.proportions = proportions
+ self.spacing = spacing
+ }
+
+ public func sizeThatFits(
+ proposal: ProposedViewSize,
+ subviews: Subviews,
+ cache: inout Void
+ ) -> CGSize {
+ proposal.replacingUnspecifiedDimensions()
+ }
+
+ public func placeSubviews(
+ in bounds: CGRect,
+ proposal: ProposedViewSize,
+ subviews: Subviews,
+ cache: inout Void
+ ) {
+ guard subviews.count == proportions.count else {
+ return
+ }
+
+ let totalHeight = bounds.height - (spacing * CGFloat(subviews.count - 1))
+ var yOffset: CGFloat = bounds.minY
+
+ for (index, subview) in subviews.enumerated() {
+ let height = totalHeight * proportions[index]
+ subview.place(
+ at: CGPoint(x: bounds.midX, y: yOffset),
+ anchor: .top,
+ proposal: ProposedViewSize(width: bounds.width, height: height)
+ )
+ yOffset += height + spacing
+ }
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/CACornerMask+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/CACornerMask+extensions.swift
new file mode 100644
index 0000000..70075b3
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/CACornerMask+extensions.swift
@@ -0,0 +1,18 @@
+//
+// CACornerMask+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+import UIKit
+
+public extension CACornerMask {
+ /// Contains all corner masks (MaxXMaxY, MinXMaxY, MaxXMinY, MinXMinY).
+ static let all: CACornerMask = [
+ CACornerMask.layerMaxXMaxYCorner,
+ CACornerMask.layerMinXMaxYCorner,
+ CACornerMask.layerMaxXMinYCorner,
+ CACornerMask.layerMinXMinYCorner
+ ]
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/Collection+indexPath.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/Collection+indexPath.swift
new file mode 100644
index 0000000..f3c83a0
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/Collection+indexPath.swift
@@ -0,0 +1,17 @@
+//
+// Collection+indexPath.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+import UIKit
+
+public extension Collection where Index == Int, Iterator.Element: Collection, Iterator.Element.Index == Int {
+ /// Returns Element in two dimensional array by IndexPath.
+ /// - Parameter indexPath: An IndexPath to get Element by section and row.
+ /// - Returns: Element by given indexPath.
+ subscript(indexPath: IndexPath) -> Iterator.Element.Iterator.Element {
+ self[indexPath.section][indexPath.row]
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/NSLayoutConstrain+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/NSLayoutConstrain+extensions.swift
new file mode 100644
index 0000000..2d6a459
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/NSLayoutConstrain+extensions.swift
@@ -0,0 +1,26 @@
+//
+// NSLayoutConstrain+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+import UIKit
+
+public extension NSLayoutConstraint {
+ @discardableResult
+ /// Returns self with given priority.
+ /// - Parameter priority: UILayoutPriority to set.
+ /// - Returns: self.
+ func withPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
+ self.priority = priority
+ return self
+ }
+}
+
+public extension Array where Element == NSLayoutConstraint {
+ /// Activates each constraint in self
+ func activate() {
+ NSLayoutConstraint.activate(self)
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/NibInitializable.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/NibInitializable.swift
new file mode 100644
index 0000000..a9ee774
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/NibInitializable.swift
@@ -0,0 +1,38 @@
+//
+// NibInitializable.swift
+//
+//
+// Created by Sergey Kletsov on 28.03.2022.
+//
+
+import UIKit
+
+/// Protocol that helps to initialize view with xib.
+/// All properties and method have default implementation.
+public protocol NibInitializable {
+ /// Default implementation returns class name.
+ static var nibName: String { get }
+ /// Default implementation returns nib with self.nibName in default bundle.
+ static var nib: UINib { get }
+ /// Returns view initiated from xib
+ /// - Returns: Default implementation returns first view in self.nib casted to Self.
+ static func initFromNib() -> Self
+}
+
+public extension NibInitializable where Self: UIView {
+ static var nibName: String {
+ String(describing: Self.self)
+ }
+
+ static var nib: UINib {
+ UINib(nibName: nibName, bundle: nil)
+ }
+
+ static func initFromNib() -> Self {
+ guard let view = nib.instantiate(withOwner: nil, options: nil).first as? Self else {
+ fatalError("Could not instantiate view from nib with name \(nibName).")
+ }
+
+ return view
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/Protocols/ReusableCell.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/ReusableCell.swift
similarity index 66%
rename from Sources/Uptech-iOS-Helpers/Protocols/ReusableCell.swift
rename to Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/ReusableCell.swift
index 0574479..eb32886 100644
--- a/Sources/Uptech-iOS-Helpers/Protocols/ReusableCell.swift
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/Protocols/ReusableCell.swift
@@ -1,6 +1,6 @@
//
// ReusableCell.swift
-//
+//
//
// Created by Sergey Kletsov on 28.03.2022.
//
@@ -10,12 +10,12 @@ import UIKit
/// Protocol that helps with reusable cells. If used on UITableViewCell subclasses provides handy methods for registering and dequeueing cells in UITableView's
/// Has default implementation for reuseIdentifier.
public protocol ReusableCell {
- /// Default implementation return's class name
- static var reuseIdentifier: String { get }
+ /// Default implementation return's class name
+ static var reuseIdentifier: String { get }
}
public extension ReusableCell {
- static var reuseIdentifier: String {
- String(describing: Self.self)
- }
+ static var reuseIdentifier: String {
+ String(describing: Self.self)
+ }
}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/UICollectionView+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/UICollectionView+extensions.swift
new file mode 100644
index 0000000..a406063
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/UICollectionView+extensions.swift
@@ -0,0 +1,36 @@
+//
+// UICollectionView+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 05.04.2022.
+//
+
+import UIKit
+
+public extension UICollectionView {
+ /// Dequeue's reusable cell for index path with given cell type.
+ /// - Important: Will crash if cell type for given reuseIdentifier don't match passed cellType!
+ /// - Parameters:
+ /// - indexPath: The index path specifying the location of the cell.
+ /// - cellType: Type of cell that should be dequeued. Must be ReusableCell.
+ /// - Returns: UICollectionViewCell dequeued and cased to cellType.
+ func dequeueReusableCell(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: ReusableCell {
+ guard let cell = self.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
+ fatalError("Failed to dequeue a cell with identifier \(cellType.reuseIdentifier) matching type \(cellType.self).")
+ }
+
+ return cell
+ }
+
+ /// Registers a class to use in creating new table cells by cellType.reuseIdentifier.
+ /// - Parameter cellType: Cell type that will be registered by cellType.reuseIdentifier
+ func register(_ cellType: T.Type) where T: ReusableCell {
+ self.register(cellType.self, forCellWithReuseIdentifier: cellType.reuseIdentifier)
+ }
+
+ /// Registers a nib object that contains a cell with the table view under a specified identifier by cellType.reuseIdentifier.
+ /// - Parameter cellType: Cell type, nib of witch will be registered by cellType.reuseIdentifier.
+ func register(_ cellType: T.Type) where T: ReusableCell & NibInitializable {
+ self.register(cellType.nib, forCellWithReuseIdentifier: cellType.reuseIdentifier)
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/UILayoutPriority+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/UILayoutPriority+extensions.swift
new file mode 100644
index 0000000..e40ca7a
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/UILayoutPriority+extensions.swift
@@ -0,0 +1,17 @@
+//
+// UILayoutPriority+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+import UIKit
+
+public extension UILayoutPriority {
+ /// rawValue 999
+ static let almostRequired = UILayoutPriority(999)
+ /// rawValue 751
+ static let prioritizedCompressionResistance = UILayoutPriority(751)
+ /// rawValue 251
+ static let prioritizedHugging = UILayoutPriority(251)
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/UITableView+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/UITableView+extensions.swift
new file mode 100644
index 0000000..8d44675
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/UITableView+extensions.swift
@@ -0,0 +1,36 @@
+//
+// UITableView+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 05.04.2022.
+//
+
+import UIKit
+
+public extension UITableView {
+ /// Dequeue's reusable cell for index path with given cell type.
+ /// - Important: Will crash if cell type for given reuseIdentifier don't match passed cellType!
+ /// - Parameters:
+ /// - indexPath: The index path specifying the location of the cell.
+ /// - cellType: Type of cell that should be dequeued. Must be ReusableCell.
+ /// - Returns: UITableViewCell dequeued and cased to cellType.
+ func dequeueReusableCell(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: ReusableCell {
+ guard let cell = self.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
+ fatalError("Failed to dequeue a cell with identifier \(cellType.reuseIdentifier) matching type \(cellType.self).")
+ }
+
+ return cell
+ }
+
+ /// Registers a class to use in creating new table cells by cellType.reuseIdentifier.
+ /// - Parameter cellType: Cell type that will be registered by cellType.reuseIdentifier
+ func register(_ cellType: T.Type) where T: ReusableCell {
+ self.register(cellType.self, forCellReuseIdentifier: cellType.reuseIdentifier)
+ }
+
+ /// Registers a nib object that contains a cell with the table view under a specified identifier by cellType.reuseIdentifier.
+ /// - Parameter cellType: Cell type, nib of witch will be registered by cellType.reuseIdentifier.
+ func register(_ cellType: T.Type) where T: ReusableCell & NibInitializable {
+ self.register(cellType.nib, forCellReuseIdentifier: cellType.reuseIdentifier)
+ }
+}
diff --git a/Sources/Uptech-iOS-Helpers/UIKitHelper/UIView+extensions.swift b/Sources/Uptech-iOS-Helpers/UIKitHelper/UIView+extensions.swift
new file mode 100644
index 0000000..c0cac35
--- /dev/null
+++ b/Sources/Uptech-iOS-Helpers/UIKitHelper/UIView+extensions.swift
@@ -0,0 +1,74 @@
+//
+// UIView+extensions.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+import UIKit
+
+public extension UIView {
+ /// Adds a view as subview the self.
+ /// Set's view translatesAutoresizingMaskIntoConstraints to false.
+ /// Activates given constrains array.
+ /// - Parameters:
+ /// - view: View to add as subview and disable Autoresizing Mask.
+ /// - constraints: Constrains array to activate after adding subview and disabling Autoresizing Mask.
+ func addSubview( _ view: UIView, withConstraints constraints: [NSLayoutConstraint]) {
+ addSubview(view)
+ view.translatesAutoresizingMaskIntoConstraints = false
+ constraints.activate()
+ }
+
+ /// Adds a view as subview the self.
+ /// Adds constrains that fill given view to self's bounds.
+ /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
+ /// - Parameter view: View to add as subview and fill to self's bounds.
+ func addSubviewWithEdgeConstraints(_ view: UIView) {
+ addSubview(view, withConstraints: [
+ view.topAnchor.constraint(equalTo: topAnchor),
+ view.leadingAnchor.constraint(equalTo: leadingAnchor),
+ view.trailingAnchor.constraint(equalTo: trailingAnchor),
+ view.bottomAnchor.constraint(equalTo: bottomAnchor),
+ ])
+ }
+
+ /// Inserts a view to self at given index with given constrains.
+ /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
+ /// - Parameters:
+ /// - view: View to insert as subview and disable Autoresizing Mask.
+ /// - index: The index in the array of the subviews property at which to insert the view. Default value .zero
+ /// - constraints: Constrains array to activate after adding subview and disabling Autoresizing Mask.
+ func insertSubview(_ view: UIView, at index: Int = .zero, withConstraints constraints: [NSLayoutConstraint] ) {
+ insertSubview(view, at: index)
+ view.translatesAutoresizingMaskIntoConstraints = false
+ constraints.activate()
+ }
+
+ /// Inserts a view as subview the self.
+ /// Adds constrains that fill given view to self's bounds.
+ /// - Also will set given view's translatesAutoresizingMaskIntoConstraints to false.
+ /// - Parameters:
+ /// - view: View to insert as subview and fill to self's bounds.
+ /// - index: The index in the array of the subviews property at which to insert the view. Default value .zero
+ func insertSubviewWithEdgeConstraints(_ view: UIView, at index: Int = .zero) {
+ insertSubview(view, at: index, withConstraints: [
+ view.topAnchor.constraint(equalTo: topAnchor),
+ view.leadingAnchor.constraint(equalTo: leadingAnchor),
+ view.trailingAnchor.constraint(equalTo: trailingAnchor),
+ view.bottomAnchor.constraint(equalTo: bottomAnchor),
+ ])
+ }
+
+ /// Adds corner radius to given corners.
+ /// - Parameters:
+ /// - radius: Corner radius to add.
+ /// - corners: Corners to add corner radius to. Default value .all
+ func roundCornersContinuously(radius: CGFloat, corners: CACornerMask = .all) {
+ layer.maskedCorners = corners
+ layer.cornerRadius = radius
+
+ if #available(iOS 13.0, *) {
+ layer.cornerCurve = .continuous
+ }
+ }
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/Foundation/Array+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/Foundation/Array+extensionsTests.swift
deleted file mode 100644
index a3bd358..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/Foundation/Array+extensionsTests.swift
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-// Array+extensionsTests.swift
-//
-//
-// Created by Sergey Kletsov on 25.03.2022.
-//
-
-import XCTest
-@testable import Uptech_iOS_Helpers
-
-final class ArrayExtensionsTests: XCTestCase {
- private let sut = [0, 1, 2, 3, 4]
-
- func testSafeElementReturnsElement() throws {
- let result = sut[safe: 2]
- let expectedResult = 2
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeElementReturnsNil() throws {
- let result = sut[safe: 10]
- XCTAssertNil(result, "Result should be nil")
- }
-
- func testSafeRangeReturnsElements() throws {
- let result = sut[safe: 1..<3]
- let expectedResult = [1, 2]
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeNegativeRangeReturnsEmpty() throws {
- let result = sut[safe: -3..<(-1)]
- let expectedResult: [Int] = []
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeRangeReturnsEmpty() throws {
- let result = sut[safe: 7..<10]
- let expectedResult: [Int] = []
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeClosedRangeReturnsElements() throws {
- let result = sut[safe: 1...3]
- let expectedResult = [1, 2, 3]
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeNegativeClosedRangeReturnsEmpty() throws {
- let result = sut[safe: -3...(-1)]
- let expectedResult: [Int] = []
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testSafeClosedRangeReturnsEmpty() throws {
- let result = sut[safe: 7...10]
- let expectedResult: [Int] = []
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testRemoveExistingElement() throws {
- var sut = self.sut
- sut.remove(1)
- let expectedResult = [0, 2, 3, 4]
- XCTAssertEqual(sut, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testRemoveNonExistingElement() throws {
- var sut = self.sut
- sut.remove(5)
- let expectedResult = self.sut
- XCTAssertEqual(sut, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testNextElementExist() throws {
- let result = sut.next(item: 2)
- let expectedResult = 3
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testNextElementNil() throws {
- let result = sut.next(item: 4)
- XCTAssertNil(result, "Result should be nil")
- }
-
- func testPreviousElementExist() throws {
- let result = sut.previous(item: 2)
- let expectedResult = 1
- XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
- }
-
- func testPreviousElementNil() throws {
- let result = sut.previous(item: 0)
- XCTAssertNil(result, "Result should be nil")
- }
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/FoundationHelper/Array+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/FoundationHelper/Array+extensionsTests.swift
new file mode 100644
index 0000000..642ed5e
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/FoundationHelper/Array+extensionsTests.swift
@@ -0,0 +1,96 @@
+//
+// Array+extensionsTests.swift
+//
+//
+// Created by Sergey Kletsov on 25.03.2022.
+//
+
+import XCTest
+@testable import UptechFoundationHelper
+
+final class ArrayExtensionsTests: XCTestCase {
+ private let sut = [0, 1, 2, 3, 4]
+
+ func testSafeElementReturnsElement() throws {
+ let result = sut[safe: 2]
+ let expectedResult = 2
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeElementReturnsNil() throws {
+ let result = sut[safe: 10]
+ XCTAssertNil(result, "Result should be nil")
+ }
+
+ func testSafeRangeReturnsElements() throws {
+ let result = sut[safe: 1..<3]
+ let expectedResult = [1, 2]
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeNegativeRangeReturnsEmpty() throws {
+ let result = sut[safe: -3..<(-1)]
+ let expectedResult: [Int] = []
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeRangeReturnsEmpty() throws {
+ let result = sut[safe: 7..<10]
+ let expectedResult: [Int] = []
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeClosedRangeReturnsElements() throws {
+ let result = sut[safe: 1...3]
+ let expectedResult = [1, 2, 3]
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeNegativeClosedRangeReturnsEmpty() throws {
+ let result = sut[safe: -3...(-1)]
+ let expectedResult: [Int] = []
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testSafeClosedRangeReturnsEmpty() throws {
+ let result = sut[safe: 7...10]
+ let expectedResult: [Int] = []
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testRemoveExistingElement() throws {
+ var sut = self.sut
+ sut.remove(1)
+ let expectedResult = [0, 2, 3, 4]
+ XCTAssertEqual(sut, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testRemoveNonExistingElement() throws {
+ var sut = self.sut
+ sut.remove(5)
+ let expectedResult = self.sut
+ XCTAssertEqual(sut, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testNextElementExist() throws {
+ let result = sut.next(item: 2)
+ let expectedResult = 3
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testNextElementNil() throws {
+ let result = sut.next(item: 4)
+ XCTAssertNil(result, "Result should be nil")
+ }
+
+ func testPreviousElementExist() throws {
+ let result = sut.previous(item: 2)
+ let expectedResult = 1
+ XCTAssertEqual(result, expectedResult, "Result should be \(expectedResult)")
+ }
+
+ func testPreviousElementNil() throws {
+ let result = sut.previous(item: 0)
+ XCTAssertNil(result, "Result should be nil")
+ }
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKit/Collection+indexPathTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKit/Collection+indexPathTests.swift
deleted file mode 100644
index 8ae87da..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/UIKit/Collection+indexPathTests.swift
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// Collection+indexPathTests.swift
-//
-//
-// Created by Sergey Kletsov on 29.03.2022.
-//
-
-import XCTest
-@testable import Uptech_iOS_Helpers
-
-final class CollectionIndexPathTests: XCTestCase {
- private let sut = [[0, 1, 2],
- [3, 4, 5],
- [6, 7, 8]]
-
- func testIndexPathSubscriptFirstItem() {
- let result = sut[IndexPath(item: 0, section: 0)]
- let expectedResult = 0
-
- XCTAssertEqual(result, expectedResult)
- }
-
- func testIndexPathSubscriptMiddleItem() {
- let result = sut[IndexPath(row: 1, section: 1)]
- let expectedResult = 4
-
- XCTAssertEqual(result, expectedResult)
- }
-
- func testIndexPathSubscriptLastItem() {
- let result = sut[IndexPath(item: 2, section: 2)]
- let expectedResult = 8
-
- XCTAssertEqual(result, expectedResult)
- }
-
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKit/NSLayoutConstraint+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKit/NSLayoutConstraint+extensionsTests.swift
deleted file mode 100644
index 9117f22..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/UIKit/NSLayoutConstraint+extensionsTests.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// NSLayoutConstraint+extensionsTests.swift
-//
-//
-// Created by Sergey Kletsov on 29.03.2022.
-//
-
-import XCTest
-@testable import Uptech_iOS_Helpers
-
-final class NSLayoutConstraintExtensionsTests: XCTestCase {
-
- func testWithPriority() throws {
- let sut = NSLayoutConstraint()
- let priority = UILayoutPriority.almostRequired
- let result = sut.withPriority(priority)
- XCTAssertEqual(result.priority, priority, "sut's priority should be \(priority)")
- }
-
- func testActivateExtension() throws {
- let rootView = UIView()
- let subview = UIView()
-
- subview.translatesAutoresizingMaskIntoConstraints = false
- rootView.addSubview(subview)
-
- let sut = [subview.heightAnchor.constraint(equalToConstant: 100),
- subview.widthAnchor.constraint(equalTo: rootView.widthAnchor),
- subview.centerYAnchor.constraint(equalTo: rootView.centerYAnchor),
- subview.centerXAnchor.constraint(equalTo: rootView.centerXAnchor)]
-
- sut.activate()
- XCTAssertTrue(sut.allSatisfy(\.isActive), "all constrains in sut array should be activated")
- }
-
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UICollectionView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKit/UICollectionView+extensionsTests.swift
deleted file mode 100644
index 0448e72..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UICollectionView+extensionsTests.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// UICollectionView+extensionsTests.swift
-//
-//
-// Created by Sergey Kletsov on 05.04.2022.
-//
-
-import XCTest
-import UIKit
-@testable import Uptech_iOS_Helpers
-
-final class UICollectionViewExtensionsTests: XCTestCase {
- private class TestCell: UICollectionViewCell, ReusableCell { }
- private let cellType = TestCell.self
-
- func testRegisterCorrectType() throws {
- let collectionView = createCollectionView()
- collectionView.register(cellType)
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: .init(item: 0, section: 0))
- let isCellRightType = cell.isKind(of: cellType)
- XCTAssertTrue(isCellRightType, "dequeued cell must be \(cellType)")
- }
-
- func testDequeueNotCrashing() {
- let collectionView = createCollectionView()
- collectionView.register(cellType, forCellWithReuseIdentifier: cellType.reuseIdentifier)
- let _ = collectionView.dequeueReusableCell(for: .init(item: 0, section: 0), cellType: cellType)
- }
-
- private func createCollectionView() -> UICollectionView {
- UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
- }
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UITableView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKit/UITableView+extensionsTests.swift
deleted file mode 100644
index 0517f81..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UITableView+extensionsTests.swift
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// UITableView+extensionsTests.swift
-//
-//
-// Created by Sergey Kletsov on 05.04.2022.
-//
-
-import XCTest
-import UIKit
-@testable import Uptech_iOS_Helpers
-
-final class UITableViewExtensionsTests: XCTestCase {
- private class TestCell: UITableViewCell, ReusableCell { }
- private let cellType = TestCell.self
-
- func testRegisterCorrectType() throws {
- let tableView = UITableView()
- tableView.register(cellType)
- let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: .init(item: 0, section: 0))
- let isCellRightType = cell.isKind(of: cellType)
- XCTAssertTrue(isCellRightType, "dequeued cell must be \(cellType)")
- }
-
- func testDequeueNotCrashing() {
- let tableView = UITableView()
- tableView.register(cellType, forCellReuseIdentifier: cellType.reuseIdentifier)
- let _ = tableView.dequeueReusableCell(for: .init(item: 0, section: 0), cellType: cellType)
- }
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UIView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKit/UIView+extensionsTests.swift
deleted file mode 100644
index 38de2a5..0000000
--- a/Tests/Uptech-iOS-Helpers-Tests/UIKit/UIView+extensionsTests.swift
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// UIView+extensionsTests.swift
-//
-//
-// Created by Sergey Kletsov on 29.03.2022.
-//
-
-import XCTest
-@testable import Uptech_iOS_Helpers
-
-final class UIViewExtensionsTests: XCTestCase {
-
- func testSubviewAdding() throws {
- let view = UIView()
- let sut = UIView()
- let constrains = [sut.heightAnchor.constraint(equalToConstant: 10),
- sut.widthAnchor.constraint(equalTo: view.widthAnchor),
- sut.centerYAnchor.constraint(equalTo: view.centerYAnchor)]
-
- view.addSubview(sut, withConstraints: constrains)
-
- XCTAssertTrue(view.subviews.contains(sut), "view should contain sut")
- XCTAssertFalse(sut.translatesAutoresizingMaskIntoConstraints, "sut's translatesAutoresizingMaskIntoConstraints should be false")
- XCTAssertTrue(constrains.allSatisfy(\.isActive), "all constrains should be active")
- }
-
- func testSubviewWithEdgeConstraintsAdding() throws {
- let frame = CGRect(origin: .zero, size: .init(width: 10, height: 10))
- let view = UIView(frame: frame)
- let sut = UIView()
-
- view.addSubviewWithEdgeConstraints(sut)
- sut.layoutIfNeeded()
-
- XCTAssertEqual(frame, sut.bounds, "sut's bounds should be equal to view's bounds")
- }
-
- func testSubviewInserting() throws {
- let view = UIView()
- let subview1 = UIView()
- let subview2 = UIView()
- view.addSubview(subview1)
- view.addSubview(subview2)
-
- let sut = UIView()
- let constrains = [sut.heightAnchor.constraint(equalToConstant: 10),
- sut.widthAnchor.constraint(equalTo: view.widthAnchor),
- sut.centerYAnchor.constraint(equalTo: view.centerYAnchor)]
- let index = 1
- view.insertSubview(sut, at: index, withConstraints: constrains)
-
- XCTAssertEqual(view.subviews.firstIndex(of: sut), index, "sut's index should be \(index)")
- XCTAssertFalse(sut.translatesAutoresizingMaskIntoConstraints, "sut's translatesAutoresizingMaskIntoConstraints should be false")
- XCTAssertTrue(constrains.allSatisfy(\.isActive), "all constrains should be active")
- }
-
- func testSubviewWithEdgeConstraintsInserting() throws {
- let frame = CGRect(origin: .zero, size: .init(width: 10, height: 10))
- let view = UIView(frame: frame)
- let subview1 = UIView()
- let subview2 = UIView()
- view.addSubview(subview1)
- view.addSubview(subview2)
-
- let sut = UIView()
- let index = 1
- view.insertSubviewWithEdgeConstraints(sut, at: index)
- sut.layoutIfNeeded()
-
- XCTAssertEqual(view.subviews.firstIndex(of: sut), index, "sut's index should be \(index)")
- XCTAssertEqual(frame, sut.bounds, "sut's bounds should be equal to view's bounds")
- }
-
-}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/Collection+indexPathTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/Collection+indexPathTests.swift
new file mode 100644
index 0000000..ac051ca
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/Collection+indexPathTests.swift
@@ -0,0 +1,37 @@
+//
+// Collection+indexPathTests.swift
+//
+//
+// Created by Sergey Kletsov on 29.03.2022.
+//
+
+import XCTest
+@testable import UptechUIKitHelper
+
+final class CollectionIndexPathTests: XCTestCase {
+ private let sut = [[0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]]
+
+ func testIndexPathSubscriptFirstItem() {
+ let result = sut[IndexPath(item: 0, section: 0)]
+ let expectedResult = 0
+
+ XCTAssertEqual(result, expectedResult)
+ }
+
+ func testIndexPathSubscriptMiddleItem() {
+ let result = sut[IndexPath(row: 1, section: 1)]
+ let expectedResult = 4
+
+ XCTAssertEqual(result, expectedResult)
+ }
+
+ func testIndexPathSubscriptLastItem() {
+ let result = sut[IndexPath(item: 2, section: 2)]
+ let expectedResult = 8
+
+ XCTAssertEqual(result, expectedResult)
+ }
+
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/NSLayoutConstraint+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/NSLayoutConstraint+extensionsTests.swift
new file mode 100644
index 0000000..9d2c266
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/NSLayoutConstraint+extensionsTests.swift
@@ -0,0 +1,36 @@
+//
+// NSLayoutConstraint+extensionsTests.swift
+//
+//
+// Created by Sergey Kletsov on 29.03.2022.
+//
+
+import XCTest
+@testable import UptechUIKitHelper
+
+final class NSLayoutConstraintExtensionsTests: XCTestCase {
+
+ func testWithPriority() throws {
+ let sut = NSLayoutConstraint()
+ let priority = UILayoutPriority.almostRequired
+ let result = sut.withPriority(priority)
+ XCTAssertEqual(result.priority, priority, "sut's priority should be \(priority)")
+ }
+
+ func testActivateExtension() throws {
+ let rootView = UIView()
+ let subview = UIView()
+
+ subview.translatesAutoresizingMaskIntoConstraints = false
+ rootView.addSubview(subview)
+
+ let sut = [subview.heightAnchor.constraint(equalToConstant: 100),
+ subview.widthAnchor.constraint(equalTo: rootView.widthAnchor),
+ subview.centerYAnchor.constraint(equalTo: rootView.centerYAnchor),
+ subview.centerXAnchor.constraint(equalTo: rootView.centerXAnchor)]
+
+ sut.activate()
+ XCTAssertTrue(sut.allSatisfy(\.isActive), "all constrains in sut array should be activated")
+ }
+
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UICollectionView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UICollectionView+extensionsTests.swift
new file mode 100644
index 0000000..72b5c5d
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UICollectionView+extensionsTests.swift
@@ -0,0 +1,33 @@
+//
+// UICollectionView+extensionsTests.swift
+//
+//
+// Created by Sergey Kletsov on 05.04.2022.
+//
+
+import XCTest
+import UIKit
+@testable import UptechUIKitHelper
+
+final class UICollectionViewExtensionsTests: XCTestCase {
+ private class TestCell: UICollectionViewCell, ReusableCell { }
+ private let cellType = TestCell.self
+
+ func testRegisterCorrectType() throws {
+ let collectionView = createCollectionView()
+ collectionView.register(cellType)
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: .init(item: 0, section: 0))
+ let isCellRightType = cell.isKind(of: cellType)
+ XCTAssertTrue(isCellRightType, "dequeued cell must be \(cellType)")
+ }
+
+ func testDequeueNotCrashing() {
+ let collectionView = createCollectionView()
+ collectionView.register(cellType, forCellWithReuseIdentifier: cellType.reuseIdentifier)
+ let _ = collectionView.dequeueReusableCell(for: .init(item: 0, section: 0), cellType: cellType)
+ }
+
+ private func createCollectionView() -> UICollectionView {
+ UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
+ }
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UITableView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UITableView+extensionsTests.swift
new file mode 100644
index 0000000..8c7fd68
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UITableView+extensionsTests.swift
@@ -0,0 +1,29 @@
+//
+// UITableView+extensionsTests.swift
+//
+//
+// Created by Sergey Kletsov on 05.04.2022.
+//
+
+import XCTest
+import UIKit
+@testable import UptechUIKitHelper
+
+final class UITableViewExtensionsTests: XCTestCase {
+ private class TestCell: UITableViewCell, ReusableCell { }
+ private let cellType = TestCell.self
+
+ func testRegisterCorrectType() throws {
+ let tableView = UITableView()
+ tableView.register(cellType)
+ let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: .init(item: 0, section: 0))
+ let isCellRightType = cell.isKind(of: cellType)
+ XCTAssertTrue(isCellRightType, "dequeued cell must be \(cellType)")
+ }
+
+ func testDequeueNotCrashing() {
+ let tableView = UITableView()
+ tableView.register(cellType, forCellReuseIdentifier: cellType.reuseIdentifier)
+ let _ = tableView.dequeueReusableCell(for: .init(item: 0, section: 0), cellType: cellType)
+ }
+}
diff --git a/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UIView+extensionsTests.swift b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UIView+extensionsTests.swift
new file mode 100644
index 0000000..4b0c091
--- /dev/null
+++ b/Tests/Uptech-iOS-Helpers-Tests/UIKitHelper/UIView+extensionsTests.swift
@@ -0,0 +1,74 @@
+//
+// UIView+extensionsTests.swift
+//
+//
+// Created by Sergey Kletsov on 29.03.2022.
+//
+
+import XCTest
+@testable import UptechUIKitHelper
+
+final class UIViewExtensionsTests: XCTestCase {
+
+ func testSubviewAdding() throws {
+ let view = UIView()
+ let sut = UIView()
+ let constrains = [sut.heightAnchor.constraint(equalToConstant: 10),
+ sut.widthAnchor.constraint(equalTo: view.widthAnchor),
+ sut.centerYAnchor.constraint(equalTo: view.centerYAnchor)]
+
+ view.addSubview(sut, withConstraints: constrains)
+
+ XCTAssertTrue(view.subviews.contains(sut), "view should contain sut")
+ XCTAssertFalse(sut.translatesAutoresizingMaskIntoConstraints, "sut's translatesAutoresizingMaskIntoConstraints should be false")
+ XCTAssertTrue(constrains.allSatisfy(\.isActive), "all constrains should be active")
+ }
+
+ func testSubviewWithEdgeConstraintsAdding() throws {
+ let frame = CGRect(origin: .zero, size: .init(width: 10, height: 10))
+ let view = UIView(frame: frame)
+ let sut = UIView()
+
+ view.addSubviewWithEdgeConstraints(sut)
+ sut.layoutIfNeeded()
+
+ XCTAssertEqual(frame, sut.bounds, "sut's bounds should be equal to view's bounds")
+ }
+
+ func testSubviewInserting() throws {
+ let view = UIView()
+ let subview1 = UIView()
+ let subview2 = UIView()
+ view.addSubview(subview1)
+ view.addSubview(subview2)
+
+ let sut = UIView()
+ let constrains = [sut.heightAnchor.constraint(equalToConstant: 10),
+ sut.widthAnchor.constraint(equalTo: view.widthAnchor),
+ sut.centerYAnchor.constraint(equalTo: view.centerYAnchor)]
+ let index = 1
+ view.insertSubview(sut, at: index, withConstraints: constrains)
+
+ XCTAssertEqual(view.subviews.firstIndex(of: sut), index, "sut's index should be \(index)")
+ XCTAssertFalse(sut.translatesAutoresizingMaskIntoConstraints, "sut's translatesAutoresizingMaskIntoConstraints should be false")
+ XCTAssertTrue(constrains.allSatisfy(\.isActive), "all constrains should be active")
+ }
+
+ func testSubviewWithEdgeConstraintsInserting() throws {
+ let frame = CGRect(origin: .zero, size: .init(width: 10, height: 10))
+ let view = UIView(frame: frame)
+ let subview1 = UIView()
+ let subview2 = UIView()
+ view.addSubview(subview1)
+ view.addSubview(subview2)
+
+ let sut = UIView()
+ let index = 1
+ view.insertSubviewWithEdgeConstraints(sut, at: index)
+ sut.layoutIfNeeded()
+
+ XCTAssertEqual(view.subviews.firstIndex(of: sut), index, "sut's index should be \(index)")
+ XCTAssertEqual(frame, sut.bounds, "sut's bounds should be equal to view's bounds")
+ }
+
+}
diff --git a/Uptech_iOS_Helpers.podspec b/Uptech_iOS_Helpers.podspec
index b4406b5..085479c 100644
--- a/Uptech_iOS_Helpers.podspec
+++ b/Uptech_iOS_Helpers.podspec
@@ -1,12 +1,12 @@
Pod::Spec.new do |s|
s.name = 'Uptech_iOS_Helpers'
- s.version = '1.0.1'
+ s.version = '2.0.0'
s.summary = 'iOS helper library that contains commonly used code in Uptech iOS projects.'
s.homepage = 'https://github.com/uptechteam/Uptech-iOS-Helpers'
s.license = { :type => 'MIT', :file => 'LICENSE.md' }
s.author = { 'Sergey Kletsov' => 'sergey.kletsov@uptech.team' }
s.source = { :git => 'https://github.com/uptechteam/Uptech-iOS-Helpers.git', :tag => s.version.to_s }
- s.ios.deployment_target = '11.0'
+ s.ios.deployment_target = '13.0'
s.swift_version = '5.5'
s.source_files = 'Sources/Uptech-iOS-Helpers/**/*'
end