3

I am developing an iOS project with XCode8 + Swift3.

I have created the following two functions to store string to keychain and read it back from keychain:

var query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrService as String: "my service",
    kSecAttrAccount as String: "my-key"
]

func storeString(value: String) -> Bool {
    if let data = value.data(using: .utf8) {
        // delete data if exist
        SecItemDelete(query as CFDictionary)

        // add value to query
        query[kSecValueData as String] = data

        // add to keychain
        let status = SecItemAdd(query as CFDictionary, nil)

        return status == noErr
    }
    return false
}

func readString() -> String? {
    // update query
    query[kSecReturnData as String] = kCFBooleanTrue
    query[kSecMatchLimit as String] = kSecMatchLimit

    var result: AnyObject?
    // fetch items from keychain
    let status: OSStatus = withUnsafeMutablePointer(to: &result) {
        SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
    }

    // I always get error -50 here
    if status == noErr {
        if let resultData = result as? Data {
            if let strVal = String(data: resultData, encoding: .utf8) {
                return strVal
            }
        }
    }
    return nil

}

I invoke the functions:

boolean hasStored = storeString(value: "John")
let readVal = readString()

I got hasStored is true, but readVal always nil. I investigated my functions, I see I always get error -50 as status code in reader function (see my comment in my function).

Why? Why I can't read the value I stored in keychain (I am not sure whether it is really stored but I do get status == noErr always retuns true in storeString(value:) function)

2
  • What happens if you set the value of kSecMatchLimit to kSecMatchLimitOne? Commented Aug 10, 2017 at 21:02
  • @Max, could you please make an answer, I will accept it. It works now after changed to kSecMatchLimitOne. It would be nice if you could also explain a bit why/what makes you think kSecMatchLimitOne could fix the issue (in order to help other people as well) :) Commented Aug 11, 2017 at 8:57

1 Answer 1

5

There is a typo in your code:

query[kSecMatchLimit as String] = kSecMatchLimit
//                                ^~~~~~~~~~~~~~

kSecMatchLimit is the key and not a valid value. The expected value should be a CFNumber or kSecMatchLimitOne or kSecMatchLimitAll. If you expect a single item to be returned, use kSecMatchLimitOne. See also Search Attribute Keys and Values.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.