Sauvegarder et récupérer la valeur via le porte-clés

j'essaie de stocker un entier et de le récupérer en utilisant un porte-clés.

c'est de Cette façon que je sauvegarde:

func SaveNumberOfImagesTaken()
    {
        let key = "IMAGE_TAKEN"
        var taken = 10
        let data = NSKeyedArchiver.archivedDataWithRootObject(taken)
        let query : [String:AnyObject] = [
            kSecClass as String : kSecClassGenericPassword,
            kSecAttrAccount as String : key,
            kSecValueData as String : data
        ]
        let status : OSStatus = SecItemAdd(query as CFDictionaryRef, nil)

    }

C'est comme ça que j'essaie de le récupérer:

func CheckIfKeyChainValueExitss() -> AnyObject? {
    var key = "IMAGE_TAKEN"
    let query : [String:AnyObject] = [
        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 let op = dataTypeRef?.toOpaque() {
        let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
        if let string: AnyObject? =  NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
            if key == "IMAGE_TAKEN"
            {
                return string as! String!

            }
            else if string == nil
            {
                return nil
            }
        }
    }
    return nil

}

j'obtiens l'erreur suivante:

Pourrait ne pas jeter de valeur de type '__NSCFNumber' à 'NSString'

j'ai essayé de jouer avec les variables, mais sans succès.

7
demandé sur Julian E. 2015-06-09 00:46:50

5 réponses

J'ai mis à jour la version D'Eric pour Swift 3:

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 { "151900920".pointee }
    }
}

exemple d'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)
    }
17
répondu Kosuke Ogawa 2017-02-21 12:18:25

Eh bien, je viens d'utiliser la source etc et fait mon aide auto gentil : Profitez-en!

 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)
4
répondu Roi Mulia 2015-06-12 19:20:19

Roi Mulia réponse fonctionne très bien, voici une version avec quelques adaptations minimales pour la Swift 2:

class KeyChain {
    class func save(key: String, data: NSData) -> OSStatus {
        let query = [
            kSecClass as String       : kSecClassGenericPassword as String,
            kSecAttrAccount as String : key,
            kSecValueData as String   : data ]

        SecItemDelete(query as CFDictionaryRef)

        return 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:AnyObject? = nil

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

        if status == noErr {
            return (dataTypeRef! 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
    {
        let 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
    {
        let uuid: CFUUIDRef = CFUUIDCreate(nil)
        let cfStr:CFString = CFUUIDCreateString(nil, uuid)

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

exemple d'usage:

let data = KeyChain.intToNSDATA(555)
let status = KeyChain.save("MyNumber", data: data)
print(status)

if let receivedData = KeyChain.load("MyNumber") {
    let result = KeyChain.NSDATAtoInteger(receivedData)
    print(result)
}
3
répondu Moritz 2017-05-23 12:26:27

C'est réponse de Sazzad Hissain Khan réécrit pour iOS sans attributs non-Swifty NS-prefixed et un code plus propre.

import Security

class KeychainService {
    class func updatePassword(service: String, account: String, data: String) {
        guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
            return
        }

        let status = SecItemUpdate(modifierQuery(service: service, account: account), [kSecValueData: dataFromString] as CFDictionary)

        checkError(status)
    }

    class func removePassword(service: String, account: String) {
        let status = SecItemDelete(modifierQuery(service: service, account: account))

        checkError(status)
    }

    class func savePassword(service: String, account: String, data: String) {
        guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
            return
        }

        let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
                                              kSecAttrService: service,
                                              kSecAttrAccount: account,
                                              kSecValueData: dataFromString]

        let status = SecItemAdd(keychainQuery as CFDictionary, nil)

        checkError(status)
    }

    class func loadPassword(service: String, account: String) -> String? {
        var dataTypeRef: CFTypeRef?

        let status = SecItemCopyMatching(modifierQuery(service: service, account: account), &dataTypeRef)

        if status == errSecSuccess,
            let retrievedData = dataTypeRef as? Data {
            return String(data: retrievedData, encoding: .utf8)
        } else {
            checkError(status)

            return nil
        }
    }

    fileprivate static func modifierQuery(service: String, account: String) -> CFDictionary {
        let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
                                              kSecAttrService: service,
                                              kSecAttrAccount: account,
                                              kSecReturnData: kCFBooleanTrue]

        return keychainQuery as CFDictionary
    }

    fileprivate static func checkError(_ status: OSStatus) {
        if status != errSecSuccess {
            if #available(iOS 11.3, *),
            let err = SecCopyErrorMessageString(status, nil) {
                print("Operation failed: \(err)")
            } else {
                print("Operation failed: \(status). Check the error message through https://osstatus.com.")
            }
        }
    }
}
2
répondu Tamás Sengel 2018-04-23 09:41:06

vous stockez un nombre, pas une chaîne, donc vous récupérez un nombre NSN, pas une chaîne. L'exception est assez claire - vous ne pouvez pas downcast un NSNumber à une chaîne de caractères - vous pouvez utiliser stringValue() pour obtenir la représentation d'une chaîne de caractères D'un NSNumber

if let op = dataTypeRef?.toOpaque() {
    let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
    if let string: AnyObject? =  NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
        if key == "IMAGE_TAKEN"
        {
            return string.stringValue() as! String!
        }
        else if string == nil
        {
                return nil
        }
    }
}
0
répondu Paulw11 2015-06-08 22:47:08