Skip to content

Commit c70d5bd

Browse files
committed
Keychain dependency implementation
1 parent 5bd423d commit c70d5bd

16 files changed

Lines changed: 377 additions & 44 deletions

.gitattributes

Lines changed: 0 additions & 2 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 0 additions & 23 deletions
This file was deleted.

.github/workflows/code-quality.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Code Quality
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
7+
jobs:
8+
SwiftLint:
9+
name: Swift files are formatted
10+
runs-on: self-hosted
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
submodules: recursive
15+
- name: SwiftLint
16+
run: swiftlint --quiet --reporter github-actions-logging

.github/workflows/test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Test
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
7+
jobs:
8+
Test:
9+
name: Unit tests pass
10+
runs-on: self-hosted
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
submodules: recursive
15+
- id: cache
16+
uses: actions/cache@v4
17+
with:
18+
path: |
19+
.build
20+
.swiftpm
21+
key: ${{ runner.os }}-cache
22+
- run: swift test

.swiftlint.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
disabled_rules:
2-
- closure_parameter_position
32
- identifier_name
43
- multiple_closures_with_trailing_closure
4+
- todo
55
opt_in_rules:
6-
- attributes
76
- closure_end_indentation
8-
- closure_parameter_position
97
- closure_spacing
108
- contains_over_filter_count
119
- contains_over_filter_is_empty
@@ -19,11 +17,9 @@ opt_in_rules:
1917
- first_where
2018
- flatmap_over_map_reduce
2119
- indentation_width
22-
- legacy_random
2320
- literal_expression_end_indentation
2421
- lower_acl_than_parent
2522
- operator_usage_whitespace
26-
- redundant_nil_coalescing
2723
- redundant_type_annotation
2824
- sorted_first_last
2925
- sorted_imports
@@ -35,8 +31,8 @@ opt_in_rules:
3531
- vertical_parameter_alignment_on_call
3632
- yoda_condition
3733
excluded:
38-
- .build
39-
- .swiftpm
34+
- "**/.build/"
35+
- "**/.swiftpm/"
4036
indentation_width:
4137
include_comments: false
4238
include_compiler_directives: false

Package.resolved

Lines changed: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1-
// swift-tools-version:5.9
1+
// swift-tools-version:6.0
22
import PackageDescription
33

44
let package = Package(
5-
name: "SwiftPackage",
5+
name: "keychain",
6+
platforms: [
7+
.iOS(.v16),
8+
.macOS(.v13),
9+
.tvOS(.v16),
10+
.visionOS(.v1),
11+
.watchOS(.v9)
12+
],
613
products: [
7-
.library(name: "SwiftPackage", targets: ["SwiftPackage"])
14+
.library(name: "Keychain", targets: ["Keychain"])
15+
],
16+
dependencies: [
17+
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.1"),
18+
.package(url: "https://github.com/square/Valet", from: "5.0.0")
819
],
920
targets: [
10-
.target(name: "SwiftPackage"),
11-
.testTarget(name: "SwiftPackageTests", dependencies: ["SwiftPackage"])
21+
.target(
22+
name: "Keychain",
23+
dependencies: [
24+
.product(name: "Dependencies", package: "swift-dependencies"),
25+
.product(name: "Valet", package: "Valet")
26+
]
27+
),
28+
.testTarget(name: "KeychainTests", dependencies: ["Keychain"])
1229
]
1330
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Dependencies
2+
import Foundation
3+
import os
4+
5+
public final class InMemoryKeychain {
6+
7+
private static let data = OSAllocatedUnfairLock(initialState: [String: Data]())
8+
9+
init() {}
10+
11+
private let encoder = JSONEncoder()
12+
private let decoder = JSONDecoder()
13+
14+
}
15+
16+
// MARK: Keychain
17+
18+
extension InMemoryKeychain: Keychain {
19+
20+
public func load<T>(key: String) throws -> T where T: Decodable {
21+
return try decoder.decode(T.self, from: InMemoryKeychain.data.withLock { $0[key] } ?? Data())
22+
}
23+
24+
public func save<T>(key: String, value: T) throws where T: Encodable {
25+
let data = try encoder.encode(value)
26+
InMemoryKeychain.data.withLock { $0[key] = data }
27+
}
28+
29+
public func delete(key: String) throws {
30+
_ = InMemoryKeychain.data.withLock { $0.removeValue(forKey: key) }
31+
}
32+
33+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Dependencies
2+
import Foundation
3+
4+
extension DependencyValues {
5+
6+
public var keychain: any Keychain {
7+
get { self[KeychainDependencyKey.self] }
8+
set { self[KeychainDependencyKey.self] = newValue }
9+
}
10+
11+
}
12+
13+
public enum KeychainDependencyKey: DependencyKey {
14+
public static let liveValue: any Keychain = ValetKeychain()
15+
}
16+
17+
extension KeychainDependencyKey: TestDependencyKey {
18+
public static let testValue: any Keychain = InMemoryKeychain()
19+
}

Sources/Keychain/Keychain.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Foundation
2+
3+
public protocol Keychain: Sendable {
4+
5+
@inlinable
6+
func load<T>(key: String) throws -> T where T: Decodable
7+
8+
@inlinable
9+
func save<T>(key: String, value: T) throws where T: Encodable
10+
11+
func delete(key: String) throws
12+
13+
}

0 commit comments

Comments
 (0)