This question already has answers here:
Save and retrieve value via KeyChain
(7 answers)
Closed 6 years ago.
How to simply store a String in Keychain and load when needed.
There are several SO solution which mostly refers to Git repo. But I need the smallest and the simplest solution on latest Swift. Certainly, I don't want to add git framework for simply storing a password in my project.
There are similar solution Save and retrieve value via KeyChain , which did not work for me. Tired with compiler errors.
Simplest Source
import Foundation
import Security
// Constant Identifiers
let userAccount = "AuthenticatedUser"
let accessGroup = "SecuritySerivice"
/**
* User defined keys for new entry
* Note: add new keys for new secure item and use them in load and save methods
*/
let passwordKey = "KeyForPassword"
// Arguments for the keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
public class KeychainService: NSObject {
/**
* Exposed methods to perform save and load queries.
*/
public class func savePassword(token: NSString) {
self.save(passwordKey, data: token)
}
public class func loadPassword() -> NSString? {
return self.load(passwordKey)
}
/**
* Internal methods for querying the keychain.
*/
private class func save(service: NSString, data: NSString) {
let dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
SecItemAdd(keychainQuery as CFDictionaryRef, nil)
}
private class func load(service: NSString) -> NSString? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
var dataTypeRef :AnyObject?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: NSString? = nil
if status == errSecSuccess {
if let retrievedData = dataTypeRef as? NSData {
contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
Example of Calling
KeychainService.savePassword("Pa55worD")
let password = KeychainService.loadPassword() // password = "Pa55worD"
SWIFT 4: VERSION WITH UPDATE AND REMOVE PASSWORD
import Cocoa
import Security
// see https://stackoverflow.com/a/37539998/1694526
// Arguments for the keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
public class KeychainService: NSObject {
class func updatePassword(service: String, account:String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Read failed: \(err)")
}
}
}
}
class func removePassword(service: String, account:String) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue])
// Delete any existing items
let status = SecItemDelete(keychainQuery as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Remove failed: \(err)")
}
}
}
class func savePassword(service: String, account:String, data: String) {
if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
// Add the new keychain item
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
if (status != errSecSuccess) { // Always check the status
if let err = SecCopyErrorMessageString(status, nil) {
print("Write failed: \(err)")
}
}
}
}
class func loadPassword(service: String, account:String) -> String? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
var dataTypeRef :AnyObject?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: String?
if status == errSecSuccess {
if let retrievedData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
You need to imagine the following wired up to a text input field and a label, then having four buttons wired up, one for each of the methods.
class ViewController: NSViewController {
#IBOutlet weak var enterPassword: NSTextField!
#IBOutlet weak var retrievedPassword: NSTextField!
let service = "myService"
let account = "myAccount"
// will only work after
#IBAction func updatePassword(_ sender: Any) {
KeychainService.updatePassword(service: service, account: account, data: enterPassword.stringValue)
}
#IBAction func removePassword(_ sender: Any) {
KeychainService.removePassword(service: service, account: account)
}
#IBAction func passwordSet(_ sender: Any) {
let password = enterPassword.stringValue
KeychainService.savePassword(service: service, account: account, data: password)
}
#IBAction func passwordGet(_ sender: Any) {
if let str = KeychainService.loadPassword(service: service, account: account) {
retrievedPassword.stringValue = str
}
else {retrievedPassword.stringValue = "Password does not exist" }
}
}
Swift 5
Kosuke's version for Swift 5
import Security
import UIKit
class KeyChain {
class func save(key: String, data: Data) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ] as [String : Any]
SecItemDelete(query as CFDictionary)
return SecItemAdd(query as CFDictionary, nil)
}
class func load(key: String) -> Data? {
let query = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
kSecReturnData as String : kCFBooleanTrue!,
kSecMatchLimit as String : kSecMatchLimitOne ] as [String : Any]
var dataTypeRef: AnyObject? = nil
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == noErr {
return dataTypeRef as! Data?
} else {
return nil
}
}
class func createUniqueID() -> String {
let uuid: CFUUID = CFUUIDCreate(nil)
let cfStr: CFString = CFUUIDCreateString(nil, uuid)
let swiftString: String = cfStr as String
return swiftString
}
}
extension Data {
init<T>(from value: T) {
var value = value
self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
func to<T>(type: T.Type) -> T {
return self.withUnsafeBytes { $0.load(as: T.self) }
}
}
Example usage:
let int: Int = 555
let data = Data(from: int)
let status = KeyChain.save(key: "MyNumber", data: data)
print("status: ", status)
if let receivedData = KeyChain.load(key: "MyNumber") {
let result = receivedData.to(type: Int.self)
print("result: ", result)
}
Related
I'm trying to use some keychain code to securely and persistently store some sensitive data. I have functions to save and read data which work fine but I cannot seem to get the update one to work.
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
public class KeychainService: NSObject {
class func updatePassword(service: String, account:String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Read failed: \(err)")
print("status: \(status)")
}
}
}
}
...
I am constantly getting error status -50 indicating one or more parameters passed to the function were not valid. I tried reading through the docs but couldn't come up with anything that worked.
Thank you in advance!
It turned out that I had to remove the last two keys from the keychainQuery dictionary so it looks like this:
public class KeychainService: NSObject {
class func updatePassword(service: String, account:String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Read failed: \(err)")
print("status: \(status)")
}
}
}
}
...
I am trying to create VPN connection in my app. I go through this link
https://developer.apple.com/documentation/networkextension/nevpnmanager
but did not find any official code to use NEVPNManager and even not found any tutorial to use this NEVPNManager.
I am new to VPN concept and don't know that much about it. so Can anyone give some solutions?
Here is a IKEv2 configuration of VPN using Network Extension (Without shared key and certificate) in Swift 4.2:
final class VPNHandler {
let vpnManager = NEVPNManager.shared()
func initVPNTunnelProviderManager() {
print("CALL LOAD TO PREFERENCES...")
self.vpnManager.loadFromPreferences { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 1")
} else {
let IKEv2Protocol = NEVPNProtocolIKEv2()
IKEv2Protocol.username = vpnUser.username
IKEv2Protocol.serverAddress = vpnServer.serverID //server tunneling Address
IKEv2Protocol.remoteIdentifier = vpnServer.remoteID //Remote id
IKEv2Protocol.localIdentifier = vpnUser.localID //Local id
IKEv2Protocol.deadPeerDetectionRate = .low
IKEv2Protocol.authenticationMethod = .none
IKEv2Protocol.useExtendedAuthentication = true //if you are using sharedSecret method then make it false
IKEv2Protocol.disconnectOnSleep = false
//Set IKE SA (Security Association) Params...
IKEv2Protocol.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256
IKEv2Protocol.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA256
IKEv2Protocol.ikeSecurityAssociationParameters.diffieHellmanGroup = .group14
IKEv2Protocol.ikeSecurityAssociationParameters.lifetimeMinutes = 1440
//IKEv2Protocol.ikeSecurityAssociationParameters.isProxy() = false
//Set CHILD SA (Security Association) Params...
IKEv2Protocol.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256
IKEv2Protocol.childSecurityAssociationParameters.integrityAlgorithm = .SHA256
IKEv2Protocol.childSecurityAssociationParameters.diffieHellmanGroup = .group14
IKEv2Protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440
let kcs = KeychainService()
//Save password in keychain...
kcs.save(key: "VPN_PASSWORD", value: vpnUser.password)
//Load password from keychain...
IKEv2Protocol.passwordReference = kcs.load(key: "VPN_PASSWORD")
self.vpnManager.protocolConfiguration = IKEv2Protocol
self.vpnManager.localizedDescription = "Safe Login Configuration"
self.vpnManager.isEnabled = true
self.vpnManager.isOnDemandEnabled = true
//print(IKEv2Protocol)
//Set rules
var rules = [NEOnDemandRule]()
let rule = NEOnDemandRuleConnect()
rule.interfaceTypeMatch = .any
rules.append(rule)
print("SAVE TO PREFERENCES...")
//SAVE TO PREFERENCES...
self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 2")
} else {
print("CALL LOAD TO PREFERENCES AGAIN...")
//CALL LOAD TO PREFERENCES AGAIN...
self.vpnManager.loadFromPreferences(completionHandler: { (error) in
if ((error) != nil) {
print("VPN Preferences error: 2")
} else {
var startError: NSError?
do {
//START THE CONNECTION...
try self.vpnManager.connection.startVPNTunnel()
} catch let error as NSError {
startError = error
print(startError.debugDescription)
} catch {
print("Fatal Error")
fatalError()
}
if ((startError) != nil) {
print("VPN Preferences error: 3")
//Show alert here
print("title: Oops.., message: Something went wrong while connecting to the VPN. Please try again.")
print(startError.debugDescription)
} else {
//self.VPNStatusDidChange(nil)
print("Starting VPN...")
}
}
})
}
})
}
} //END OF .loadFromPreferences //
}
//MARK:- Connect VPN
static func connectVPN() {
VPNHandler().initVPNTunnelProviderManager()
}
//MARK:- Disconnect VPN
static func disconnectVPN() {
VPNHandler().vpnManager.connection.stopVPNTunnel()
}
//MARK:- check connection staatus
static func checkStatus() {
let status = VPNHandler().vpnManager.connection.status
print("VPN connection status = \(status.rawValue)")
switch status {
case NEVPNStatus.connected:
print("Connected")
case NEVPNStatus.invalid, NEVPNStatus.disconnected :
print("Disconnected")
case NEVPNStatus.connecting , NEVPNStatus.reasserting:
print("Connecting")
case NEVPNStatus.disconnecting:
print("Disconnecting")
default:
print("Unknown VPN connection status")
}
}
}
Code for keychain:
//MARK:- Variables for keychain access
// Identifiers
let serviceIdentifier = "MySerivice"
let userAccount = "authenticatedUser"
let accessGroup = "MySerivice"
// Arguments for the keychain queries
var kSecAttrAccessGroupSwift = NSString(format: kSecClass)
let kSecClassValue = kSecClass as CFString
let kSecAttrAccountValue = kSecAttrAccount as CFString
let kSecValueDataValue = kSecValueData as CFString
let kSecClassGenericPasswordValue = kSecClassGenericPassword as CFString
let kSecAttrServiceValue = kSecAttrService as CFString
let kSecMatchLimitValue = kSecMatchLimit as CFString
let kSecReturnDataValue = kSecReturnData as CFString
let kSecMatchLimitOneValue = kSecMatchLimitOne as CFString
let kSecAttrGenericValue = kSecAttrGeneric as CFString
let kSecAttrAccessibleValue = kSecAttrAccessible as CFString
class KeychainService: NSObject {
func save(key:String, value:String) {
let keyData: Data = key.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let valueData: Data = value.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let keychainQuery = NSMutableDictionary();
keychainQuery[kSecClassValue as! NSCopying] = kSecClassGenericPasswordValue
keychainQuery[kSecAttrGenericValue as! NSCopying] = keyData
keychainQuery[kSecAttrAccountValue as! NSCopying] = keyData
keychainQuery[kSecAttrServiceValue as! NSCopying] = "VPN"
keychainQuery[kSecAttrAccessibleValue as! NSCopying] = kSecAttrAccessibleAlwaysThisDeviceOnly
keychainQuery[kSecValueData as! NSCopying] = valueData;
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionary)
SecItemAdd(keychainQuery as CFDictionary, nil)
}
func load(key: String)->Data {
let keyData: Data = key.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let keychainQuery = NSMutableDictionary();
keychainQuery[kSecClassValue as! NSCopying] = kSecClassGenericPasswordValue
keychainQuery[kSecAttrGenericValue as! NSCopying] = keyData
keychainQuery[kSecAttrAccountValue as! NSCopying] = keyData
keychainQuery[kSecAttrServiceValue as! NSCopying] = "VPN"
keychainQuery[kSecAttrAccessibleValue as! NSCopying] = kSecAttrAccessibleAlwaysThisDeviceOnly
keychainQuery[kSecMatchLimit] = kSecMatchLimitOne
keychainQuery[kSecReturnPersistentRef] = kCFBooleanTrue
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) { SecItemCopyMatching(keychainQuery, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let data = result as! NSData? {
if let value = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue) {
//print(value)
}
return data as Data;
}
}
return "".data(using: .utf8)!;
}
}
This tutorial help me to create VPN connection.
http://blog.moatazthenervous.com/create-a-vpn-connection-with-apple-swift/
VPN file for connection and disconnection
import Foundation
import NetworkExtension
// MARK: - NEVPNManager
// MARK: -
private var vpnLoadHandler: (Error?) -> Void { return
{ (error:Error?) in
if ((error) != nil) {
print("Could not load VPN Configurations")
self.removeToast()
return;
}
self.showToast(msg: STRINGVALUES.kCreatingConnection)
//VPN connection via Username password
let p = NEVPNProtocolIPSec()
let kcs = KeychainService()
p.authenticationMethod = NEVPNIKEAuthenticationMethod.sharedSecret
//For the security purpose added word xyz in password .so it should be remove while connecting
if self.selectedSever != nil{
self.selectedSever?.password = (self.selectedSever?.password.replacingOccurrences(of: "xyz", with: ""))!
p.username = self.selectedSever?.userName
p.serverAddress = self.selectedSever?.serverAddress
kcs.save(key: "SHARED", value: (self.selectedSever?.password)!)
kcs.save(key: "VPN_PASSWORD", value: (self.selectedSever?.password)!)
p.sharedSecretReference = kcs.load(key: STRINGVALUES.kShared)
p.passwordReference = kcs.load(key: STRINGVALUES.kVPN_Pswd)
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
// Check for free subscriber
if self.selectedSever?.serverType == STRINGVALUES.VIP.lowercased() && !Singleton.checkForPaidReciept(){
self.disconnectVPN()
Helper.showAlert(sender: self, title: STRINGVALUES.AppName, message: AlertMessage.kValidateSubscription)
return
}
self.vpnManager.protocolConfiguration = p
self.vpnManager.localizedDescription = STRINGVALUES.AppName
self.vpnManager.isEnabled = true
self.vpnManager.saveToPreferences(completionHandler: self.vpnSaveHandler)
}else{
}
}
}
private var vpnSaveHandler: (Error?) -> Void { return
{ (error:Error?) in
if (error != nil) {
print("Could not save VPN Configurations")
self.removeToast()
return
} else {
do {
try self.vpnManager.connection.startVPNTunnel()
} catch let error {
print("Error starting VPN Connection \(error.localizedDescription)");
self.removeToast()
}
}
}
//self.vpnlock = false
}
public func connectVPN() {
//For no known reason the process of saving/loading the VPN configurations fails.On the 2nd time it works
do {
try self.vpnManager.loadFromPreferences(completionHandler: self.vpnLoadHandler)
} catch let error {
print("Could not start VPN Connection: \(error.localizedDescription)" )
self.removeToast()
}
}
public func disconnectVPN() ->Void {
vpnManager.connection.stopVPNTunnel()
}
func vpnConnectionStatusChanged(){
let status = self.vpnManager.connection.status
print("VPN connection status = \(status)")
switch status {
case NEVPNStatus.connected:
showToast(msg: STRINGVALUES.kConnected)
case NEVPNStatus.invalid, NEVPNStatus.disconnected :
showToast(msg: STRINGVALUES.kDisconnected)
case NEVPNStatus.connecting , NEVPNStatus.reasserting:
showToast(msg: STRINGVALUES.kConnecting)
case NEVPNStatus.disconnecting:
showToast(msg: STRINGVALUES.kDisconnecting)
default:
print("Unknown VPN connection status")
}
}
I want to remove keychain value form my service, when user has logout. However, I feel confused about the best practice way to do that.
Here is my service
let userAccount = "AuthenticatedUser"
let accessGroup = "SecuritySerivice"
let passwordKey = "KeyForPassword"
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
public class KeychainService: NSObject {
public class func savePassword(token: String) {
self.save(service: passwordKey, data: token)
}
public class func loadPassword() -> String? {
return self.load(service: passwordKey)
}
public class func removePassword() {
}
private class func save(service: String, data: String) {
let dataFromString: Data = data.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
SecItemDelete(keychainQuery as CFDictionary)
SecItemAdd(keychainQuery as CFDictionary, nil)
}
private class func load(service: String) -> String? {
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
var dataTypeRef :AnyObject?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: String? = nil
if status == errSecSuccess {
if let retrievedData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8)
}
} else {
print("KEY: Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
private class func remove(service: String) {
}
}
Obviously, I'm not shure whether I should remove anything form keychain actually
If you want to delete the item using KeychainItemWrapper, use -resetKeychainItem. This calls SecItemDelete() with the correct value.
To remove username password in Keychain use
func removeUserFromKeychain() {
let spec: NSDictionary = [kSecClass: kSecClassGenericPassword]
SecItemDelete(spec)
}
You can clear your data. Please try below lines
keychain["yourKey"] = nil
or
do {
try keychain.remove("yourKey")
} catch let error {
print("error: \(error)")
}
Thanks
I am trying to save an array in keychain but I can't convert an array to NSData. I have my function prepared for saving strings but I don't know how to get a valueData from an array.
func add(key: String, value: AnyObject) {
let service = NSBundle.mainBundle().bundleIdentifier!
let valueData: NSData! = value.dataUsingEncoding(NSUTF8StringEncoding,
allowLossyConversion: false)
let secItem = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrService as String : service,
kSecAttrAccount as String : key,
kSecValueData as String : valueData
]
let result: UnsafeMutablePointer<AnyObject?> = nil
let status = Int(SecItemAdd(secItem, result))
if status == Int(errSecDuplicateItem){
self.update(key, newData: value)
} else {
print("An error occurred with code \(status)")
}
}
You can use NSKeyedArchiver and NSKeyedUnarchiver to convert NSArrays and NSDictionaries to data:
let array : NSArray = ["one", "two"]
let data = NSKeyedArchiver.archivedDataWithRootObject(array)
let arrayFromData = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! NSArray
You can use NSKeyedArchiver.archivedDataWithRootObject on your array when saving, and NSKeyedUnarchiver.unarchiveObjectWithData when loading.
Providing the solution with the new methods in swift 5 code.
The provided answers are using deprecated methods.
let array : NSArray = ["one", "two"]
var data = Data()
do {
data = try NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true)
} catch {
print("archivedData Error");
}
do {
let arrayFromData = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data)
} catch {
print("unarchivedObject Error");
}
I have used AddessBook library (for ios8) to get all contact (firstname, lastname, contactId, all phone number). it worked perfect on simulitor.
my code:
private func getContacts()->[Person] {
var peapleOfContact: [Person] = []
if !self.determineStatus() {
return peapleOfContact
}
if let people = ABAddressBookCopyArrayOfAllPeople(self.adbk)?.takeRetainedValue() as? NSArray {
for person in people{
let contactID = ABRecordGetRecordID(person)
let firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as! String
let lastName = ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as! String
let personOfContact = Person(id: String(contactID), firstName: firstName, lastName: lastName)
let numbers:ABMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty).takeRetainedValue()
for ix in 0 ..< ABMultiValueGetCount(numbers) {
let type = ABMultiValueCopyLabelAtIndex(numbers,ix).takeRetainedValue() as String
let number = ABMultiValueCopyValueAtIndex(numbers,ix).takeRetainedValue() as! String
let cleaned = self.removeSpecialCharsFromString(type)
let shortNumber = self.makeSpecialShortNumberFromString(number)
let phone = Number(number: number, short: shortNumber, type: cleaned)
personOfContact.addPhoneNumber(phone)
}
peapleOfContact.append(personOfContact)
}
}
return peapleOfContact
}
when xcode 7.1.1 is updated and used ios9, this library not work so used ContactUI Framework.
my code:
private func getContact(){
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
CNContactImageDataKey,
CNContactPhoneNumbersKey]
let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
do {
try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (let contact, let stop) -> Void in
self.contacts.append(contact)
})
}
catch let error as NSError {
print(error.localizedDescription)
}
}
and used like this:
self.getContact()
for contact in self.contacts{
print("Full name: \(CNContactFormatter.stringFromContact(contact, style: .FullName))")
print("Give name: \(contact.givenName)")
print("fimily name: \(contact.familyName)")
print("Idnetifier name: \(contact.identifier)")
for phoneNo in contact.phoneNumbers {
if phoneNo.label == CNLabelPhoneNumberMobile {
let number = (phoneNo.value as! CNPhoneNumber).stringValue
print("Phone Number: \(number)")
}
}
}
but not found contactId. How to get contactId ? How to use ContactUI Framework ?
advise me. thanks
In iOS 9.*, there is a new framework called 'Contacts'
Usage:
import Contacts
var validContacts: [CNContact] = []
let contactStore = CNContactStore()
// Request for contact access
contactStore.requestAccessForEntityType(.Contacts) { (granted, e) -> Void in
if granted {
do {
// Specify the key fields that you want to be fetched.
// Note: if you didn't specify your specific field request. your app will crash
let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactMiddleNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataKey, CNContactThumbnailImageDataKey])
try contactStore.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (contact, error) -> Void in
// Lets filter (optional)
if !contact.emailAddresses.isEmpty || !contact.phoneNumbers.isEmpty {
validContacts.append(contact)
}
})
print(validContacts)
}catch let e as NSError {
print(e)
}
}
}
Parsing:
// Loop through contatcs
for contact in validContacts {
var phoneNumbers: [String] = []
for phoneNumber in contact.phoneNumbers {
let value = phoneNumber.value as! CNPhoneNumber
phoneNumbers.append(value.stringValue)
}
var emailAddresses: [String] = []
for emailAddress in contact.emailAddresses {
let value = emailAddress.value as! String
emailAddresses.append(value)
}
if let imageData = contact.imageData {
let image = UIImage(data: imageData)
print("image: \(image)")
}
// Lets log
print("givenName: \(contact.givenName), middleName: \(contact.middleName), familyName: \(contact.familyName), phoneNumbers: \(phoneNumbers), emailAddresses: \(emailAddresses)\n")
}
I have a sample project which implement this.
GitHub
This code sample will help you :
public static func refreshContacts(){
let status = ABAddressBookGetAuthorizationStatus()
if status == .Denied || status == .Restricted {
// user previously denied, to tell them to fix that in settings
return
}
// open it
var error: Unmanaged<CFError>?
let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue()
if addressBook == nil {
println(error?.takeRetainedValue())
return
}
// request permission to use it
ABAddressBookRequestAccessWithCompletion(addressBook) {
granted, error in
if !granted {
// warn the user that because they just denied permission, this functionality won't work
// also let them know that they have to fix this in settings
return
}
if let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as? NSArray {
for person in people{
if let name = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as? String {
println(name)//persons name
}
let numbers:ABMultiValue = ABRecordCopyValue(
person, kABPersonPhoneProperty).takeRetainedValue()
for ix in 0 ..< ABMultiValueGetCount(numbers) {
let label = ABMultiValueCopyLabelAtIndex(numbers,ix).takeRetainedValue() as String
let value = ABMultiValueCopyValueAtIndex(numbers,ix).takeRetainedValue() as! String
println("Phonenumber \(label) is \(value))
}
}
}
}
}
Please try this working for me. sort according to givenname(firstname). import Contacts first
let contactStore = CNContactStore()
var results: [CNContact] = []
do {
let request:CNContactFetchRequest
request = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactEmailAddressesKey,CNContactPhoneNumbersKey])
request.sortOrder = CNContactSortOrder.GivenName
try contactStore.enumerateContactsWithFetchRequest(request) {
(contact, cursor) -> Void in
results.append(contact)
}
}
catch{
print("Handle the error please")
}
print(results)