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
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)")
}
}
}
}
...
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)
}
I have a custom User class which stores the phone number of the user.
class User {
let phoneNumber: String
}
How do I get the corresponding contact from the users contact book?
I tried the following but it seems like this works just for the contacts name because I'm always getting nil:
let predicate = CNContact.predicateForContactsMatchingName(userInstance.phoneNumber)
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactPhoneNumbersKey]
// Is already permitted
try! CNContactStore().unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch).first // This returns nil
I've searched in the docs but I didn't find a proper solution.
let contactStroe = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
contactStroe.requestAccessForEntityType(.Contacts, completionHandler: { (granted, error) -> Void in
if granted {
let predicate = CNContact.predicateForContactsInContainerWithIdentifier(contactStroe.defaultContainerIdentifier())
var contacts: [CNContact]! = []
do {
contacts = try contactStroe.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)// [CNContact]
}catch {
}
for contact in contacts {
var phoneStr = ""
var nameStr = ""
var number: CNPhoneNumber!
if contact.phoneNumbers.count > 0 {
number = contact.phoneNumbers[0].value as! CNPhoneNumber
phoneStr = number.stringValue.stringByReplacingOccurrencesOfString("-", withString: "")
}
nameStr = contact.familyName + contact.givenName
if !nameStr.isEmpty && !phoneStr.isEmpty {
let friend = YFriendsModel()
friend.name = nameStr
friend.phone = phoneStr
self.friendArr.append(friend)
}
}
})
this is my way, you can have a test
You can't.
This is a stupid solution as a huge workaround.
Read each contact
Normalize the phone number (not the easiest thing to do!)
Cache contacts into a [String : Contact]
Then you can lookup contacts with contacts[phone_number]?
Swift 3
A nice solution, taking care also of efficiency:
func getAllContacts() {
let status = CNContactStore.authorizationStatus(for: CNEntityType.contacts) as CNAuthorizationStatus
if status == CNAuthorizationStatus.denied {
self.showAccessContactsDeniedAlert()
return
}
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey] as [Any]
let request = CNContactFetchRequest(keysToFetch:keysToFetch as! [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request, usingBlock: { (contact:CNContact, stop:UnsafeMutablePointer<ObjCBool>) -> Void in
print(contact)
for email in contact.emailAddresses {
var dict = [String:String]()
dict["name"] = contact.familyName + contact.givenName
dict["email"] = email.value
self.allContacts.add(dict)
}
})
} catch {
//catch
}
}
In this case I save name and email into a dict and I add it to a class variable called allContacts.
Note that a contact can have more than one email, so I create a dict for any email address in this case
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)
Communicating with a REST API and saving API token in iOS's keychain. But the keychain code is throwing a nil error.
KeychainAccess.swift:
public class func passwordForAccount(account: String, service: String = "keyChainDefaultService") -> String? {
let queryAttributes = NSDictionary(objects: [secClassGenericPassword(), service, account, true], forKeys: [secClass(), secAttrService(), secAttrAccount(), secReturnData()])
var retrievedData: NSData?
var extractedData: AnyObject?
let status = SecItemCopyMatching(queryAttributes, &extractedData)
if (status == errSecSuccess) {
retrievedData = extractedData as? NSData
}
let password = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding)
return (password as! String)
}
In the above code, retrievedData is nil. If I do print(status), I get -25300. This function is being called from a view controller:
// check if API token has expired
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let userTokenExpiryDate : String? = KeychainAccess.passwordForAccount("Auth_Token_Expiry", service: "KeyChainService")
let dateFromString : NSDate? = dateFormatter.dateFromString(userTokenExpiryDate!)
let now = NSDate()
I am not sure where I'm going wrong here. Any pointers?
var extractedData : AnyObject?;
let status = withUnsafeMutablePointer(&extractedData) {
SecItemCopyMatching(queryAttributes, UnsafeMutablePointer($0))
}
Try doing it like this, this is how I had to get it working