Skip to content

Instantly share code, notes, and snippets.

@0xc010d
Last active November 17, 2017 02:55
Show Gist options
  • Save 0xc010d/4d37db5d6ca3f3562efd to your computer and use it in GitHub Desktop.
Save 0xc010d/4d37db5d6ca3f3562efd to your computer and use it in GitHub Desktop.
import Foundation
import Security
private let keychainQueue = dispatch_queue_create("networking.keychain", DISPATCH_QUEUE_CONCURRENT)
public class Keychain {
private let service: String
public init(service: String) {
self.service = service
}
public subscript(key: String) -> String? {
get {
return getItem(key)
}
set(newValue) {
setItem(newValue, forKey: key)
}
}
public func deleteAll() {
let query: [String : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
]
dispatch_barrier_async(keychainQueue, {
SecItemDelete(query)
return
})
}
private func setItem(value: String?, forKey key: String) {
deleteItem(key)
if let value = value {
addItem(value, forKey: key)
}
}
private func addItem(value: String, forKey key: String) {
let data = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
if let data = data {
let query: [String : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : key,
kSecValueData : data,
]
dispatch_barrier_async(keychainQueue, {
SecItemAdd(query, nil)
return
})
}
}
private func deleteItem(key: String) {
let query: [String : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : key,
]
dispatch_barrier_async(keychainQueue, {
SecItemDelete(query)
return
})
}
private func getItem(key: String) -> String? {
let query: [String : AnyObject] = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : key,
kSecReturnData : kCFBooleanTrue,
kSecMatchLimit : kSecMatchLimitOne,
]
var value: String?
dispatch_sync(keychainQueue, {
var dataDescriptorRef: Unmanaged<AnyObject>?
SecItemCopyMatching(query, &dataDescriptorRef)
let dataDescriptor = dataDescriptorRef?.toOpaque()
if let dataDescriptor = dataDescriptor {
let data = Unmanaged<NSData>.fromOpaque(dataDescriptor).takeUnretainedValue()
value = NSString(data: data, encoding: NSUTF8StringEncoding)
}
})
return value
}
}
import Foundation
import XCTest
import Networking
class KeychainTests: XCTestCase {
let keychain: Keychain = Keychain(service: "service")
override func tearDown() {
keychain.deleteAll()
}
func testEmpty() {
XCTAssertNil(keychain["key1"], "Value should be nil")
}
func testAdd() {
keychain["key1"] = "value1"
let value = keychain["key1"]
XCTAssertNotNil(value, "Value should not be nil")
if let value = value {
XCTAssertEqual(value, "value1", "Value should be properly set")
}
}
func testSet() {
keychain["key1"] = "value1"
keychain["key1"] = "value2"
let value = keychain["key1"]
XCTAssertNotNil(value, "Value should not be nil")
if let value = value {
XCTAssertEqual(value, "value2", "Value should be properly set")
}
}
func testDelete() {
keychain["key1"] = "value1"
keychain["key1"] = nil
XCTAssertNil(keychain["key1"], "Value should be nil")
}
func testDeleteAll() {
keychain["key1"] = "value1"
keychain["key2"] = "value2"
keychain.deleteAll()
XCTAssertNil(keychain["key1"], "Value should be nil")
XCTAssertNil(keychain["key2"], "Value should be nil")
}
func testTwoKeychains() {
let keychain2: Keychain = Keychain(service: "service")
keychain2["key1"] = "value1"
let value = keychain["key1"]
XCTAssertNotNil(value, "Value should not be nil")
if let value = value {
XCTAssertEqual(value, "value1", "Value should be properly set")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment