I have an iOS project.
I have extracted some authentication code today and moved it to it's own project.
This project supports macOS and iOS.
I have created a workspace that contains both the iOS project and the Auth project and I have added the auth .framework
to my iOS project via the Frameworks, Libraries, and Embedded Content
panel.
I can build and use my services. I have also setup a custom scheme CI_iOS
that allows me to run the tests for these projects.
I have now added some integration tests to my auth module, so I can test my KeychainTokenStore
class against the real Keychain.
These work when testing my module against macOS, however attempting to run them on the iOS simulator they all fall.
I can see when attempting to interact with the Keychain an error code is returned -34018
which I believe indicates errSecMissingEntitlement
.
I have been reading a number of posts that seem to suggest I need to enable keychain sharing.
I cannot seem to make this work however.
My KeychainTokenStore
looks like this
import Foundation
public protocol TokenStore {
typealias DeleteCacheResult = Result<Void, Error>
typealias DeleteCacheCompletion = (DeleteCacheResult) -> Void
typealias InsertCacheResult = Result<Void, Error>
typealias InsertCacheCompletion = (InsertCacheResult) -> Void
typealias RetrieveCacheResult = Result<String?, Error>
typealias RetrieveCacheCompletion = (RetrieveCacheResult) -> Void
/// The completion handler can be invoked in any thread.
/// Clients are responsible for dispatching to the appropriate thread, if needed.
func insert(_ token: String, key: String, completion: @escaping InsertCacheCompletion)
/// The completion handler can be invoked in any thread.
/// Clients are responsible for dispatching to the appropriate thread, if needed.
func delete(_ key: String, completion: @escaping DeleteCacheCompletion)
/// The completion handler can be invoked in any thread.
/// Clients are responsible for dispatching to the appropriate thread, if needed.
func retrieve(_ key: String, completion: @escaping RetrieveCacheCompletion)
}
public final class KeychainTokenStore: TokenStore {
public enum Error: Swift.Error {
case saveFailed
}
public init() { }
private lazy var queue = DispatchQueue(label: "KeychainTokenStore.queue", qos: .userInitiated, attributes: .concurrent)
public func insert(_ token: String, key: String, completion: @escaping InsertCacheCompletion) {
queue.async(flags: .barrier) {
completion(Result {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: Data(token.utf8)
] as CFDictionary
SecItemDelete(query)
guard SecItemAdd(query, nil) == noErr else { throw Error.saveFailed }
})
}
}
public func delete(_ key: String, completion: @escaping DeleteCacheCompletion) {
queue.async(flags: .barrier) {
completion(Result {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
] as CFDictionary
SecItemDelete(query)
})
}
}
public func retrieve(_ key: String, completion: @escaping RetrieveCacheCompletion) {
queue.async {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecReturnData: kCFBooleanTrue as Any,
kSecMatchLimit: kSecMatchLimitOne
] as CFDictionary
var result: AnyObject?
let status = SecItemCopyMatching(query, &result)
guard status == noErr, let data = result as? Data else {
return completion(.success(.none))
}
completion(Result {
String(decoding: data, as: UTF8.self)
})
}
}
}
In my iOS app I enabled sharing as follows:
When I attempt to enable sharing in my auth framework however I see the following
How can I ensure my DigiAuth
framework can access the Keychain with running my DigiApp
project?