Skip to content

Commit a8dcd27

Browse files
committed
✨ Add Identified nibble.
1 parent 1170fdc commit a8dcd27

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed
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 = "Identified"
18+
BuildableName = "Identified"
19+
BlueprintName = "Identified"
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 = "Identified"
53+
BuildableName = "Identified"
54+
BlueprintName = "Identified"
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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ let package = Package(
1313
products: [
1414
.library(name: "Cache", targets: ["Cache"]),
1515
.library(name: "Extensions", targets: ["Extensions"]),
16+
.library(name: "Identified", targets: ["Identified"]),
1617
],
1718
targets: [
1819
.target(name: "Cache"),
1920
.testTarget(name: "CacheTests", dependencies: ["Cache"]),
2021

2122
.target(name: "Extensions"),
2223
.testTarget(name: "ExtensionsTests", dependencies: ["Extensions"]),
24+
25+
.target(name: "Identified"),
26+
.testTarget(name: "IdentifiedTests", dependencies: ["Identified"]),
2327
]
2428
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
3+
// MARK: - Identified
4+
5+
/// An object that is uniquely identifiable.
6+
public protocol Identified {
7+
associatedtype IdentifierValue: Hashable
8+
var id: Identifier<Self, IdentifierValue> { get }
9+
}
10+
11+
// MARK: - Identifier
12+
13+
/// An identified that identifies a particular object type.
14+
public struct Identifier<Object, IdentifierValue: Hashable>: Hashable, Equatable {
15+
16+
/// The underlying value of the identified.
17+
public let value: IdentifierValue
18+
19+
/// Creates an identified backed by the provided value.
20+
///
21+
/// - Parameter value: The value that backs this identified.
22+
public init(value: IdentifierValue) {
23+
self.value = value
24+
}
25+
}
26+
27+
// MARK: - Identifier + Decodable
28+
29+
extension Identifier: Decodable where IdentifierValue: Decodable {
30+
public init(from decoder: Decoder) throws {
31+
let container = try decoder.singleValueContainer()
32+
value = try container.decode(IdentifierValue.self)
33+
}
34+
}
35+
36+
// MARK: - Identifier + Encodable
37+
38+
extension Identifier: Encodable where IdentifierValue: Encodable {
39+
public func encode(to encoder: Encoder) throws {
40+
var container = encoder.singleValueContainer()
41+
try container.encode(value)
42+
}
43+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@testable import Identified
2+
import XCTest
3+
4+
class IdentifiedTests: XCTestCase {
5+
6+
// MARK: Dog
7+
8+
struct Dog: Identified, Codable {
9+
var id: Identifier<Self, Int>
10+
}
11+
12+
// MARK: Tests
13+
14+
func test_identifierValue_whenCreated_matchesExpectation() {
15+
let dog = Dog(id: .init(value: 1))
16+
XCTAssertEqual(dog.id.value, 1)
17+
}
18+
19+
func test_identifierValue_whenDecoded_matchesExpectation() throws {
20+
let data = #"{ "id": 1 }"#.data(using: .utf8)!
21+
let decoder = JSONDecoder()
22+
let dog = try decoder.decode(Dog.self, from: data)
23+
XCTAssertEqual(dog.id.value, 1)
24+
}
25+
26+
func test_identifierValue_whenEncoded_matchesExpectation() throws {
27+
let dog = Dog(id: .init(value: 1))
28+
let encoder = JSONEncoder()
29+
let data = try encoder.encode(dog)
30+
XCTAssertEqual(data, #"{"id":1}"#.data(using: .utf8)!)
31+
}
32+
}
33+

0 commit comments

Comments
 (0)