import UIKit | |
import Security | |
let serviceIdentifier = "com.company" | |
let accessGroup = "com.company.app" | |
let kSecClassValue = kSecClass as NSString | |
let kSecAttrAccountValue = kSecAttrAccount as NSString | |
let kSecValueDataValue = kSecValueData as NSString | |
let kSecClassGenericPasswordValue = kSecClassGenericPassword as NSString | |
let kSecAttrServiceValue = kSecAttrService as NSString | |
let kSecMatchLimitValue = kSecMatchLimit as NSString | |
let kSecReturnDataValue = kSecReturnData as NSString | |
let kSecMatchLimitOneValue = kSecMatchLimitOne as NSString | |
class KeychainService: NSObject { | |
class func setString(value: NSString, forKey: String) { | |
self.save(serviceIdentifier, key: forKey, data: value) | |
} | |
class func stringForKey(key: String) -> NSString? { | |
var token = self.load(serviceIdentifier, key: key) | |
return token | |
} | |
class func removeItemForKey(key: String) { | |
self.save(serviceIdentifier, key: key, data: "") | |
} | |
class func save(service: NSString, key: String, data: NSString) { | |
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! | |
// Instantiate a new default keychain query | |
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) | |
// Delete any existing items | |
SecItemDelete(keychainQuery as CFDictionaryRef) | |
if data == "" { return } | |
// Add the new keychain item | |
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil) | |
} | |
class func load(service: NSString, key: String) -> NSString? { | |
// Instantiate a new default keychain query | |
// Tell the query to return a result | |
// Limit our results to one item | |
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) | |
var dataTypeRef :Unmanaged<AnyObject>? | |
// Search for the keychain items | |
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) | |
let opaque = dataTypeRef?.toOpaque() | |
var contentsOfKeychain: NSString? | |
if let op = opaque? { | |
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue() | |
// Convert the data retrieved from the keychain into a string | |
contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding) | |
} else { | |
return nil | |
} | |
return contentsOfKeychain | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
@dvdowns I've updated the code in my post to use Swift's new access control modifiers, along with the changes in this gist. Please let me know if it works for you :) (I haven't had a chance to grab the latest beta version yet) |
This comment has been minimized.
This comment has been minimized.
This solution is having issues in beta 6. You can only access kSecClass.__conversion() etc. |
This comment has been minimized.
This comment has been minimized.
Now, __conversion() is also removed. |
This comment has been minimized.
This comment has been minimized.
I updated the code for XCode 6.0 GM -- however I'm running into an issue where it only works when building for Debug not for Release, tracking that down |
This comment has been minimized.
This comment has been minimized.
OK fair warning, the code above only works if the Swift Compiler - Code Generation level is set at "none", not at "Fastest" which is the default Make sure to test the code against your release scheme |
This comment has been minimized.
This comment has been minimized.
I can confirm the issue @alexbosworth mentioned exists in xcode 6.1 as well. Nice catch dude! |
This comment has been minimized.
This comment has been minimized.
I have developed a Swift wrapper over the entire C Keychain API. See if it can be useful for you: https://github.com/deniskr/KeychainSwiftAPI |
This comment has been minimized.
This comment has been minimized.
Another note about this, if you are using this code and you want the keys to be accessible from the lockscreen, like in a background fetch, you need to adjust the kSecAttrAccessible value |
This comment has been minimized.
This comment has been minimized.
up! |
This comment has been minimized.
Having issues with KeychainService.saveToken("token") and KeychainService.loadToken() from Matt Palmer's original post. When trying to implement saveToken, it basically tells me that is not found in KeychainService. If the replacements made also changed the calls, could you point me to those. Thanks.