2

I'm finding that sometimes I get that error when trying to get a secure item I previously succesfully stored by using Keychain API. I have found it when the app running in my device was in background state and I locked the screen. The device hasn't lock code set, and this is the function I'm calling:

+ (NSString *)findValueForKey:(NSString *)keyStr
{
   NSString *valueStr = @"";

   if ((keyStr != nil) && (![keyStr isEqualToString:@""])) {
       NSString *service = [[NSBundle mainBundle] bundleIdentifier];

       NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService : service,
                            (__bridge id)kSecAttrAccount : keyStr,
                            (__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue};

       CFDataRef cfValue = NULL;
       OSStatus results = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&cfValue);

       if ([self checkIfNoError:results]) {
           valueStr = [[NSString alloc] initWithData:(__bridge_transfer NSData *)cfValue encoding:NSUTF8StringEncoding];
       }

       else {
          NSLog(@"%@", [self getErrorMessageForStatus:results]);
       }
   }

   return valueStr;
}

and the item was stored by calling this method:

+ (BOOL)storeInKeychainWithKey:(NSString *)keyStr withValueStr:(NSString *)valueStr
{
   if ((keyStr != nil) && (![keyStr isEqualToString:@""]) &&
       (valueStr != nil) && (![valueStr isEqualToString:@""])) {

       NSData *valueData = [valueStr dataUsingEncoding:NSUTF8StringEncoding];
       NSString *service = [[NSBundle mainBundle] bundleIdentifier];

       NSDictionary *secItem = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                              (__bridge id)kSecAttrService : service,
                              (__bridge id)kSecAttrAccount : keyStr,
                              (__bridge id)kSecValueData : valueData};

       CFTypeRef result = NULL;

       OSStatus status = SecItemAdd((__bridge CFDictionaryRef)secItem, &result);

       NSLog(@"%@", [self getErrorMessageForStatus:status]);

      return [self checkIfItem:status];
   }
   else {
      return NO;
   }
}

I thought that keychain items were always accessible in iOS... this post seems to be about something similar but I'm not sure if it is is deprecated and how should I solve this...

Thanks in advance

Community
  • 1
  • 1
AppsDev
  • 12,319
  • 23
  • 93
  • 186

1 Answers1

-2

We had the same issue, @AppsDev, and the post you mention is accurate. We solved it by keeping the keychain as a last resort for things we need available even if we uninstall / reinstall the app.

We now loop back on the app defaults (in Swift 3 that would be UserDefaults.standard) to keep that info handy while the install lifecycle doesn't reach the "uninstalled" stage.

If uninstalled, next time is installed we go to the keychain (by definition, a just installed app isn't in background so it won't fail). With the data retrieved we refresh the app defaults and, from there, we just use the app defaults.

Diegum
  • 55
  • 4