Skip to content

Commit 02c8559

Browse files
committed
✨ Add Extensions nibble.
1 parent 2acc461 commit 02c8559

File tree

18 files changed

+841
-0
lines changed

18 files changed

+841
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
6+
<true/>
7+
</dict>
8+
</plist>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1500"
4+
version = "1.7">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "Extensions"
18+
BuildableName = "Extensions"
19+
BlueprintName = "Extensions"
20+
ReferencedContainer = "container:">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES"
30+
shouldAutocreateTestPlan = "YES">
31+
</TestAction>
32+
<LaunchAction
33+
buildConfiguration = "Debug"
34+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
35+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
36+
launchStyle = "0"
37+
useCustomWorkingDirectory = "NO"
38+
ignoresPersistentStateOnLaunch = "NO"
39+
debugDocumentVersioning = "YES"
40+
debugServiceExtension = "internal"
41+
allowLocationSimulation = "YES">
42+
</LaunchAction>
43+
<ProfileAction
44+
buildConfiguration = "Release"
45+
shouldUseLaunchSchemeArgsEnv = "YES"
46+
savedToolIdentifier = ""
47+
useCustomWorkingDirectory = "NO"
48+
debugDocumentVersioning = "YES">
49+
<MacroExpansion>
50+
<BuildableReference
51+
BuildableIdentifier = "primary"
52+
BlueprintIdentifier = "Extensions"
53+
BuildableName = "Extensions"
54+
BlueprintName = "Extensions"
55+
ReferencedContainer = "container:">
56+
</BuildableReference>
57+
</MacroExpansion>
58+
</ProfileAction>
59+
<AnalyzeAction
60+
buildConfiguration = "Debug">
61+
</AnalyzeAction>
62+
<ArchiveAction
63+
buildConfiguration = "Release"
64+
revealArchiveInOrganizer = "YES">
65+
</ArchiveAction>
66+
</Scheme>

Package.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// swift-tools-version: 5.8.0
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "swift-nibbles",
7+
platforms: [
8+
.iOS(.v16),
9+
.macOS(.v13),
10+
.tvOS(.v16),
11+
.watchOS(.v9),
12+
],
13+
products: [
14+
.library(name: "Extensions", targets: ["Extensions"]),
15+
],
16+
targets: [
17+
.target(name: "Extensions"),
18+
.testTarget(name: "ExtensionsTests", dependencies: ["Extensions"]),
19+
]
20+
)

Sources/Extensions/Array.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Foundation
2+
3+
extension Array {
4+
/// Splits the array into chunks of the specified size.
5+
public func chunked(into size: Int) -> [[Element]] {
6+
guard size > 0 else {
7+
assertionFailure("Size must not be zero.")
8+
return []
9+
}
10+
11+
return stride(from: 0, to: count, by: size).map {
12+
Array(self[$0 ..< Swift.min($0 + size, count)])
13+
}
14+
}
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
3+
extension Collection {
4+
/// Attempts to retrieve the item at the provided index, returning `nil` if the index is beyond the bounds of the collection.
5+
///
6+
/// - Parameter index: The index from which to retrieve an item.
7+
public subscript(safe index: Index) -> Iterator.Element? {
8+
return indices.contains(index) ? self[index] : nil
9+
}
10+
}

Sources/Extensions/Color.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#if canImport(UIKit)
2+
import UIKit
3+
import SwiftUI
4+
5+
extension Color: Codable {
6+
7+
// MARK: Decodable
8+
9+
public init(from decoder: Decoder) throws {
10+
let container = try decoder.container(keyedBy: CodingKeys.self)
11+
let r = try container.decode(Double.self, forKey: .red)
12+
let g = try container.decode(Double.self, forKey: .green)
13+
let b = try container.decode(Double.self, forKey: .blue)
14+
let a = try container.decode(Double.self, forKey: .alpha)
15+
self.init(.sRGB, red: r, green: g, blue: b, opacity: a)
16+
}
17+
18+
// MARK: Codable
19+
20+
public func encode(to encoder: Encoder) throws {
21+
guard let colorComponents = colorComponents else {
22+
throw EncodingError.invalidValue(self, .init(
23+
codingPath: CodingKeys.allCases,
24+
debugDescription: "Unable to get colorComponents from Color."
25+
))
26+
}
27+
28+
var container = encoder.container(keyedBy: CodingKeys.self)
29+
try container.encode(colorComponents.red, forKey: .red)
30+
try container.encode(colorComponents.green, forKey: .green)
31+
try container.encode(colorComponents.blue, forKey: .blue)
32+
try container.encode(colorComponents.alpha, forKey: .alpha)
33+
}
34+
35+
private var colorComponents: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)? {
36+
var r: CGFloat = 0
37+
var g: CGFloat = 0
38+
var b: CGFloat = 0
39+
var a: CGFloat = 0
40+
41+
guard UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) else {
42+
return nil
43+
}
44+
45+
return (r, g, b, a)
46+
}
47+
48+
// MARK: CodingKeys
49+
50+
enum CodingKeys: String, CodingKey, CaseIterable {
51+
case red
52+
case green
53+
case blue
54+
case alpha
55+
}
56+
}
57+
#endif
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Foundation
2+
3+
extension Comparable {
4+
/// Clamps the value to the provided range.
5+
///
6+
/// - Parameter limits: The range to clamp the value to.
7+
/// - Returns: The value clamped to the provided range.
8+
public func clamped(to limits: ClosedRange<Self>) -> Self {
9+
return min(max(self, limits.lowerBound), limits.upperBound)
10+
}
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import SwiftUI
2+
3+
extension EdgeInsets {
4+
/// A value that represents an `EdgeInsets` with zero around all edges.
5+
public static let zero: EdgeInsets = .init(padding: 0)
6+
7+
/// Creates `EdgeInsets` using the provided `padding` for all edges.
8+
///
9+
/// - Parameter padding: The value to be used for each edge.
10+
public init(padding: CGFloat) {
11+
self.init(
12+
top: padding,
13+
leading: padding,
14+
bottom: padding,
15+
trailing: padding
16+
)
17+
}
18+
19+
/// Creates `EdgeInsets` using the provided `vertical` and `horizontal` values.
20+
///
21+
/// - Parameters:
22+
/// - vertical: The value to be used for both the `top` and `bottom` edges.
23+
/// - horizontal: The value to be used for both the `leading` and `trailing` edges.
24+
public init(vertical: CGFloat, horizontal: CGFloat) {
25+
self.init(
26+
top: vertical,
27+
leading: horizontal,
28+
bottom: vertical,
29+
trailing: horizontal
30+
)
31+
}
32+
}

0 commit comments

Comments
 (0)