-
-
Save jackreichert/414623731241c95f0e20 to your computer and use it in GitHub Desktop.
Swift Keychain class ( supported Xcode Version 7.0 beta 4 (7A165t) )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import Security | |
class Keychain { | |
class func save(key: String, data: NSData) -> Bool { | |
let query = [ | |
kSecClass as String : kSecClassGenericPassword as String, | |
kSecAttrAccount as String : key, | |
kSecValueData as String : data ] | |
SecItemDelete(query as CFDictionaryRef) | |
let status: OSStatus = SecItemAdd(query as CFDictionaryRef, nil) | |
return status == noErr | |
} | |
class func load(key: String) -> NSData? { | |
let query = [ | |
kSecClass as String : kSecClassGenericPassword, | |
kSecAttrAccount as String : key, | |
kSecReturnData as String : kCFBooleanTrue, | |
kSecMatchLimit as String : kSecMatchLimitOne ] | |
var dataTypeRef: AnyObject? | |
let status = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query, UnsafeMutablePointer($0)) } | |
if status == errSecSuccess { | |
if let data = dataTypeRef as! NSData? { | |
return data | |
} | |
} | |
return nil | |
} | |
class func delete(key: String) -> Bool { | |
let query = [ | |
kSecClass as String : kSecClassGenericPassword, | |
kSecAttrAccount as String : key ] | |
let status: OSStatus = SecItemDelete(query as CFDictionaryRef) | |
return status == noErr | |
} | |
class func clear() -> Bool { | |
let query = [ kSecClass as String : kSecClassGenericPassword ] | |
let status: OSStatus = SecItemDelete(query as CFDictionaryRef) | |
return status == noErr | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import XCTest | |
class KeychainTests: XCTestCase { | |
override func setUp() { | |
super.setUp() | |
Keychain.clear() | |
} | |
override func tearDown() { | |
Keychain.clear() | |
super.tearDown() | |
} | |
func testSaveLoad() { | |
let key1 = "testSaveLoadKey1" | |
let key2 = "testSaveLoadKey2" | |
let saveData = "data".dataValue | |
XCTAssertTrue(Keychain.load(key1) == nil) | |
XCTAssertTrue(Keychain.load(key2) == nil) | |
XCTAssertTrue(Keychain.save(key1, data: saveData)) | |
XCTAssertTrue(Keychain.load(key1) != nil) | |
XCTAssertTrue(Keychain.load(key2) == nil) | |
let loadData = Keychain.load(key1)! | |
XCTAssertEqual(loadData.stringValue, saveData.stringValue) | |
} | |
func testDelete() { | |
let key1 = "testDeleteKey1" | |
let key2 = "testDeleteKey2" | |
let saveData = "testDeleteData".dataValue | |
XCTAssertTrue(Keychain.save(key1, data: saveData)) | |
XCTAssertTrue(Keychain.save(key2, data: saveData)) | |
XCTAssertTrue(Keychain.load(key1) != nil) | |
XCTAssertTrue(Keychain.load(key2) != nil) | |
XCTAssertTrue(Keychain.delete(key1)) | |
XCTAssertTrue(Keychain.load(key1) == nil) | |
XCTAssertTrue(Keychain.load(key2) != nil) | |
} | |
func testClear() { | |
let key = "testClearKey" | |
let data = "testClearData".dataValue | |
Keychain.save(key, data: data) | |
XCTAssertTrue(Keychain.load(key) != nil) | |
Keychain.clear() | |
XCTAssertTrue(Keychain.load(key) == nil) | |
} | |
} | |
extension String { | |
public var dataValue: NSData { | |
return dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! | |
} | |
} | |
extension NSData { | |
public var stringValue: String { | |
return NSString(data: self, encoding: NSUTF8StringEncoding)! | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@jackreichert Thanks so much!