SpaceDog SpaceDog - 2 months ago 10
iOS Question

KeychainItemWrapper returning NSZeroData?

I am storing passwords on keychain using Apple's

KeychainItemWrapper
. At some point, if I try to fetch a password that was not setup yet using

KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:identifier accessGroup:nil];
NSString *password = [keychain objectForKey:kSecValueData];


password returns with this identification on the debugger

password = (_NSZeroData *) class name = _NSZeroData


If I let the code go on and try to use this value it will crash.

This value is not a nil. If I test using
if (!password)
it will fail... and is not a string empty value (it will crash if I try to test it as a NSString).

How do I test this thing to see if it is valid before proceeding?

Answer

I discovered the problem and by the way, I hate with passion the crappy documentation Apple writes about everything.

This is the problem. Despite the documentation suggesting that you should store passwords like plain strings like this using kSecValueData:

  NSString *myPassword = @"12345";
  KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:identifier accessGroup:nil];
  [keychain setObject:myPassword forKey:(__bridge id)kSecValueData];

you should never do that. In fact kSecValueData, as the name suggests and the documentation should say, expects a NSData object. So, you must convert the NSString to NSData before storing it, like this:

  NSString *myPassword = @"12345";
  KeychainItemWrapper* keychain = [[KeychainItemWrapper alloc] initWithIdentifier:identifier accessGroup:nil];
  NSData *myPasswordData = [myPassword dataUsingEncoding:NSUTF8StringEncoding];
  [keychain setObject:myPasswordData forKey:(__bridge id)kSecValueData];

when getting it back you must cast it to NSData and convert it back to NSString:

  NSData *myPasswordData = (NSData *)[keychain objectForKey:(__bridge id)kSecValueData];

  NSString *myPassword = [[NSString alloc] initWithData: myPasswordData
                                           encoding:NSUTF8StringEncoding];