Skip to content

Commit

Permalink
Add property wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
blorenzo10 committed Oct 16, 2023
1 parent 40f33c7 commit 243b703
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 20 deletions.
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ let package = Package(
.target(
name: "KeychainUtility",
dependencies: []),
.testTarget(
name: "KeychainUtilityTests",
dependencies: ["KeychainUtility"]),
]
)
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Keychain Utility is a wrapper to help you interact with the Keychain APIs in an easier way

# Usage
Add `@KeychainStorage` annotation in the properties you want to store into the keychain (or read from it). Provide a `Key` and an `ItemType` (you can check the list [here](https://blorenzo10.github.io/keychain-utility/documentation/keychainutility/itemclass))

```swift
@KeychainStorage(key: "API-Token", itemClass: .generic)
var token: String?

// It will print the current value
print(token)

// Update item
token = "c0c61d55558b0c8dac82a16c04981eea7c99e37d714367e575028221028b0d4cff122d6a7556fc0ab1c66d1d4b05b378"

// Delete item
token = nil
```

# Documentation

Check [Keychain Utility Documentation Page](https://blorenzo10.github.io/keychain-utility/documentation/keychainutility/) for more info.
Expand Down
31 changes: 11 additions & 20 deletions Sources/KeychainUtility/KeychainManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import Foundation
/// Manager with all the necessary methods to interact with the keychain
public class KeychainManager {

/// Shared instance to access the manager
public static let shared = KeychainManager()

This comment has been minimized.

Copy link
@codebettertech

codebettertech Jul 11, 2024

[IT]

Credo che in un commit hai rimosso tutte le variabili necessarie al funzionamenrto, come shared, attributes... Al momento l esempio su GitHub non è funzionante, io lo ho sistemato da me. Probabilmente con l inserimento del @propertyWrapper si è perso tutto.

[EN]

I think in a commit you removed all the variables needed for it to work, like shared, attributes... At the moment the example on GitHub is not working, I fixed it myself. Probably with the insertion of the @propertyWrapper everything was lost.

private var attributes: ItemAttributes?

public typealias KeychainDictionary = [String : Any]
public typealias ItemAttributes = [CFString : Any]
public class var standard: KeychainManager {
return KeychainManager()
}

private init() {}
public init(attributes: ItemAttributes? = nil) {
self.attributes = attributes
}

/// Save any Encodable data into the keychain
///
Expand All @@ -31,8 +33,7 @@ public class KeychainManager {
/// - Parameter itemClass: The item class
/// - Parameter key: Key to identifiy the item
/// - Parameter attributes: Optional dictionary with attributes to narrow the search
public func saveItem<T: Encodable>(_ item: T, itemClass: ItemClass, key: String, attributes: ItemAttributes? = nil) throws {

public func saveItem<T: Encodable>(_ item: T, itemClass: ItemClass, key: String) throws {
let itemData = try JSONEncoder().encode(item)
var query: KeychainDictionary = [
kSecClass as String: itemClass.rawValue,
Expand Down Expand Up @@ -69,7 +70,7 @@ public class KeychainManager {
/// - Parameter key: Key to identifiy the item
/// - Parameter attributes: Optional dictionary with attributes to narrow the search
/// - Returns: An instance of type `T`
public func retrieveItem<T: Decodable>(ofClass itemClass: ItemClass, key: String, attributes: ItemAttributes? = nil) throws -> T {
public func retrieveItem<T: Decodable>(ofClass itemClass: ItemClass, key: String) throws -> T {
var query: KeychainDictionary = [
kSecClass as String: itemClass.rawValue,
kSecAttrAccount as String: key as AnyObject,
Expand Down Expand Up @@ -113,7 +114,7 @@ public class KeychainManager {
/// - Parameter itemClass: The item class
/// - Parameter key: Key to identifiy the item
/// - Parameter attributes: Optional dictionary with attributes to narrow the search
public func updateItem<T: Encodable>(with item: T, ofClass itemClass: ItemClass, key: String, attributes: ItemAttributes? = nil) throws {
public func updateItem<T: Encodable>(with item: T, ofClass itemClass: ItemClass, key: String) throws {
var query: KeychainDictionary = [
kSecClass as String: itemClass.rawValue,
kSecAttrAccount as String: key as AnyObject,
Expand Down Expand Up @@ -153,7 +154,7 @@ public class KeychainManager {
/// - Parameter itemClass: The item class
/// - Parameter key: Key to identifiy the item
/// - Parameter attributes: Optional dictionary with attributes to narrow the search
public func deleteItem(ofClass itemClass: ItemClass, key: String, attributes: ItemAttributes? = nil) throws {
public func deleteItem(ofClass itemClass: ItemClass, key: String) throws {
var query: KeychainDictionary = [
kSecClass as String: itemClass.rawValue,
kSecAttrAccount as String: key as AnyObject
Expand Down Expand Up @@ -188,13 +189,3 @@ private extension KeychainManager {
}

}

// MARK: - Dictionary

extension KeychainManager.KeychainDictionary {
mutating func addAttributes(_ attributes: KeychainManager.ItemAttributes) {
for(key, value) in attributes {
self[key as String] = value
}
}
}
73 changes: 73 additions & 0 deletions Sources/KeychainUtility/KeychainStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Foundation

@propertyWrapper
public struct KeychainStorage<T: Codable> {
let key: String
let itemClass: ItemClass
let keychain: KeychainManager

private var currentValue: T?

public var wrappedValue: T? {
get {
return getItem()
}

set {
if let newValue {
getItem() != nil
? updateItem(newValue)
: saveItem(newValue)
} else {
deleteItem()
}
}
}

public init(key: String, itemClass: ItemClass, keychain: KeychainManager = .standard) {
self.key = key
self.itemClass = itemClass
self.keychain = keychain
}
}

// MARK: - Helpers
private extension KeychainStorage {

func getItem() -> T? {
do {
return try keychain.retrieveItem(ofClass: itemClass, key: key)
} catch {
handleError(error)
}
return nil
}

func saveItem(_ item: T) {
do {
try keychain.saveItem(item, itemClass: itemClass, key: key)
} catch {
handleError(error)
}
}

func updateItem(_ item: T) {
do {
try keychain.updateItem(with: item, ofClass: itemClass, key: key)
} catch {
handleError(error)
}
}

func deleteItem() {
do {
try keychain.deleteItem(ofClass: itemClass, key: key)
} catch {
handleError(error)
}
}

func handleError(_ error: Error) {
print(error)
}
}
19 changes: 19 additions & 0 deletions Sources/KeychainUtility/KeychainUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// File.swift
//
//
// Created by Bruno Lorenzo on 5/10/23.
//

import Foundation

public typealias KeychainDictionary = [String : Any]
public typealias ItemAttributes = [CFString : Any]

extension KeychainDictionary {
mutating func addAttributes(_ attributes: ItemAttributes) {
for(key, value) in attributes {
self[key as String] = value
}
}
}
47 changes: 47 additions & 0 deletions Tests/KeychainUtilityTests/PropertyWrapperTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

import Foundation
import XCTest
@testable import KeychainUtility

final class PropertyWrapperTests: XCTestCase {

@KeychainStorage(key: "API-Token", itemClass: .generic)
var token: String?

override func setUp() {
super.setUp()
}

override func tearDown() {
super.tearDown()
token = nil
}

func testSaveItem() {
token = "c0c61d55558b0c8dac82a16c04981eea7c99e37d714367e575028221028b0d4cff122d6a7556fc0ab1c66d1d4b05b378"
let keychainToken: String? = try? KeychainManager.standard.retrieveItem(ofClass: .generic, key: "API-Token")
XCTAssertEqual(token, keychainToken)
}

func testUpdateItem() {
token = "c0c61d55558b0c8dac82a16c04981eea7c99e37d714367e575028221028b0d4cff122d6a7556fc0ab1c66d1d4b05b378"
var keychainToken: String? = try? KeychainManager.standard.retrieveItem(ofClass: .generic, key: "API-Token")
XCTAssertEqual(token, keychainToken)

token = "b7bb1d55558b0c8dac82a16c04981eea7c99e37d714367e575028221028b0d4cff122d6a7556fc0ab1c66d1d4b05b378"
XCTAssertNotEqual(token, keychainToken)

keychainToken = try? KeychainManager.standard.retrieveItem(ofClass: .generic, key: "API-Token")
XCTAssertEqual(token, keychainToken)
}

func testRemoveItem() {
token = "c0c61d55558b0c8dac82a16c04981eea7c99e37d714367e575028221028b0d4cff122d6a7556fc0ab1c66d1d4b05b378"
var keychainToken: String? = try? KeychainManager.standard.retrieveItem(ofClass: .generic, key: "API-Token")
XCTAssertEqual(token, keychainToken)

token = nil
keychainToken = try? KeychainManager.standard.retrieveItem(ofClass: .generic, key: "API-Token")
XCTAssertNil(token)
}
}

0 comments on commit 243b703

Please sign in to comment.