Просмотр исходного кода

General keychain cleanup

- KeyChain → Keychain
- Misc renames and refactorings
- Change title
- Tabs to spaces
- Indentation
Tanner Bennett лет назад: 6
Родитель
Сommit
8255c7fe79

+ 1 - 1
Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h

@@ -19,7 +19,7 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
     FLEXGlobalsRowCookies,
     FLEXGlobalsRowSystemLibraries,
     FLEXGlobalsRowAppClasses,
-    FLEXGlobalsRowAppKeyChainItems,
+    FLEXGlobalsRowAppKeychainItems,
     FLEXGlobalsRowAppDelegate,
     FLEXGlobalsRowRootViewController,
     FLEXGlobalsRowUserDefaults,

+ 4 - 4
Classes/GlobalStateExplorers/Globals/FLEXGlobalsTableViewController.m

@@ -11,7 +11,7 @@
 #import "FLEXRuntimeUtility.h"
 #import "FLEXLibrariesTableViewController.h"
 #import "FLEXClassesTableViewController.h"
-#import "FLEXKeyChainTableViewController.h"
+#import "FLEXKeychainTableViewController.h"
 #import "FLEXObjectExplorerViewController.h"
 #import "FLEXObjectExplorerFactory.h"
 #import "FLEXLiveObjectsTableViewController.h"
@@ -57,8 +57,8 @@ static __weak UIWindow *s_applicationWindow = nil;
     switch (row) {
         case FLEXGlobalsRowAppClasses:
             return [FLEXClassesTableViewController flex_concreteGlobalsEntry:row];
-        case FLEXGlobalsRowAppKeyChainItems:
-            return [FLEXKeyChainTableViewController flex_concreteGlobalsEntry:row];
+        case FLEXGlobalsRowAppKeychainItems:
+            return [FLEXKeychainTableViewController flex_concreteGlobalsEntry:row];
         case FLEXGlobalsRowAddressInspector:
             return [FLEXAddressExplorerCoordinator flex_concreteGlobalsEntry:row];
         case FLEXGlobalsRowSystemLibraries:
@@ -124,7 +124,7 @@ static __weak UIWindow *s_applicationWindow = nil;
             @[ // FLEXGlobalsSectionAppShortcuts
                 [self globalsEntryForRow:FLEXGlobalsRowMainBundle],
                 [self globalsEntryForRow:FLEXGlobalsRowUserDefaults],
-                [self globalsEntryForRow:FLEXGlobalsRowAppKeyChainItems],
+                [self globalsEntryForRow:FLEXGlobalsRowAppKeychainItems],
                 [self globalsEntryForRow:FLEXGlobalsRowApplication],
                 [self globalsEntryForRow:FLEXGlobalsRowAppDelegate],
                 [self globalsEntryForRow:FLEXGlobalsRowKeyWindow],

+ 0 - 198
Classes/GlobalStateExplorers/KeyChain/FLEXKeychain.h

@@ -1,198 +0,0 @@
-//
-//  SSKeychain.h
-//  SSKeychain
-//
-//  Created by Sam Soffes on 5/19/10.
-//  Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
-//
-
-#if __has_feature(modules)
-	@import Foundation;
-#else
-	#import <Foundation/Foundation.h>
-#endif
-
-/**
- Error code specific to SSKeychain that can be returned in NSError objects.
- For codes returned by the operating system, refer to SecBase.h for your
- platform.
- */
-typedef NS_ENUM(OSStatus, SSKeychainErrorCode) {
-	/** Some of the arguments were invalid. */
-	SSKeychainErrorBadArguments = -1001,
-};
-
-/** SSKeychain error domain */
-extern NSString *const kSSKeychainErrorDomain;
-
-/** Account name. */
-extern NSString *const kSSKeychainAccountKey;
-
-/**
- Time the item was created.
-
- The value will be a string.
- */
-extern NSString *const kSSKeychainCreatedAtKey;
-
-/** Item class. */
-extern NSString *const kSSKeychainClassKey;
-
-/** Item description. */
-extern NSString *const kSSKeychainDescriptionKey;
-
-/** Item label. */
-extern NSString *const kSSKeychainLabelKey;
-
-/** Time the item was last modified.
-
- The value will be a string.
- */
-extern NSString *const kSSKeychainLastModifiedKey;
-
-/** Where the item was created. */
-extern NSString *const kSSKeychainWhereKey;
-
-/**
- Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system
- Keychain on Mac OS X and iOS.
-
- This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors.
- SSKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it).
- */
-@interface FLEXKeychain : NSObject
-
-#pragma mark - Classic methods
-
-/**
- Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a
- password for the given parameters.
-
- @param serviceName The service for which to return the corresponding password.
-
- @param account The account for which to return the corresponding password.
-
- @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't
- have a password for the given parameters.
- */
-+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
-+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
-
-/**
- Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't have a
- password for the given parameters.
-
- @param serviceName The service for which to return the corresponding password.
-
- @param account The account for which to return the corresponding password.
-
- @return Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't
- have a password for the given parameters.
- */
-+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
-+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
-
-
-/**
- Deletes a password from the Keychain.
-
- @param serviceName The service for which to delete the corresponding password.
-
- @param account The account for which to delete the corresponding password.
-
- @return Returns `YES` on success, or `NO` on failure.
- */
-+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
-+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
-
-
-/**
- Sets a password in the Keychain.
-
- @param password The password to store in the Keychain.
-
- @param serviceName The service for which to set the corresponding password.
-
- @param account The account for which to set the corresponding password.
-
- @return Returns `YES` on success, or `NO` on failure.
- */
-+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
-+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
-
-/**
- Sets a password in the Keychain.
-
- @param password The password to store in the Keychain.
-
- @param serviceName The service for which to set the corresponding password.
-
- @param account The account for which to set the corresponding password.
-
- @return Returns `YES` on success, or `NO` on failure.
- */
-+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
-+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none)));
-
-/**
- Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts.
-
- See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
- dictionaries returned by this method.
-
- @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any
- accounts. The order of the objects in the array isn't defined.
- */
-+ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts;
-+ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts:(NSError *__autoreleasing *)error __attribute__((swift_error(none)));
-
-
-/**
- Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any
- accounts for the given service.
-
- See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the
- dictionaries returned by this method.
-
- @param serviceName The service for which to return the corresponding accounts.
-
- @return An array of dictionaries containing the Keychain's accounts for a given `serviceName`, or `nil` if the Keychain
- doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined.
- */
-+ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName;
-+ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error __attribute__((swift_error(none)));
-
-
-#pragma mark - Configuration
-
-#if __IPHONE_4_0 && TARGET_OS_IPHONE
-/**
- Returns the accessibility type for all future passwords saved to the Keychain.
-
- @return Returns the accessibility type.
-
- The return value will be `NULL` or one of the "Keychain Item Accessibility
- Constants" used for determining when a keychain item should be readable.
-
- @see setAccessibilityType
- */
-+ (CFTypeRef)accessibilityType;
-
-/**
- Sets the accessibility type for all future passwords saved to the Keychain.
-
- @param accessibilityType One of the "Keychain Item Accessibility Constants"
- used for determining when a keychain item should be readable.
-
- If the value is `NULL` (the default), the Keychain default will be used which
- is highly insecure. You really should use at least `kSecAttrAccessibleAfterFirstUnlock`
- for background applications or `kSecAttrAccessibleWhenUnlocked` for all
- other applications.
-
- @see accessibilityType
- */
-+ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
-#endif
-
-@end
-

+ 0 - 143
Classes/GlobalStateExplorers/KeyChain/FLEXKeychainQuery.h

@@ -1,143 +0,0 @@
-//
-//  SSKeychainQuery.h
-//  SSKeychain
-//
-//  Created by Caleb Davenport on 3/19/13.
-//  Copyright (c) 2013-2014 Sam Soffes. All rights reserved.
-//
-
-#if __has_feature(modules)
-	@import Foundation;
-	@import Security;
-#else
-	#import <Foundation/Foundation.h>
-	#import <Security/Security.h>
-#endif
-
-#if __IPHONE_7_0 || __MAC_10_9
-	// Keychain synchronization available at compile time
-	#define SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1
-#endif
-
-#if __IPHONE_3_0 || __MAC_10_9
-	// Keychain access group available at compile time
-	#define SSKEYCHAIN_ACCESS_GROUP_AVAILABLE 1
-#endif
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-typedef NS_ENUM(NSUInteger, SSKeychainQuerySynchronizationMode) {
-	SSKeychainQuerySynchronizationModeAny,
-	SSKeychainQuerySynchronizationModeNo,
-	SSKeychainQuerySynchronizationModeYes
-};
-#endif
-
-/**
- Simple interface for querying or modifying keychain items.
- */
-@interface FLEXKeychainQuery : NSObject
-
-/** kSecAttrAccount */
-@property (nonatomic, copy) NSString *account;
-
-/** kSecAttrService */
-@property (nonatomic, copy) NSString *service;
-
-/** kSecAttrLabel */
-@property (nonatomic, copy) NSString *label;
-
-#ifdef SSKEYCHAIN_ACCESS_GROUP_AVAILABLE
-/** kSecAttrAccessGroup (only used on iOS) */
-@property (nonatomic, copy) NSString *accessGroup;
-#endif
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-/** kSecAttrSynchronizable */
-@property (nonatomic) SSKeychainQuerySynchronizationMode synchronizationMode;
-#endif
-
-/** Root storage for password information */
-@property (nonatomic, copy) NSData *passwordData;
-
-/**
- This property automatically transitions between an object and the value of
- `passwordData` using NSKeyedArchiver and NSKeyedUnarchiver.
- */
-@property (nonatomic, copy) id<NSCoding> passwordObject;
-
-/**
- Convenience accessor for setting and getting a password string. Passes through
- to `passwordData` using UTF-8 string encoding.
- */
-@property (nonatomic, copy) NSString *password;
-
-
-///------------------------
-/// @name Saving & Deleting
-///------------------------
-
-/**
- Save the receiver's attributes as a keychain item. Existing items with the
- given account, service, and access group will first be deleted.
-
- @param error Populated should an error occur.
-
- @return `YES` if saving was successful, `NO` otherwise.
- */
-- (BOOL)save:(NSError **)error;
-
-/**
- Delete keychain items that match the given account, service, and access group.
-
- @param error Populated should an error occur.
-
- @return `YES` if saving was successful, `NO` otherwise.
- */
-- (BOOL)deleteItem:(NSError **)error;
-
-
-///---------------
-/// @name Fetching
-///---------------
-
-/**
- Fetch all keychain items that match the given account, service, and access
- group. The values of `password` and `passwordData` are ignored when fetching.
-
- @param error Populated should an error occur.
-
- @return An array of dictionaries that represent all matching keychain items or
- `nil` should an error occur.
- The order of the items is not determined.
- */
-- (NSArray<NSDictionary<NSString *, id> *> *)fetchAll:(NSError **)error;
-
-/**
- Fetch the keychain item that matches the given account, service, and access
- group. The `password` and `passwordData` properties will be populated unless
- an error occurs. The values of `password` and `passwordData` are ignored when
- fetching.
-
- @param error Populated should an error occur.
-
- @return `YES` if fetching was successful, `NO` otherwise.
- */
-- (BOOL)fetch:(NSError **)error;
-
-
-///-----------------------------
-/// @name Synchronization Status
-///-----------------------------
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-/**
- Returns a boolean indicating if keychain synchronization is available on the device at runtime. The #define 
- SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time. If you are checking for the presence of synchronization,
- you should use this method.
- 
- @return A value indicating if keychain synchronization is available
- */
-+ (BOOL)isSynchronizationAvailable;
-#endif
-
-@end

+ 0 - 316
Classes/GlobalStateExplorers/KeyChain/FLEXKeychainQuery.m

@@ -1,316 +0,0 @@
-//
-//  SSKeychainQuery.m
-//  SSKeychain
-//
-//  Created by Caleb Davenport on 3/19/13.
-//  Copyright (c) 2013-2014 Sam Soffes. All rights reserved.
-//
-
-#import "FLEXKeychainQuery.h"
-#import "FLEXKeychain.h"
-
-@implementation FLEXKeychainQuery
-
-@synthesize account = _account;
-@synthesize service = _service;
-@synthesize label = _label;
-@synthesize passwordData = _passwordData;
-
-#ifdef SSKEYCHAIN_ACCESS_GROUP_AVAILABLE
-@synthesize accessGroup = _accessGroup;
-#endif
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-@synthesize synchronizationMode = _synchronizationMode;
-#endif
-
-#pragma mark - Public
-
-- (BOOL)save:(NSError *__autoreleasing *)error {
-	OSStatus status = SSKeychainErrorBadArguments;
-	if (!self.service || !self.account || !self.passwordData) {
-		if (error) {
-			*error = [[self class] errorWithCode:status];
-		}
-		return NO;
-	}
-	NSMutableDictionary *query = nil;
-	NSMutableDictionary * searchQuery = [self query];
-	status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
-	if (status == errSecSuccess) {//item already exists, update it!
-		query = [[NSMutableDictionary alloc]init];
-		[query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
-#if __IPHONE_4_0 && TARGET_OS_IPHONE
-		CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
-		if (accessibilityType) {
-			[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
-		}
-#endif
-		status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
-	}else if(status == errSecItemNotFound){//item not found, create it!
-		query = [self query];
-		if (self.label) {
-			[query setObject:self.label forKey:(__bridge id)kSecAttrLabel];
-		}
-		[query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
-#if __IPHONE_4_0 && TARGET_OS_IPHONE
-		CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
-		if (accessibilityType) {
-			[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
-		}
-#endif
-		status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
-	}
-	if (status != errSecSuccess && error != NULL) {
-		*error = [[self class] errorWithCode:status];
-	}
-	return (status == errSecSuccess);}
-
-
-- (BOOL)deleteItem:(NSError *__autoreleasing *)error {
-	OSStatus status = SSKeychainErrorBadArguments;
-	if (!self.service || !self.account) {
-		if (error) {
-			*error = [[self class] errorWithCode:status];
-		}
-		return NO;
-	}
-
-	NSMutableDictionary *query = [self query];
-#if TARGET_OS_IPHONE
-	status = SecItemDelete((__bridge CFDictionaryRef)query);
-#else
-	// On Mac OS, SecItemDelete will not delete a key created in a different
-	// app, nor in a different version of the same app.
-	//
-	// To replicate the issue, save a password, change to the code and
-	// rebuild the app, and then attempt to delete that password.
-	//
-	// This was true in OS X 10.6 and probably later versions as well.
-	//
-	// Work around it by using SecItemCopyMatching and SecKeychainItemDelete.
-	CFTypeRef result = NULL;
-	[query setObject:@YES forKey:(__bridge id)kSecReturnRef];
-	status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
-	if (status == errSecSuccess) {
-		status = SecKeychainItemDelete((SecKeychainItemRef)result);
-		CFRelease(result);
-	}
-#endif
-
-	if (status != errSecSuccess && error != NULL) {
-		*error = [[self class] errorWithCode:status];
-	}
-
-	return (status == errSecSuccess);
-}
-
-
-- (NSArray *)fetchAll:(NSError *__autoreleasing *)error {
-	NSMutableDictionary *query = [self query];
-	[query setObject:@YES forKey:(__bridge id)kSecReturnAttributes];
-	[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
-#if __IPHONE_4_0 && TARGET_OS_IPHONE
-	CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
-	if (accessibilityType) {
-		[query setObject:(__bridge id)accessibilityType forKey:(__bridge id)kSecAttrAccessible];
-	}
-#endif
-
-	CFTypeRef result = NULL;
-	OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
-	if (status != errSecSuccess && error != NULL) {
-		*error = [[self class] errorWithCode:status];
-		return nil;
-	}
-
-	return (__bridge_transfer NSArray *)result;
-}
-
-
-- (BOOL)fetch:(NSError *__autoreleasing *)error {
-	OSStatus status = SSKeychainErrorBadArguments;
-	if (!self.service || !self.account) {
-		if (error) {
-			*error = [[self class] errorWithCode:status];
-		}
-		return NO;
-	}
-
-	CFTypeRef result = NULL;
-	NSMutableDictionary *query = [self query];
-	[query setObject:@YES forKey:(__bridge id)kSecReturnData];
-	[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
-	status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
-
-	if (status != errSecSuccess) {
-		if (error) {
-			*error = [[self class] errorWithCode:status];
-		}
-		return NO;
-	}
-
-	self.passwordData = (__bridge_transfer NSData *)result;
-	return YES;
-}
-
-
-#pragma mark - Accessors
-
-- (void)setPasswordObject:(id<NSCoding>)object {
-	self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
-}
-
-
-- (id<NSCoding>)passwordObject {
-	if ([self.passwordData length]) {
-		return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
-	}
-	return nil;
-}
-
-
-- (void)setPassword:(NSString *)password {
-	self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
-}
-
-
-- (NSString *)password {
-	if ([self.passwordData length]) {
-		return [[NSString alloc] initWithData:self.passwordData encoding:NSUTF8StringEncoding];
-	}
-	return nil;
-}
-
-
-#pragma mark - Synchronization Status
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-+ (BOOL)isSynchronizationAvailable {
-#if TARGET_OS_IPHONE
-	// Apple suggested way to check for 7.0 at runtime
-	// https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/SupportingEarlieriOS.html
-	return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
-#else
-	return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
-#endif
-}
-#endif
-
-
-#pragma mark - Private
-
-- (NSMutableDictionary *)query {
-	NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
-	[dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
-
-	if (self.service) {
-		[dictionary setObject:self.service forKey:(__bridge id)kSecAttrService];
-	}
-
-	if (self.account) {
-		[dictionary setObject:self.account forKey:(__bridge id)kSecAttrAccount];
-	}
-
-#ifdef SSKEYCHAIN_ACCESS_GROUP_AVAILABLE
-#if !TARGET_IPHONE_SIMULATOR
-	if (self.accessGroup) {
-		[dictionary setObject:self.accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
-	}
-#endif
-#endif
-
-#ifdef SSKEYCHAIN_SYNCHRONIZATION_AVAILABLE
-	if ([[self class] isSynchronizationAvailable]) {
-		id value;
-
-		switch (self.synchronizationMode) {
-			case SSKeychainQuerySynchronizationModeNo: {
-			  value = @NO;
-			  break;
-			}
-			case SSKeychainQuerySynchronizationModeYes: {
-			  value = @YES;
-			  break;
-			}
-			case SSKeychainQuerySynchronizationModeAny: {
-			  value = (__bridge id)(kSecAttrSynchronizableAny);
-			  break;
-			}
-		}
-
-		[dictionary setObject:value forKey:(__bridge id)(kSecAttrSynchronizable)];
-	}
-#endif
-
-	return dictionary;
-}
-
-
-+ (NSError *)errorWithCode:(OSStatus) code {
-	static dispatch_once_t onceToken;
-	static NSBundle *resourcesBundle = nil;
-	dispatch_once(&onceToken, ^{
-		NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:@"SSKeychain" withExtension:@"bundle"];
-		resourcesBundle = [NSBundle bundleWithURL:url];
-	});
-	
-	NSString *message = nil;
-	switch (code) {
-		case errSecSuccess: return nil;
-		case SSKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"SSKeychainErrorBadArguments", @"SSKeychain", resourcesBundle, nil); break;
-
-#if TARGET_OS_IPHONE
-		case errSecUnimplemented: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecParam: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecParam", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecAllocate: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecAllocate", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecNotAvailable: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecNotAvailable", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecDuplicateItem: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecDuplicateItem", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecItemNotFound: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecItemNotFound", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecInteractionNotAllowed: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecInteractionNotAllowed", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecDecode: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecDecode", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		case errSecAuthFailed: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecAuthFailed", @"SSKeychain", resourcesBundle, nil);
-			break;
-		}
-		default: {
-			message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"SSKeychain", resourcesBundle, nil);
-		}
-#else
-		default:
-			message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
-#endif
-	}
-
-	NSDictionary *userInfo = nil;
-	if (message) {
-		userInfo = @{ NSLocalizedDescriptionKey : message };
-	}
-	return [NSError errorWithDomain:kSSKeychainErrorDomain code:code userInfo:userInfo];
-}
-
-@end

+ 141 - 0
Classes/GlobalStateExplorers/Keychain/FLEXKeychain.h

@@ -0,0 +1,141 @@
+//
+//  FLEXKeychain.h
+//
+//  Derived from:
+//  SSKeychain.h in SSKeychain
+//  Created by Sam Soffes on 5/19/10.
+//  Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/// Error code specific to FLEXKeychain that can be returned in NSError objects.
+/// For codes returned by the operating system, refer to SecBase.h for your
+/// platform.
+typedef NS_ENUM(OSStatus, FLEXKeychainErrorCode) {
+    /// Some of the arguments were invalid.
+    FLEXKeychainErrorBadArguments = -1001,
+};
+
+/// FLEXKeychain error domain
+extern NSString *const kFLEXKeychainErrorDomain;
+
+/// Account name.
+extern NSString *const kFLEXKeychainAccountKey;
+
+/// Time the item was created.
+///
+/// The value will be a string.
+extern NSString *const kFLEXKeychainCreatedAtKey;
+
+/// Item class.
+extern NSString *const kFLEXKeychainClassKey;
+
+/// Item description.
+extern NSString *const kFLEXKeychainDescriptionKey;
+
+/// Item label.
+extern NSString *const kFLEXKeychainLabelKey;
+
+/// Time the item was last modified.
+///
+/// The value will be a string.
+extern NSString *const kFLEXKeychainLastModifiedKey;
+
+/// Where the item was created.
+extern NSString *const kFLEXKeychainWhereKey;
+
+/// A simple wrapper for accessing accounts, getting passwords,
+/// setting passwords, and deleting passwords using the system Keychain.
+@interface FLEXKeychain : NSObject
+
+#pragma mark - Classic methods
+
+/// @param serviceName The service for which to return the corresponding password.
+/// @param account The account for which to return the corresponding password.
+/// @return Returns a string containing the password for a given account and service,
+/// or `nil` if the Keychain doesn't have a password for the given parameters.
++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+/// Returns a nsdata containing the password for a given account and service,
+/// or `nil` if the Keychain doesn't have a password for the given parameters.
+///
+/// @param serviceName The service for which to return the corresponding password.
+/// @param account The account for which to return the corresponding password.
+/// @return Returns a nsdata containing the password for a given account and service,
+/// or `nil` if the Keychain doesn't have a password for the given parameters.
++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+
+/// Deletes a password from the Keychain.
+///
+/// @param serviceName The service for which to delete the corresponding password.
+/// @param account The account for which to delete the corresponding password.
+/// @return Returns `YES` on success, or `NO` on failure.
++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+
+/// Sets a password in the Keychain.
+///
+/// @param password The password to store in the Keychain.
+/// @param serviceName The service for which to set the corresponding password.
+/// @param account The account for which to set the corresponding password.
+/// @return Returns `YES` on success, or `NO` on failure.
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+/// Sets a password in the Keychain.
+///
+/// @param password The password to store in the Keychain.
+/// @param serviceName The service for which to set the corresponding password.
+/// @param account The account for which to set the corresponding password.
+/// @return Returns `YES` on success, or `NO` on failure.
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+
+/// @return An array of dictionaries containing the Keychain's accounts, or `nil` if
+/// the Keychain doesn't have any accounts. The order of the objects in the array isn't defined.
+///
+/// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that
+/// can be used when accessing the dictionaries returned by this method.
++ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts;
++ (NSArray<NSDictionary<NSString *, id> *> *)allAccounts:(NSError *__autoreleasing *)error;
+
+/// @param serviceName The service for which to return the corresponding accounts.
+/// @return An array of dictionaries containing the Keychain's accounts for a given `serviceName`,
+/// or `nil` if the Keychain doesn't have any accounts for the given `serviceName`.
+/// The order of the objects in the array isn't defined.
+///
+/// @note See the `NSString` constants declared in FLEXKeychain.h for a list of keys that
+/// can be used when accessing the dictionaries returned by this method.
++ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName;
++ (NSArray<NSDictionary<NSString *, id> *> *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error;
+
+
+#pragma mark - Configuration
+
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+/// Returns the accessibility type for all future passwords saved to the Keychain.
+///
+/// @return `NULL` or one of the "Keychain Item Accessibility
+/// Constants" used for determining when a keychain item should be readable.
++ (CFTypeRef)accessibilityType;
+
+/// Sets the accessibility type for all future passwords saved to the Keychain.
+///
+/// @param accessibilityType One of the "Keychain Item Accessibility Constants"
+/// used for determining when a keychain item should be readable.
+/// If the value is `NULL` (the default), the Keychain default will be used which
+/// is highly insecure. You really should use at least `kSecAttrAccessibleAfterFirstUnlock`
+/// for background applications or `kSecAttrAccessibleWhenUnlocked` for all
+/// other applications.
+///
+/// @note See Security/SecItem.h
++ (void)setAccessibilityType:(CFTypeRef)accessibilityType;
+#endif
+
+@end
+

+ 43 - 53
Classes/GlobalStateExplorers/KeyChain/FLEXKeychain.m

@@ -1,7 +1,8 @@
 //
-//  SSKeychain.m
-//  SSKeychain
+//  FLEXKeychain.m
 //
+//  Forked from:
+//  SSKeychain.m in SSKeychain
 //  Created by Sam Soffes on 5/19/10.
 //  Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
 //
@@ -9,81 +10,75 @@
 #import "FLEXKeychain.h"
 #import "FLEXKeychainQuery.h"
 
-NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain";
-NSString *const kSSKeychainAccountKey = @"acct";
-NSString *const kSSKeychainCreatedAtKey = @"cdat";
-NSString *const kSSKeychainClassKey = @"labl";
-NSString *const kSSKeychainDescriptionKey = @"desc";
-NSString *const kSSKeychainLabelKey = @"labl";
-NSString *const kSSKeychainLastModifiedKey = @"mdat";
-NSString *const kSSKeychainWhereKey = @"svce";
+NSString * const kFLEXKeychainErrorDomain = @"com.flipboard.flex";
+NSString * const kFLEXKeychainAccountKey = @"acct";
+NSString * const kFLEXKeychainCreatedAtKey = @"cdat";
+NSString * const kFLEXKeychainClassKey = @"labl";
+NSString * const kFLEXKeychainDescriptionKey = @"desc";
+NSString * const kFLEXKeychainLabelKey = @"labl";
+NSString * const kFLEXKeychainLastModifiedKey = @"mdat";
+NSString * const kFLEXKeychainWhereKey = @"svce";
 
 #if __IPHONE_4_0 && TARGET_OS_IPHONE
-	static CFTypeRef SSKeychainAccessibilityType = NULL;
+static CFTypeRef FLEXKeychainAccessibilityType = NULL;
 #endif
 
 @implementation FLEXKeychain
 
 + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account {
-	return [self passwordForService:serviceName account:account error:nil];
+    return [self passwordForService:serviceName account:account error:nil];
 }
 
-
 + (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
-	FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
-	query.service = serviceName;
-	query.account = account;
-	[query fetch:error];
-	return query.password;
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
+    query.service = serviceName;
+    query.account = account;
+    [query fetch:error];
+    return query.password;
 }
 
 + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account {
-	return [self passwordDataForService:serviceName account:account error:nil];
+    return [self passwordDataForService:serviceName account:account error:nil];
 }
 
 + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
-    FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
     query.service = serviceName;
     query.account = account;
     [query fetch:error];
-
+    
     return query.passwordData;
 }
 
-
 + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account {
-	return [self deletePasswordForService:serviceName account:account error:nil];
+    return [self deletePasswordForService:serviceName account:account error:nil];
 }
 
-
 + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
-	FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
-	query.service = serviceName;
-	query.account = account;
-	return [query deleteItem:error];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
+    query.service = serviceName;
+    query.account = account;
+    return [query deleteItem:error];
 }
 
-
 + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account {
-	return [self setPassword:password forService:serviceName account:account error:nil];
+    return [self setPassword:password forService:serviceName account:account error:nil];
 }
 
-
 + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error {
-	FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
-	query.service = serviceName;
-	query.account = account;
-	query.password = password;
-	return [query save:error];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
+    query.service = serviceName;
+    query.account = account;
+    query.password = password;
+    return [query save:error];
 }
 
 + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account {
-	return [self setPasswordData:password forService:serviceName account:account error:nil];
+    return [self setPasswordData:password forService:serviceName account:account error:nil];
 }
 
-
 + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error {
-    FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
     query.service = serviceName;
     query.account = account;
     query.passwordData = password;
@@ -91,39 +86,34 @@ NSString *const kSSKeychainWhereKey = @"svce";
 }
 
 + (NSArray *)allAccounts {
-	return [self allAccounts:nil];
+    return [self allAccounts:nil];
 }
 
-
 + (NSArray *)allAccounts:(NSError *__autoreleasing *)error {
     return [self accountsForService:nil error:error];
 }
 
-
 + (NSArray *)accountsForService:(NSString *)serviceName {
-	return [self accountsForService:serviceName error:nil];
+    return [self accountsForService:serviceName error:nil];
 }
 
-
 + (NSArray *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error {
-    FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
     query.service = serviceName;
     return [query fetchAll:error];
 }
 
-
 #if __IPHONE_4_0 && TARGET_OS_IPHONE
 + (CFTypeRef)accessibilityType {
-	return SSKeychainAccessibilityType;
+    return FLEXKeychainAccessibilityType;
 }
 
-
 + (void)setAccessibilityType:(CFTypeRef)accessibilityType {
-	CFRetain(accessibilityType);
-	if (SSKeychainAccessibilityType) {
-		CFRelease(SSKeychainAccessibilityType);
-	}
-	SSKeychainAccessibilityType = accessibilityType;
+    CFRetain(accessibilityType);
+    if (FLEXKeychainAccessibilityType) {
+        CFRelease(FLEXKeychainAccessibilityType);
+    }
+    FLEXKeychainAccessibilityType = accessibilityType;
 }
 #endif
 

+ 112 - 0
Classes/GlobalStateExplorers/Keychain/FLEXKeychainQuery.h

@@ -0,0 +1,112 @@
+//
+//  FLEXKeychainQuery.h
+//
+//  Derived from:
+//  SSKeychainQuery.h in SSKeychain
+//  Created by Caleb Davenport on 3/19/13.
+//  Copyright (c) 2010-2014 Sam Soffes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+#if __IPHONE_7_0 || __MAC_10_9
+// Keychain synchronization available at compile time
+#define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE 1
+#endif
+
+#if __IPHONE_3_0 || __MAC_10_9
+// Keychain access group available at compile time
+#define FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE 1
+#endif
+
+#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+typedef NS_ENUM(NSUInteger, FLEXKeychainQuerySynchronizationMode) {
+    FLEXKeychainQuerySynchronizationModeAny,
+    FLEXKeychainQuerySynchronizationModeNo,
+    FLEXKeychainQuerySynchronizationModeYes
+};
+#endif
+
+/// Simple interface for querying or modifying keychain items.
+@interface FLEXKeychainQuery : NSObject
+
+/// kSecAttrAccount
+@property (nonatomic, copy) NSString *account;
+
+/// kSecAttrService
+@property (nonatomic, copy) NSString *service;
+
+/// kSecAttrLabel
+@property (nonatomic, copy) NSString *label;
+
+#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
+/// kSecAttrAccessGroup (only used on iOS)
+@property (nonatomic, copy) NSString *accessGroup;
+#endif
+
+#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+/// kSecAttrSynchronizable
+@property (nonatomic) FLEXKeychainQuerySynchronizationMode synchronizationMode;
+#endif
+
+/// Root storage for password information
+@property (nonatomic, copy) NSData *passwordData;
+
+/// This property automatically transitions between an object and the value of
+/// `passwordData` using NSKeyedArchiver and NSKeyedUnarchiver.
+@property (nonatomic, copy) id<NSCoding> passwordObject;
+
+/// Convenience accessor for setting and getting a password string. Passes through
+/// to `passwordData` using UTF-8 string encoding.
+@property (nonatomic, copy) NSString *password;
+
+
+#pragma mark Saving & Deleting
+
+/// Save the receiver's attributes as a keychain item. Existing items with the
+/// given account, service, and access group will first be deleted.
+///
+/// @param error Populated should an error occur.
+/// @return `YES` if saving was successful, `NO` otherwise.
+- (BOOL)save:(NSError **)error;
+
+/// Delete keychain items that match the given account, service, and access group.
+///
+/// @param error Populated should an error occur.
+/// @return `YES` if saving was successful, `NO` otherwise.
+- (BOOL)deleteItem:(NSError **)error;
+
+
+#pragma mark Fetching
+
+/// Fetch all keychain items that match the given account, service, and access
+/// group. The values of `password` and `passwordData` are ignored when fetching.
+///
+/// @param error Populated should an error occur.
+/// @return An array of dictionaries that represent all matching keychain items,
+/// or `nil` should an error occur. The order of the items is not determined.
+- (NSArray<NSDictionary<NSString *, id> *> *)fetchAll:(NSError **)error;
+
+/// Fetch the keychain item that matches the given account, service, and access
+/// group. The `password` and `passwordData` properties will be populated unless
+/// an error occurs. The values of `password` and `passwordData` are ignored when
+/// fetching.
+///
+/// @param error Populated should an error occur.
+/// @return `YES` if fetching was successful, `NO` otherwise.
+- (BOOL)fetch:(NSError **)error;
+
+
+#pragma mark Synchronization Status
+
+#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+/// Returns a boolean indicating if keychain synchronization is available on the device at runtime.
+/// The #define FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE is only for compile time.
+/// If you are checking for the presence of synchronization, you should use this method.
+///
+/// @return A value indicating if keychain synchronization is available
++ (BOOL)isSynchronizationAvailable;
+#endif
+
+@end

+ 304 - 0
Classes/GlobalStateExplorers/Keychain/FLEXKeychainQuery.m

@@ -0,0 +1,304 @@
+//
+//  FLEXKeychainQuery.m
+//  FLEXKeychain
+//
+//  Created by Caleb Davenport on 3/19/13.
+//  Copyright (c) 2013-2014 Sam Soffes. All rights reserved.
+//
+
+#import "FLEXKeychainQuery.h"
+#import "FLEXKeychain.h"
+
+@implementation FLEXKeychainQuery
+
+#pragma mark - Public
+
+- (BOOL)save:(NSError *__autoreleasing *)error {
+    OSStatus status = FLEXKeychainErrorBadArguments;
+    if (!self.service || !self.account || !self.passwordData) {
+        if (error) {
+            *error = [self errorWithCode:status];
+        }
+        return NO;
+    }
+    
+    NSMutableDictionary *query = nil;
+    NSMutableDictionary * searchQuery = [self query];
+    status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
+    if (status == errSecSuccess) {//item already exists, update it!
+        query = [[NSMutableDictionary alloc]init];
+        query[(__bridge id)kSecValueData] = self.passwordData;
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+        CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
+        if (accessibilityType) {
+            query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
+        }
+#endif
+        status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query));
+    }else if (status == errSecItemNotFound){//item not found, create it!
+        query = [self query];
+        if (self.label) {
+            query[(__bridge id)kSecAttrLabel] = self.label;
+        }
+        query[(__bridge id)kSecValueData] = self.passwordData;
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+        CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
+        if (accessibilityType) {
+            query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
+        }
+#endif
+        status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
+    }
+    
+    if (status != errSecSuccess && error != NULL) {
+        *error = [self errorWithCode:status];
+    }
+    
+    return (status == errSecSuccess);
+}
+
+
+- (BOOL)deleteItem:(NSError *__autoreleasing *)error {
+    OSStatus status = FLEXKeychainErrorBadArguments;
+    if (!self.service || !self.account) {
+        if (error) {
+            *error = [self errorWithCode:status];
+        }
+        
+        return NO;
+    }
+    
+    NSMutableDictionary *query = [self query];
+#if TARGET_OS_IPHONE
+    status = SecItemDelete((__bridge CFDictionaryRef)query);
+#else
+    // On Mac OS, SecItemDelete will not delete a key created in a different
+    // app, nor in a different version of the same app.
+    //
+    // To replicate the issue, save a password, change to the code and
+    // rebuild the app, and then attempt to delete that password.
+    //
+    // This was true in OS X 10.6 and probably later versions as well.
+    //
+    // Work around it by using SecItemCopyMatching and SecKeychainItemDelete.
+    CFTypeRef result = NULL;
+    query[(__bridge id)kSecReturnRef] = @YES;
+    status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
+    if (status == errSecSuccess) {
+        status = SecKeychainItemDelete((SecKeychainItemRef)result);
+        CFRelease(result);
+    }
+#endif
+    
+    if (status != errSecSuccess && error != NULL) {
+        *error = [self errorWithCode:status];
+    }
+    
+    return (status == errSecSuccess);
+}
+
+
+- (NSArray *)fetchAll:(NSError *__autoreleasing *)error {
+    NSMutableDictionary *query = [self query];
+    query[(__bridge id)kSecReturnAttributes] = @YES;
+    query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
+#if __IPHONE_4_0 && TARGET_OS_IPHONE
+    CFTypeRef accessibilityType = [FLEXKeychain accessibilityType];
+    if (accessibilityType) {
+        query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessibilityType;
+    }
+#endif
+    
+    CFTypeRef result = NULL;
+    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
+    if (status != errSecSuccess && error != NULL) {
+        *error = [self errorWithCode:status];
+        return nil;
+    }
+    
+    return (__bridge_transfer NSArray *)result;
+}
+
+
+- (BOOL)fetch:(NSError *__autoreleasing *)error {
+    OSStatus status = FLEXKeychainErrorBadArguments;
+    if (!self.service || !self.account) {
+        if (error) {
+            *error = [self errorWithCode:status];
+        }
+        return NO;
+    }
+    
+    CFTypeRef result = NULL;
+    NSMutableDictionary *query = [self query];
+    query[(__bridge id)kSecReturnData] = @YES;
+    query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
+    status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
+    
+    if (status != errSecSuccess) {
+        if (error) {
+            *error = [self errorWithCode:status];
+        }
+        return NO;
+    }
+    
+    self.passwordData = (__bridge_transfer NSData *)result;
+    return YES;
+}
+
+
+#pragma mark - Accessors
+
+- (void)setPasswordObject:(id<NSCoding>)object {
+    self.passwordData = [NSKeyedArchiver archivedDataWithRootObject:object];
+}
+
+
+- (id<NSCoding>)passwordObject {
+    if (self.passwordData.length) {
+        return [NSKeyedUnarchiver unarchiveObjectWithData:self.passwordData];
+    }
+    
+    return nil;
+}
+
+
+- (void)setPassword:(NSString *)password {
+    self.passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+
+- (NSString *)password {
+    if (self.passwordData.length) {
+        return [NSString stringWithCString:self.passwordData.bytes encoding:NSUTF8StringEncoding];
+    }
+    
+    return nil;
+}
+
+
+#pragma mark - Synchronization Status
+
+#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
++ (BOOL)isSynchronizationAvailable {
+#if TARGET_OS_IPHONE
+    return @available(iOS 7.0, *);
+#else
+    return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4;
+#endif
+}
+#endif
+
+
+#pragma mark - Private
+
+- (NSMutableDictionary *)query {
+    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+    dictionary[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
+    
+    if (self.service) {
+        dictionary[(__bridge id)kSecAttrService] = self.service;
+    }
+    
+    if (self.account) {
+        dictionary[(__bridge id)kSecAttrAccount] = self.account;
+    }
+    
+#ifdef FLEXKEYCHAIN_ACCESS_GROUP_AVAILABLE
+#if !TARGET_IPHONE_SIMULATOR
+    if (self.accessGroup) {
+        dictionary[(__bridge id)kSecAttrAccessGroup] = self.accessGroup;
+    }
+#endif
+#endif
+    
+#ifdef FLEXKEYCHAIN_SYNCHRONIZATION_AVAILABLE
+    if ([[self class] isSynchronizationAvailable]) {
+        id value;
+        
+        switch (self.synchronizationMode) {
+            case FLEXKeychainQuerySynchronizationModeNo: {
+                value = @NO;
+                break;
+            }
+            case FLEXKeychainQuerySynchronizationModeYes: {
+                value = @YES;
+                break;
+            }
+            case FLEXKeychainQuerySynchronizationModeAny: {
+                value = (__bridge id)(kSecAttrSynchronizableAny);
+                break;
+            }
+        }
+        
+        dictionary[(__bridge id)(kSecAttrSynchronizable)] = value;
+    }
+#endif
+    
+    return dictionary;
+}
+
+- (NSError *)errorWithCode:(OSStatus)code {
+    static dispatch_once_t onceToken;
+    static NSBundle *resourcesBundle = nil;
+    dispatch_once(&onceToken, ^{
+        NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:@"FLEXKeychain" withExtension:@"bundle"];
+        resourcesBundle = [NSBundle bundleWithURL:url];
+    });
+    
+    NSString *message = nil;
+    switch (code) {
+        case errSecSuccess: return nil;
+        case FLEXKeychainErrorBadArguments: message = NSLocalizedStringFromTableInBundle(@"FLEXKeychainErrorBadArguments", @"FLEXKeychain", resourcesBundle, nil); break;
+            
+#if TARGET_OS_IPHONE
+        case errSecUnimplemented: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecUnimplemented", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecParam: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecParam", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecAllocate: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecAllocate", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecNotAvailable: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecNotAvailable", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecDuplicateItem: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecDuplicateItem", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecItemNotFound: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecItemNotFound", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecInteractionNotAllowed: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecInteractionNotAllowed", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecDecode: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecDecode", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        case errSecAuthFailed: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecAuthFailed", @"FLEXKeychain", resourcesBundle, nil);
+            break;
+        }
+        default: {
+            message = NSLocalizedStringFromTableInBundle(@"errSecDefault", @"FLEXKeychain", resourcesBundle, nil);
+        }
+#else
+        default:
+            message = (__bridge_transfer NSString *)SecCopyErrorMessageString(code, NULL);
+#endif
+    }
+    
+    NSDictionary *userInfo = message ? @{ NSLocalizedDescriptionKey : message } : nil;
+    return [NSError errorWithDomain:kFLEXKeychainErrorDomain code:code userInfo:userInfo];
+}
+
+@end

+ 2 - 6
Classes/GlobalStateExplorers/KeyChain/FLEXKeyChainTableViewController.h

@@ -1,5 +1,5 @@
 //
-//  FLEXKeyChainTableViewController.h
+//  FLEXKeychainTableViewController.h
 //  FLEX
 //
 //  Created by ray on 2019/8/17.
@@ -9,10 +9,6 @@
 #import "FLEXGlobalsEntry.h"
 #import "FLEXTableViewController.h"
 
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLEXKeyChainTableViewController : FLEXTableViewController <FLEXGlobalsEntry>
+@interface FLEXKeychainTableViewController : FLEXTableViewController <FLEXGlobalsEntry>
 
 @end
-
-NS_ASSUME_NONNULL_END

+ 27 - 44
Classes/GlobalStateExplorers/KeyChain/FLEXKeyChainTableViewController.m

@@ -1,5 +1,5 @@
 //
-//  FLEXKeyChainTableViewController.m
+//  FLEXKeychainTableViewController.m
 //  FLEX
 //
 //  Created by ray on 2019/8/17.
@@ -8,44 +8,41 @@
 
 #import "FLEXKeychain.h"
 #import "FLEXKeychainQuery.h"
-#import "FLEXKeyChainTableViewController.h"
+#import "FLEXKeychainTableViewController.h"
 #import "FLEXUtility.h"
 
-@interface FLEXKeyChainTableViewController ()
+@interface FLEXKeychainTableViewController ()
 
 @property (nonatomic) NSArray<NSDictionary *> *keyChainItems;
 
 @end
 
-@implementation FLEXKeyChainTableViewController
+@implementation FLEXKeychainTableViewController
 
 - (void)viewDidLoad
 {
     [super viewDidLoad];
     
-    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(clearKeyChain)];
-
+    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(clearKeychain)];
     
-    _keyChainItems = [FLEXKeychain allAccounts];
-    self.title = [NSString stringWithFormat:@"🔑 KeyChains (%lu)", (unsigned long)self.keyChainItems.count];
+    self.keyChainItems = [FLEXKeychain allAccounts];
+    self.title = [NSString stringWithFormat:@"🔑 Keychain Items (%lu)", (unsigned long)self.keyChainItems.count];
 }
 
-- (void)clearKeyChain
+- (void)clearKeychain
 {
     
-    for (id account in _keyChainItems) {
-
-        FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
-
-        query.service = [account valueForKey:kSSKeychainWhereKey];
-        query.account = [account valueForKey:kSSKeychainAccountKey];
+    for (id account in self.keyChainItems) {
+        FLEXKeychainQuery *query = [FLEXKeychainQuery new];
+        query.service = account[kFLEXKeychainWhereKey];
+        query.account = account[kFLEXKeychainAccountKey];
         
-        if(![query deleteItem:nil]) {
+        if (![query deleteItem:nil]) {
             NSLog(@"Delete Keychin Item Failed.");
         }
     }
     
-    _keyChainItems = [FLEXKeychain allAccounts];
+    self.keyChainItems = [FLEXKeychain allAccounts];
     [self.tableView reloadData];
 }
 
@@ -53,24 +50,17 @@
 
 + (NSString *)globalsEntryTitle:(FLEXGlobalsRow)row
 {
-    return [NSString stringWithFormat:@"🔑  %@ KeyChain", [FLEXUtility applicationName]];
+    return @"🔑  Keychain";
 }
 
 + (UIViewController *)globalsEntryViewController:(FLEXGlobalsRow)row
 {
-    FLEXKeyChainTableViewController *keyChainViewController = [self new];
-    
-    return keyChainViewController;
+    return [self new];
 }
 
 
 #pragma mark - Table View Data Source
 
-- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
-{
-    return 1;
-}
-
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
     return self.keyChainItems.count;
@@ -87,7 +77,7 @@
     }
     
     NSDictionary *item = self.keyChainItems[indexPath.row];
-    cell.textLabel.text = item[kSSKeychainAccountKey];
+    cell.textLabel.text = item[kFLEXKeychainAccountKey];
     
     return cell;
 }
@@ -99,34 +89,27 @@
 {
     NSDictionary *item = self.keyChainItems[indexPath.row];
     
-    FLEXKeychainQuery *query = [[FLEXKeychainQuery alloc] init];
-    query.service = [item valueForKey:kSSKeychainWhereKey];
-    query.account = [item valueForKey:kSSKeychainAccountKey];
+    FLEXKeychainQuery *query = [FLEXKeychainQuery new];
+    query.service = [item valueForKey:kFLEXKeychainWhereKey];
+    query.account = [item valueForKey:kFLEXKeychainAccountKey];
     [query fetch:nil];
     
     NSString *msg = nil;
     
-    if ([query.password length])
-    {
+    if (query.password.length) {
         msg = query.password;
-    }
-    
-    else if ([query.passwordData length])
-    {
-        msg = [query.passwordData description];
-    }
-    
-    else
-    {
-        msg = @"NO Data!";
+    } else if (query.passwordData.length) {
+        msg = query.passwordData.description;
+    } else {
+        msg = @"No data";
     }
     
     UIAlertController *cv = [UIAlertController alertControllerWithTitle:@"Password" message:msg preferredStyle:UIAlertControllerStyleAlert];
-    [cv addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
+    [cv addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
         [cv dismissViewControllerAnimated:YES completion:nil];
     }]];
     
-    [cv addAction:[UIAlertAction actionWithTitle:@"Copy" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+    [cv addAction:[UIAlertAction actionWithTitle:@"Copy" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
         pasteboard.string = msg;
     }]];

+ 6 - 6
Example/UICatalog/AAPLActionSheetViewController.m

@@ -67,11 +67,11 @@ typedef NS_ENUM(NSInteger, AAPLActionSheetsViewControllerTableRow) {
     NSString *cancelButtonTitle = NSLocalizedString(@"Cancel", nil);
     NSString *destructiveButtonTitle = NSLocalizedString(@"OK", nil);
     
-	UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:cancelButtonTitle destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:nil];
+    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:cancelButtonTitle destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:nil];
     
-	actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
+    actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
     
-	[actionSheet showInView:self.view];
+    [actionSheet showInView:self.view];
 }
 
 // Show a dialog with two custom buttons.
@@ -79,11 +79,11 @@ typedef NS_ENUM(NSInteger, AAPLActionSheetsViewControllerTableRow) {
     NSString *destructiveButtonTitle = NSLocalizedString(@"Destructive Choice", nil);
     NSString *otherButtonTitle = NSLocalizedString(@"Safe Choice", nil);
 
-	UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:otherButtonTitle, nil];
+    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:otherButtonTitle, nil];
     
-	actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
+    actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
     
-	[actionSheet showInView:self.view];
+    [actionSheet showInView:self.view];
 }
 
 

+ 9 - 9
Example/UICatalog/AAPLAlertViewController.m

@@ -71,9 +71,9 @@ typedef NS_ENUM(NSInteger, AAPLAlertsViewControllerTableRow) {
     NSString *message = NSLocalizedString(@"A message should be a short, complete sentence.", nil);
     NSString *cancelButtonTitle = NSLocalizedString(@"OK", nil);
 
-	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
+    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil];
 
-	[alert show];
+    [alert show];
 }
 
 // Show an alert with an "Okay" and "Cancel" button.
@@ -83,9 +83,9 @@ typedef NS_ENUM(NSInteger, AAPLAlertsViewControllerTableRow) {
     NSString *cancelButtonTitle = NSLocalizedString(@"Cancel", nil);
     NSString *otherButtonTitle = NSLocalizedString(@"OK", nil);
 
-	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
+    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
 
-	[alert show];
+    [alert show];
 }
 
 // Show an alert with two custom buttons.
@@ -96,9 +96,9 @@ typedef NS_ENUM(NSInteger, AAPLAlertsViewControllerTableRow) {
     NSString *otherButtonTitleOne = NSLocalizedString(@"Choice One", nil);
     NSString *otherButtonTitleTwo = NSLocalizedString(@"Choice Two", nil);
 
-	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitleOne, otherButtonTitleTwo, nil];
+    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitleOne, otherButtonTitleTwo, nil];
 
-	[alert show];
+    [alert show];
 }
 
 // Show a text entry alert with two custom buttons.
@@ -108,7 +108,7 @@ typedef NS_ENUM(NSInteger, AAPLAlertsViewControllerTableRow) {
     NSString *cancelButtonTitle = NSLocalizedString(@"Cancel", nil);
     NSString *otherButtonTitle = NSLocalizedString(@"OK", nil);
     
-	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
+    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
 
     alert.alertViewStyle = UIAlertViewStylePlainTextInput;
 
@@ -122,9 +122,9 @@ typedef NS_ENUM(NSInteger, AAPLAlertsViewControllerTableRow) {
     NSString *cancelButtonTitle = NSLocalizedString(@"Cancel", nil);
     NSString *otherButtonTitle = NSLocalizedString(@"OK", nil);
 
-	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
+    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitle, nil];
 
-	alert.alertViewStyle = UIAlertViewStyleSecureTextInput;
+    alert.alertViewStyle = UIAlertViewStyleSecureTextInput;
     
     [alert show];
 }

+ 11 - 11
FLEX.xcodeproj/project.pbxproj

@@ -134,10 +134,10 @@
 		679F64861BD53B7B00A8C94C /* FLEXCookiesTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 679F64841BD53B7B00A8C94C /* FLEXCookiesTableViewController.h */; };
 		679F64871BD53B7B00A8C94C /* FLEXCookiesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 679F64851BD53B7B00A8C94C /* FLEXCookiesTableViewController.m */; };
 		71E1C2132307FBB800F5032A /* FLEXKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20B2307FBB700F5032A /* FLEXKeychain.h */; };
-		71E1C2142307FBB800F5032A /* FLEXKeyChainTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20C2307FBB700F5032A /* FLEXKeyChainTableViewController.h */; };
+		71E1C2142307FBB800F5032A /* FLEXKeychainTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20C2307FBB700F5032A /* FLEXKeychainTableViewController.h */; };
 		71E1C2152307FBB800F5032A /* FLEXKeychainQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */; };
 		71E1C2172307FBB800F5032A /* FLEXKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C20F2307FBB700F5032A /* FLEXKeychain.m */; };
-		71E1C2182307FBB800F5032A /* FLEXKeyChainTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C2102307FBB700F5032A /* FLEXKeyChainTableViewController.m */; };
+		71E1C2182307FBB800F5032A /* FLEXKeychainTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C2102307FBB700F5032A /* FLEXKeychainTableViewController.m */; };
 		71E1C2192307FBB800F5032A /* FLEXKeychainQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */; };
 		7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7349FD6822B93CDF00051810 /* FLEXColor.h */; };
 		7349FD6B22B93CDF00051810 /* FLEXColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 7349FD6922B93CDF00051810 /* FLEXColor.m */; };
@@ -353,10 +353,10 @@
 		679F64841BD53B7B00A8C94C /* FLEXCookiesTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXCookiesTableViewController.h; sourceTree = "<group>"; };
 		679F64851BD53B7B00A8C94C /* FLEXCookiesTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXCookiesTableViewController.m; sourceTree = "<group>"; };
 		71E1C20B2307FBB700F5032A /* FLEXKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychain.h; sourceTree = "<group>"; };
-		71E1C20C2307FBB700F5032A /* FLEXKeyChainTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeyChainTableViewController.h; sourceTree = "<group>"; };
+		71E1C20C2307FBB700F5032A /* FLEXKeychainTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychainTableViewController.h; sourceTree = "<group>"; };
 		71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXKeychainQuery.h; sourceTree = "<group>"; };
 		71E1C20F2307FBB700F5032A /* FLEXKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychain.m; sourceTree = "<group>"; };
-		71E1C2102307FBB700F5032A /* FLEXKeyChainTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeyChainTableViewController.m; sourceTree = "<group>"; };
+		71E1C2102307FBB700F5032A /* FLEXKeychainTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychainTableViewController.m; sourceTree = "<group>"; };
 		71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXKeychainQuery.m; sourceTree = "<group>"; };
 		7349FD6822B93CDF00051810 /* FLEXColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXColor.h; sourceTree = "<group>"; };
 		7349FD6922B93CDF00051810 /* FLEXColor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXColor.m; sourceTree = "<group>"; };
@@ -628,7 +628,7 @@
 		3A4C949A1B5B21410088C3F2 /* GlobalStateExplorers */ = {
 			isa = PBXGroup;
 			children = (
-				71E1C2092307FBB700F5032A /* KeyChain */,
+				71E1C2092307FBB700F5032A /* Keychain */,
 				C3511B8E22D7C9740057BAB7 /* Globals */,
 				779B1EBF1C0C4D7C001F5E49 /* DatabaseBrowser */,
 				3A4C94AD1B5B21410088C3F2 /* SystemLog */,
@@ -713,17 +713,17 @@
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
-		71E1C2092307FBB700F5032A /* KeyChain */ = {
+		71E1C2092307FBB700F5032A /* Keychain */ = {
 			isa = PBXGroup;
 			children = (
 				71E1C20B2307FBB700F5032A /* FLEXKeychain.h */,
 				71E1C20F2307FBB700F5032A /* FLEXKeychain.m */,
 				71E1C20D2307FBB700F5032A /* FLEXKeychainQuery.h */,
 				71E1C2112307FBB700F5032A /* FLEXKeychainQuery.m */,
-				71E1C20C2307FBB700F5032A /* FLEXKeyChainTableViewController.h */,
-				71E1C2102307FBB700F5032A /* FLEXKeyChainTableViewController.m */,
+				71E1C20C2307FBB700F5032A /* FLEXKeychainTableViewController.h */,
+				71E1C2102307FBB700F5032A /* FLEXKeychainTableViewController.m */,
 			);
-			path = KeyChain;
+			path = Keychain;
 			sourceTree = "<group>";
 		};
 		779B1EBF1C0C4D7C001F5E49 /* DatabaseBrowser */ = {
@@ -936,7 +936,7 @@
 				779B1ED41C0C4D7C001F5E49 /* FLEXTableContentCell.h in Headers */,
 				3A4C952C1B5B21410088C3F2 /* FLEXLiveObjectsTableViewController.h in Headers */,
 				3A4C94EF1B5B21410088C3F2 /* FLEXArgumentInputDateView.h in Headers */,
-				71E1C2142307FBB800F5032A /* FLEXKeyChainTableViewController.h in Headers */,
+				71E1C2142307FBB800F5032A /* FLEXKeychainTableViewController.h in Headers */,
 				3A4C94C71B5B21410088C3F2 /* FLEXClassExplorerViewController.h in Headers */,
 				3A4C94F71B5B21410088C3F2 /* FLEXArgumentInputNotSupportedView.h in Headers */,
 				C3F31D3E2267D883003C991A /* FLEXTableViewCell.h in Headers */,
@@ -1176,7 +1176,7 @@
 				3A4C95061B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m in Sources */,
 				3A4C95291B5B21410088C3F2 /* FLEXInstancesTableViewController.m in Sources */,
 				3A4C952B1B5B21410088C3F2 /* FLEXLibrariesTableViewController.m in Sources */,
-				71E1C2182307FBB800F5032A /* FLEXKeyChainTableViewController.m in Sources */,
+				71E1C2182307FBB800F5032A /* FLEXKeychainTableViewController.m in Sources */,
 				94A5151E1C4CA1F10063292F /* FLEXExplorerViewController.m in Sources */,
 				3A4C95371B5B21410088C3F2 /* FLEXNetworkHistoryTableViewController.m in Sources */,
 				C3511B9222D7C99E0057BAB7 /* FLEXTableViewSection.m in Sources */,