Save and retrieve value via KeyChain

后端 未结 7 1632
自闭症患者
自闭症患者 2020-12-02 09:51

I\'m trying to store an Integer and retrieve it using KeyChain.

This is how I save it:

func SaveNumberOfImagesTaken()
    {
        let key = \"IMAGE         


        
相关标签:
7条回答
  • 2020-12-02 09:54

    You are storing a number, not a string, so you are getting back an NSNumber, not a string. The exception is pretty clear - you can't downcast an NSNumber to a String - you can use stringValue() to get the string representation of an 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 讨论(0)
  • 2020-12-02 09:57

    I've update Eric's version for Swift 5:

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

    I've update Eric's version for 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 { $0.pointee }
        }
    }
    

    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)
    }
    
    0 讨论(0)
  • 2020-12-02 09:58

    I tried to make it as simple as possible.

    fileprivate class KeychainService {
    
      static func updatePassword(_ password: String, serviceKey: String) {
        guard let dataFromString = password.data(using: .utf8) else { return }
    
        let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
                                               kSecAttrService: serviceKey,
                                               kSecValueData: dataFromString]
        SecItemDelete(keychainQuery as CFDictionary)
        SecItemAdd(keychainQuery as CFDictionary, nil)
      }
    
      static func removePassword(serviceKey: String) {
    
        let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
                                               kSecAttrService: serviceKey]
    
        SecItemDelete(keychainQuery as CFDictionary)
      }
    
      static func loadPassword(serviceKey: String) -> String? {
        let keychainQuery: [CFString : Any] = [kSecClass : kSecClassGenericPassword,
                                               kSecAttrService : serviceKey,
                                               kSecReturnData: kCFBooleanTrue,
                                               kSecMatchLimitOne: kSecMatchLimitOne]
    
        var dataTypeRef: AnyObject?
        SecItemCopyMatching(keychainQuery as CFDictionary, &dataTypeRef)
        guard let retrievedData = dataTypeRef as? Data else { return nil }
    
        return String(data: retrievedData, encoding: .utf8)
      }
    
      static func flush()  {
        let secItemClasses =  [kSecClassGenericPassword]
        for itemClass in secItemClasses {
          let spec: NSDictionary = [kSecClass: itemClass]
          SecItemDelete(spec)
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-02 10:04

    Roi Mulia's answer works very well, here's a version with a few minimal adjustments for 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
        }
    }
    

    Example 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)
    }
    
    0 讨论(0)
  • 2020-12-02 10:06

    This is Sazzad Hissain Khan's answer rewritten for iOS without non-Swifty NS-prefixed attributes and a cleaner code.

    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.")
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 10:10

    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)
    
    0 讨论(0)
提交回复
热议问题