diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/IDEFindNavigatorScopes.plist b/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/IDEFindNavigatorScopes.plist
new file mode 100644
index 0000000..5dd5da8
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/IDEFindNavigatorScopes.plist
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..7c6985b
Binary files /dev/null and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/.swiftpm/xcode/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..528e890
--- /dev/null
+++ b/.swiftpm/xcode/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,57 @@
+
+
+
+
+ SchemeUserState
+
+ Builder.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ DataStructures.xcscheme_^#shared#^_
+
+ orderHint
+ 2
+
+ Transform.xcscheme_^#shared#^_
+
+ orderHint
+ 3
+
+ swift-extension-Package.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ Builder
+
+ primary
+
+
+ DataStructures
+
+ primary
+
+
+ Transform
+
+ primary
+
+
+ UnitTests
+
+ primary
+
+
+ swift-extension
+
+ primary
+
+
+
+
+
diff --git a/Package.resolved b/Package.resolved
index 7ed0c62..0ae1d41 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -1,61 +1,77 @@
{
- "object": {
- "pins": [
- {
- "package": "CwlCatchException",
- "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git",
- "state": {
- "branch": null,
- "revision": "35f9e770f54ce62dd8526470f14c6e137cef3eea",
- "version": "2.1.1"
- }
- },
- {
- "package": "CwlPreconditionTesting",
- "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git",
- "state": {
- "branch": null,
- "revision": "fb7a26374e8570ff5c68142e5c83406d6abae0d8",
- "version": "2.0.2"
- }
- },
- {
- "package": "Nimble",
- "repositoryURL": "https://github.com/Quick/Nimble.git",
- "state": {
- "branch": null,
- "revision": "c93f16c25af5770f0d3e6af27c9634640946b068",
- "version": "9.2.1"
- }
- },
- {
- "package": "Quick",
- "repositoryURL": "https://github.com/Quick/Quick.git",
- "state": {
- "branch": null,
- "revision": "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6",
- "version": "4.0.0"
- }
- },
- {
- "package": "SwiftDocCPlugin",
- "repositoryURL": "https://github.com/apple/swift-docc-plugin",
- "state": {
- "branch": null,
- "revision": "9b1258905c21fc1b97bf03d1b4ca12c4ec4e5fda",
- "version": "1.2.0"
- }
- },
- {
- "package": "SymbolKit",
- "repositoryURL": "https://github.com/apple/swift-docc-symbolkit",
- "state": {
- "branch": null,
- "revision": "b45d1f2ed151d057b54504d653e0da5552844e34",
- "version": "1.0.0"
- }
- }
- ]
- },
- "version": 1
+ "pins" : [
+ {
+ "identity" : "cwlcatchexception",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/mattgallagher/CwlCatchException.git",
+ "state" : {
+ "revision" : "3ef6999c73b6938cc0da422f2c912d0158abb0a0",
+ "version" : "2.2.0"
+ }
+ },
+ {
+ "identity" : "cwlpreconditiontesting",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git",
+ "state" : {
+ "revision" : "2ef56b2caf25f55fa7eef8784c30d5a767550f54",
+ "version" : "2.2.1"
+ }
+ },
+ {
+ "identity" : "nimble",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Quick/Nimble.git",
+ "state" : {
+ "revision" : "c93f16c25af5770f0d3e6af27c9634640946b068",
+ "version" : "9.2.1"
+ }
+ },
+ {
+ "identity" : "quick",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Quick/Quick.git",
+ "state" : {
+ "revision" : "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6",
+ "version" : "4.0.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections",
+ "state" : {
+ "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "swift-docc-plugin",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-docc-plugin",
+ "state" : {
+ "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
+ "version" : "1.3.0"
+ }
+ },
+ {
+ "identity" : "swift-docc-symbolkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-docc-symbolkit",
+ "state" : {
+ "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "swift-identified-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/swift-identified-collections",
+ "state" : {
+ "revision" : "d1e45f3e1eee2c9193f5369fa9d70a6ddad635e8",
+ "version" : "1.0.0"
+ }
+ }
+ ],
+ "version" : 2
}
diff --git a/Package.swift b/Package.swift
index 894d322..ab06910 100644
--- a/Package.swift
+++ b/Package.swift
@@ -22,19 +22,27 @@ let package = Package(
.package(url: "https://github.com/Quick/Quick.git", from: "4.0.0"),
.package(url: "https://github.com/Quick/Nimble.git", from: "9.2.0"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"),
+ .package(url: "https://github.com/apple/swift-collections", from: "1.0.2"),
+ .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Transform",
- dependencies: []),
+ dependencies: [
+ .product(name: "IdentifiedCollections", package: "swift-identified-collections"),
+ .product(name: "OrderedCollections", package: "swift-collections"),
+ ]),
.target(
name: "DataStructures",
dependencies: []),
.target(
name: "Builder",
- dependencies: []),
+ dependencies: [
+ .product(name: "IdentifiedCollections", package: "swift-identified-collections"),
+ .product(name: "OrderedCollections", package: "swift-collections"),
+ ]),
.testTarget(
name: "UnitTests",
dependencies: ["DataStructures", "Transform", "Quick", "Nimble"]),
diff --git a/Sources/Builder/IdentifiedArrayBuilder.swift b/Sources/Builder/IdentifiedArrayBuilder.swift
new file mode 100644
index 0000000..8e6f6af
--- /dev/null
+++ b/Sources/Builder/IdentifiedArrayBuilder.swift
@@ -0,0 +1,14 @@
+import IdentifiedCollections
+import Foundation
+
+@available(iOS 13, macOS 12, tvOS 13, watchOS 6, *)
+public extension IdentifiedArray where Element: Identifiable {
+ init(@ArrayBuilder builder: () -> [Element]) where ID == Element.ID {
+ var identifiedArray: IdentifiedArrayOf = []
+ let items = builder()
+ for item in items {
+ identifiedArray.updateOrAppend(item)
+ }
+ self = identifiedArray
+ }
+}
diff --git a/Sources/Transform/Collection/Dictionary+.swift b/Sources/Transform/Collection/Dictionary+.swift
index 13f14cf..1802acb 100644
--- a/Sources/Transform/Collection/Dictionary+.swift
+++ b/Sources/Transform/Collection/Dictionary+.swift
@@ -22,4 +22,14 @@ public extension Dictionary where Value: Hashable {
func has(key: Self.Key) -> Bool {
self[key] != nil
}
+
+ func pick(keys: [Key]) -> [Key: Value] {
+ keys.reduce(into: [Key: Value]()) { partialResult, item in
+ partialResult[item] = self[item]
+ }
+ }
+
+ func keys(for value: Value) -> [Key] {
+ return keys.filter({ self[$0] == value})
+ }
}
diff --git a/Sources/Transform/Collection/IdentifiedArray+.swift b/Sources/Transform/Collection/IdentifiedArray+.swift
new file mode 100644
index 0000000..70ecd34
--- /dev/null
+++ b/Sources/Transform/Collection/IdentifiedArray+.swift
@@ -0,0 +1,17 @@
+import IdentifiedCollections
+
+public extension IdentifiedArray {
+ func toArray() -> [Element] {
+ var array: [Element] = []
+ for value in self {
+ array.append(value)
+ }
+ return array
+ }
+}
+
+public extension IdentifiedArray where Element: Hashable {
+ func toSet() -> Set {
+ toArray().toSet()
+ }
+}
diff --git a/Sources/Transform/Fundamental/Bool+.swift b/Sources/Transform/Fundamental/Bool+.swift
new file mode 100644
index 0000000..ad349b4
--- /dev/null
+++ b/Sources/Transform/Fundamental/Bool+.swift
@@ -0,0 +1,10 @@
+public extension Bool {
+
+ func toInt() -> Int {
+ self ? 1 : 0
+ }
+
+ func toString() -> String {
+ self ? "true" : "false"
+ }
+}
diff --git a/Sources/Transform/Fundamental/Double+.swift b/Sources/Transform/Fundamental/Double+.swift
index 1432a31..ed3eb07 100644
--- a/Sources/Transform/Fundamental/Double+.swift
+++ b/Sources/Transform/Fundamental/Double+.swift
@@ -20,4 +20,20 @@ public extension Double {
func toFloat() -> Float {
toString().toFloat()!
}
+
+ func toNano() -> Double {
+ self * 1_000_000_000
+ }
+
+ func toGiga() -> Double {
+ self / 1_000_000_000
+ }
+
+ var nano: Double {
+ self * 1_000_000_000
+ }
+
+ var giga: Double {
+ self / 1_000_000_000
+ }
}
diff --git a/Sources/Transform/Fundamental/Int+.swift b/Sources/Transform/Fundamental/Int+.swift
index eac52b5..c687a6b 100644
--- a/Sources/Transform/Fundamental/Int+.swift
+++ b/Sources/Transform/Fundamental/Int+.swift
@@ -25,4 +25,13 @@ public extension Int {
return UInt(self)
}
+ func toNano() -> Double {
+ self.toDouble()/1_000_000_000
+ }
+}
+
+extension UInt64 {
+ public init(seconds: Double) {
+ self.init(seconds.nano)
+ }
}
diff --git a/Sources/Transform/Fundamental/URL+.swift b/Sources/Transform/Fundamental/URL+.swift
index e683829..69db52a 100644
--- a/Sources/Transform/Fundamental/URL+.swift
+++ b/Sources/Transform/Fundamental/URL+.swift
@@ -4,4 +4,15 @@ public extension URL {
func toString() -> String {
absoluteString
}
+
+ func toQueryParameters() -> [String: String]? {
+ guard let queryItems = URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems else {
+ return nil
+ }
+ return Dictionary(queryItems.lazy.compactMap {
+ guard let value = $0.value else { return nil }
+ return ($0.name, value)
+ }) { first, _ in first }
+ }
+
}
diff --git a/Sources/Transform/Important/ArrayExt.swift b/Sources/Transform/Important/ArrayExt.swift
index 3c295f0..577f25d 100644
--- a/Sources/Transform/Important/ArrayExt.swift
+++ b/Sources/Transform/Important/ArrayExt.swift
@@ -1,7 +1,17 @@
import Foundation
+import IdentifiedCollections
+
+public extension Array {
+ @discardableResult
+ func appending(value element: Element) -> Self {
+ var copy = self
+ copy.append(element)
+ return copy
+ }
+}
public extension Array where Element: Equatable {
- var unique: [Element] {
+ var removedDuplicates: [Element] {
var uniqueValues: [Element] = []
forEach { item in
guard !uniqueValues.contains(item) else { return }
@@ -10,3 +20,25 @@ public extension Array where Element: Equatable {
return uniqueValues
}
}
+
+public extension Array where Element == [String: Any] {
+
+ func toData(options opt: JSONSerialization.WritingOptions = []) -> Data? {
+ try? JSONSerialization.data(withJSONObject: self, options: opt)
+ }
+}
+
+
+public extension Array where Element: Codable {
+
+ func toData(options opt: JSONSerialization.WritingOptions = []) -> Data? {
+ compactMap({$0.toDictionary()}).toData(options: opt)
+ }
+}
+
+public extension IdentifiedArray where Element: Codable {
+
+ func toData(options opt: JSONSerialization.WritingOptions = []) -> Data? {
+ toArray().toData(options: opt)
+ }
+}
diff --git a/Sources/Transform/Important/DataExt.swift b/Sources/Transform/Important/DataExt.swift
index 6109e4f..5fd3ddd 100644
--- a/Sources/Transform/Important/DataExt.swift
+++ b/Sources/Transform/Important/DataExt.swift
@@ -19,7 +19,6 @@ public extension Data {
}
}
-#if os(iOS)
func toData(keyPath: String? = nil) -> Self {
guard let keyPath = keyPath else {
return self
@@ -38,5 +37,35 @@ public extension Data {
}
return self
}
-#endif
+
+ func hasData(keyPath: String? = nil) -> Bool {
+ guard let keyPath = keyPath else { return true }
+ do {
+ let json = try JSONSerialization.jsonObject(with: self, options: [])
+ if let nestedJson = (json as AnyObject).value(forKeyPath: keyPath) {
+ guard JSONSerialization.isValidJSONObject(nestedJson) else {
+ return false
+ }
+ let _ = try JSONSerialization.data(withJSONObject: nestedJson)
+ return false
+ }
+ } catch {
+ return false
+ }
+ return false
+ }
+
+ subscript(_ keyPath: String? = nil) -> Self? {
+ toData(keyPath: keyPath)
+ }
+
+ func toDataPrettyPrinted() -> Self {
+ do {
+ let dataAsJSON = try JSONSerialization.jsonObject(with: self)
+ let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
+ return prettyData
+ } catch {
+ return self // fallback to original data if it can't be serialized.
+ }
+ }
}
diff --git a/Sources/Transform/Important/IdentifiedArrayExt.swift b/Sources/Transform/Important/IdentifiedArrayExt.swift
new file mode 100644
index 0000000..372c21b
--- /dev/null
+++ b/Sources/Transform/Important/IdentifiedArrayExt.swift
@@ -0,0 +1,54 @@
+import IdentifiedCollections
+import Foundation
+
+@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
+public extension IdentifiedArray where Element: Identifiable {
+
+ init(_ builder: () -> IdentifiedArray) {
+ self = builder()
+ }
+
+ init(_ builder: () -> Array) where ID == Element.ID {
+ self = builder().toIdentifiedArray()
+ }
+}
+
+@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
+public extension Array where Element: Identifiable {
+ func toIdentifiedArray() -> IdentifiedArrayOf {
+ var identifiedArray: IdentifiedArrayOf = []
+ for value in self {
+ identifiedArray.updateOrAppend(value)
+ }
+ return identifiedArray
+ }
+}
+
+@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
+public extension IdentifiedArray where Element: Identifiable {
+
+ @discardableResult
+ mutating func updateOrAppend(_ items: Self) -> Self {
+ for item in items {
+ self.updateOrAppend(item)
+ }
+ return self
+ }
+
+ @discardableResult
+ mutating func updateOrAppend(_ items: [Element]) -> Self {
+ for item in items {
+ self.updateOrAppend(item)
+ }
+ return self
+ }
+
+ @discardableResult
+ mutating func updateOrAppend(ifLet item: Element?) -> Self {
+ guard let item = item else {
+ return self
+ }
+ self.updateOrAppend(item)
+ return self
+ }
+}
diff --git a/Sources/Transform/Important/MutableCollectionExt.swift b/Sources/Transform/Important/MutableCollectionExt.swift
deleted file mode 100644
index b93fd65..0000000
--- a/Sources/Transform/Important/MutableCollectionExt.swift
+++ /dev/null
@@ -1,14 +0,0 @@
-import Foundation
-
-public extension MutableCollection {
- subscript(safe index: Index) -> Element? {
- get {
- indices.contains(index) ? self[index] : nil
- }
- mutating set {
- if indices.contains(index), let value = newValue {
- self[index] = value
- }
- }
- }
-}
diff --git a/Sources/Transform/Important/SetExt.swift b/Sources/Transform/Important/SetExt.swift
index 8aa716d..b7ec423 100644
--- a/Sources/Transform/Important/SetExt.swift
+++ b/Sources/Transform/Important/SetExt.swift
@@ -1,5 +1,13 @@
import Foundation
public extension Set {
-
+ @discardableResult
+ mutating func toggle(_ element: Element) -> Element {
+ if self.contains(element) {
+ self.remove(element)
+ } else {
+ self.insert(element)
+ }
+ return element
+ }
}
diff --git a/Sources/Transform/Important/StringExt.swift b/Sources/Transform/Important/StringExt.swift
index 5875f5c..6cc3974 100644
--- a/Sources/Transform/Important/StringExt.swift
+++ b/Sources/Transform/Important/StringExt.swift
@@ -1,7 +1,7 @@
import Foundation
public extension String {
- func toData(using:String.Encoding = .utf8) -> Data? {
+ func toData(using: String.Encoding = .utf8) -> Data? {
return self.data(using: using)
}