Save and Load from Keychain | Swift

Save and Load from KeyChain | Swift

##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)
}

Save and retrieve value via KeyChain

Well, I just used out source etc and made my self nice helper :
Enjoy!

 class func save(key: String, data: NSData) {
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)

}

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 :Unmanaged<AnyObject>?

let status: OSStatus = SecItemCopyMatching(query, &dataTypeRef)

if status == noErr {
return (dataTypeRef!.takeRetainedValue() as! NSData)
} else {
return nil
}


}

class func stringToNSDATA(string : String)->NSData
{
let _Data = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)
return _Data!

}


class func NSDATAtoString(data: NSData)->String
{
var returned_string : String = NSString(data: data, encoding: NSUTF8StringEncoding)! as String
return returned_string
}

class func intToNSDATA(r_Integer : Int)->NSData
{

var SavedInt: Int = r_Integer
let _Data = NSData(bytes: &SavedInt, length: sizeof(Int))
return _Data

}
class func NSDATAtoInteger(_Data : NSData) -> Int
{
var RecievedValue : Int = 0
_Data.getBytes(&RecievedValue, length: sizeof(Int))
return RecievedValue

}
class func CreateUniqueID() -> String
{
var uuid: CFUUIDRef = CFUUIDCreate(nil)
var cfStr:CFString = CFUUIDCreateString(nil, uuid)

var nsTypeString = cfStr as NSString
var swiftString:String = nsTypeString as String
return swiftString
}

//EXAMPLES
//
// //Save And Parse Int


// var Int_Data = KeyChain.intToNSDATA(555)
// KeyChain.save("MAMA", data: Int_Data)
// var RecievedDataAfterSave = KeyChain.load("MAMA")
// var NSDataTooInt = KeyChain.NSDATAtoInteger(RecievedDataAfterSave!)
// println(NSDataTooInt)
//
//
// //Save And Parse String


// var string_Data = KeyChain.stringToNSDATA("MANIAK")
// KeyChain.save("ZAHAL", data: string_Data)
// var RecievedDataStringAfterSave = KeyChain.load("ZAHAL")
// var NSDATAtoString = KeyChain.NSDATAtoString(RecievedDataStringAfterSave!)
// println(NSDATAtoString)

Store & Load [Uint8] to & from Keychain (Swift)

You're overcomplicating things. Your issue lies here:

let result = receivedData.to(type: Int.self)

You were storing an [UInt8] type that you're now converting to an Int type.

You can get your array back by doing:

let result = [UInt8](receivedData)

How to load and save data? - Swift

UserDefaults is a common tool to use for this. It stores persistent data in a key-value store.

You could use it like this:

class Favorites: ObservableObject {

private var task: Set<Int>

private let saveKey = "Favorites"

init() {
self.task = Set((UserDefaults.standard.array(forKey: saveKey) as? [Int]) ?? [])
}

func contains(_ sclocations: SClocation) -> Bool {
task.contains(sclocations.id)
}

func add(_ sclocations: SClocation) -> Bool {
objectWillChange.send()
task.insert(sclocations.id)
save()
return true
}

func remove(_ sclocations: SClocation) -> Bool {
objectWillChange.send()
task.remove(sclocations.id)
save()
return true
}

func save() {
// Write out Data
UserDefaults.standard.setValue(Array(task), forKey: saveKey)
}
}

I'd also probably refactor your code a little bit. Using didSet on a @Published variable seems cleaner. Also, you have unnecessary Bool returns on your add/remove functions.

class Favorites: ObservableObject {
@Published var task: Set<Int> {
didSet {
save()
}
}

private let saveKey = "Favorites"

init() {
self.task = Set((UserDefaults.standard.array(forKey: saveKey) as? [Int]) ?? [])
}

func contains(_ sclocations: SClocation) -> Bool {
task.contains(sclocations.id)
}

func add(_ location: SClocation) {
task.insert(location.id)
}

func remove(_ location: SClocation) {
task.remove(location.id)
}

func save() {
UserDefaults.standard.setValue(Array(task), forKey: saveKey)
}
}

You could even get rid of the add/remove functions above and just call insert and remove directly on the task @Published property.


Another option besides using UserDefaults would be to look into Core Data (https://developer.apple.com/documentation/coredata) -- there are plenty of tutorials around for that -- it would be a longer and more involved answer for how to integrate this, though.

Adding Items to and Querying the iOS Keychain with Swift

In order to get this to work, you will need to retrieve the retained values of the keychain constants and store then first like so:

let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString

You can then reference the values in the NSMutableDictionary like so:

var keychainQuery: NSMutableDictionary = NSMutableDictionary(
objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue],
forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]
)

I wrote a blog post about it at:
http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

Hope this helps!

rshelby

Swift 3 How to save and load data

Best way to save scores is by using NSUserDefaults
To save settings such as volume, you can follow the model I have below.

//When you tap on the mute button, execute this code.


if soundSwitch { //true

UserDefaults.standard.set(false, forKey: "SoundSwitch")
soundSwitch = false

} else { //false

UserDefaults.standard.set(true, forKey: "SoundSwitch")
soundSwitch = true

}

If you are saving players money, name and all this other stuff, it would be better to save them in some kind of database. I'm not sure if you can do that with GameCenter.



Related Topics



Leave a reply



Submit