Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitattributes

This file was deleted.

23 changes: 0 additions & 23 deletions .github/workflows/ci.yml

This file was deleted.

16 changes: 16 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Code Quality

on:
workflow_dispatch:
pull_request:

jobs:
SwiftLint:
name: Swift files are formatted
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: SwiftLint
run: swiftlint --quiet --reporter github-actions-logging
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Test

on:
workflow_dispatch:
pull_request:

jobs:
Test:
name: Unit tests pass
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- id: cache
uses: actions/cache@v4
with:
path: |
.build
.swiftpm
key: ${{ runner.os }}-cache
- run: swift test
10 changes: 3 additions & 7 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
disabled_rules:
- closure_parameter_position
- identifier_name
- multiple_closures_with_trailing_closure
- todo
opt_in_rules:
- attributes
- closure_end_indentation
- closure_parameter_position
- closure_spacing
- contains_over_filter_count
- contains_over_filter_is_empty
Expand All @@ -19,11 +17,9 @@ opt_in_rules:
- first_where
- flatmap_over_map_reduce
- indentation_width
- legacy_random
- literal_expression_end_indentation
- lower_acl_than_parent
- operator_usage_whitespace
- redundant_nil_coalescing
- redundant_type_annotation
- sorted_first_last
- sorted_imports
Expand All @@ -35,8 +31,8 @@ opt_in_rules:
- vertical_parameter_alignment_on_call
- yoda_condition
excluded:
- .build
- .swiftpm
- "**/.build/"
- "**/.swiftpm/"
indentation_width:
include_comments: false
include_compiler_directives: false
Expand Down
69 changes: 69 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 22 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
// swift-tools-version:5.9
// swift-tools-version:6.0
import PackageDescription

let package = Package(
name: "SwiftPackage",
name: "keychain",
platforms: [
.iOS(.v16),
.macOS(.v13),
.tvOS(.v16),
.visionOS(.v1),
.watchOS(.v9)
],
products: [
.library(name: "SwiftPackage", targets: ["SwiftPackage"])
.library(name: "Keychain", targets: ["Keychain"])
],
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.1"),
.package(url: "https://github.com/square/Valet", from: "5.0.0")
],
targets: [
.target(name: "SwiftPackage"),
.testTarget(name: "SwiftPackageTests", dependencies: ["SwiftPackage"])
.target(
name: "Keychain",
dependencies: [
.product(name: "Dependencies", package: "swift-dependencies"),
.product(name: "Valet", package: "Valet")
]
),
.testTarget(name: "KeychainTests", dependencies: ["Keychain"])
]
)
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# Swift Package Repository Template
# Keychain dependency

This is a project template for Swift packages.

* Sensible `.gitignore`
* `.swiftlint.yml` for linting
* `.editorconfig` enforcing tabs with a size of 2.
Vends a [dependency](https://github.com/pointfreeco/swift-dependencies) for accessing the keychain
33 changes: 33 additions & 0 deletions Sources/Keychain/InMemoryKeychain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Dependencies
import Foundation
import os

public final class InMemoryKeychain {

private static let data = OSAllocatedUnfairLock(initialState: [String: Data]())

init() {}

private let encoder = JSONEncoder()
private let decoder = JSONDecoder()

}

// MARK: Keychain

extension InMemoryKeychain: Keychain {

public func load<T>(key: String) throws -> T where T: Decodable {
return try decoder.decode(T.self, from: InMemoryKeychain.data.withLock { $0[key] } ?? Data())
}

public func save<T>(key: String, value: T) throws where T: Encodable {
let data = try encoder.encode(value)
InMemoryKeychain.data.withLock { $0[key] = data }
}

public func delete(key: String) throws {
_ = InMemoryKeychain.data.withLock { $0.removeValue(forKey: key) }
}

}
19 changes: 19 additions & 0 deletions Sources/Keychain/Keychain+Dependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Dependencies
import Foundation

extension DependencyValues {

public var keychain: any Keychain {
get { self[KeychainDependencyKey.self] }
set { self[KeychainDependencyKey.self] = newValue }
}

}

public enum KeychainDependencyKey: DependencyKey {
public static let liveValue: any Keychain = ValetKeychain()
}

extension KeychainDependencyKey: TestDependencyKey {
public static let testValue: any Keychain = InMemoryKeychain()
}
13 changes: 13 additions & 0 deletions Sources/Keychain/Keychain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

public protocol Keychain: Sendable {

@inlinable
func load<T>(key: String) throws -> T where T: Decodable

@inlinable
func save<T>(key: String, value: T) throws where T: Encodable

func delete(key: String) throws

}
20 changes: 20 additions & 0 deletions Sources/Keychain/KeychainConfiguration+Dependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Dependencies
import Foundation
import XCTestDynamicOverlay

extension DependencyValues {

public var keychainConfiguration: KeychainConfiguration {
get { self[KeychainConfiguration.self] }
set { self[KeychainConfiguration.self] = newValue }
}

}

extension KeychainConfiguration: TestDependencyKey {

public static var testValue: KeychainConfiguration {
KeychainConfiguration(type: .local, identifier: Identifier.identifier("test"), accessibility: .afterFirstUnlock)
}

}
34 changes: 34 additions & 0 deletions Sources/Keychain/KeychainConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Foundation

public struct KeychainConfiguration: Hashable, Sendable {

public let type: Type
public let identifier: Identifier
public let accessibility: Accessibility

public init(
type: `Type`,
identifier: Identifier,
accessibility: Accessibility
) {
self.type = type
self.identifier = identifier
self.accessibility = accessibility
}

public enum `Type`: Hashable, Sendable {
case iCloud
case local
}

public enum Identifier: Hashable, Sendable {
case identifier(String)
case sharedGroup(appIDPrefix: String, groupIdentifier: String)
}

public enum Accessibility: Hashable, Sendable {
case whenUnlocked
case afterFirstUnlock
}

}
Loading