Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import

  • Loading branch information...
commit 8fcbe4885e9eb541735e083031fe842496285f7d 0 parents
@justin justin authored
Showing with 1,312 additions and 0 deletions.
  1. +45 −0 README.markdown
  2. +65 −0 SGKeychain.h
  3. +287 −0 SGKeychain.m
  4. +466 −0 SGKeychainExample/SGKeychainExample.xcodeproj/project.pbxproj
  5. +7 −0 SGKeychainExample/SGKeychainExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  6. +15 −0 SGKeychainExample/SGKeychainExample/SGAppDelegate.h
  7. +85 −0 SGKeychainExample/SGKeychainExample/SGAppDelegate.m
  8. +40 −0 SGKeychainExample/SGKeychainExample/SGKeychainExample-Info.plist
  9. +14 −0 SGKeychainExample/SGKeychainExample/SGKeychainExample-Prefix.pch
  10. +13 −0 SGKeychainExample/SGKeychainExample/SGViewController.h
  11. +34 −0 SGKeychainExample/SGKeychainExample/SGViewController.m
  12. +2 −0  SGKeychainExample/SGKeychainExample/en.lproj/InfoPlist.strings
  13. +42 −0 SGKeychainExample/SGKeychainExample/en.lproj/MainStoryboard.storyboard
  14. +18 −0 SGKeychainExample/SGKeychainExample/main.m
  15. +22 −0 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests-Info.plist
  16. +22 −0 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests.h
  17. +133 −0 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests.m
  18. +2 −0  SGKeychainExample/SGKeychainExampleTests/en.lproj/InfoPlist.strings
45 README.markdown
@@ -0,0 +1,45 @@
+SGKeychain — A class for working with the iOS keychain
+=========================
+
+Created and maintained by [Justin Williams](http://www.secondgearsoftware.com)
+
+What is SGKeychain?
+-------------------------
+
+SGKeychain is a class for working with the keychain on iOS. It has the following features:
+
+ * Creating new keychain items
+ * Fetching passwords from the keychain
+ * Deleting items from your keychain
+ * Supports keychain access groups for sharing a single keychain between multiple apps (not supported in the iPhone simulator)
+ * Built for iOS 5 and above using automatic reference counting (ARC)
+ * Unit tested
+
+What's in the box?
+-------------------------
+
+SGKeychain includes:
+
+* The SGKeychain class itself.
+* A sample application that demonstrates how to use SGKeychain's main methods.
+
+Requirements
+-------------------------
+* You will need to link your Xcode target with Security.framework in order to use SGKeychain.
+* SGKeychain is built using ARC. There are no plans to support reference counting.
+
+Find this useful?
+-------------------------
+
+If you have found this useful, I'm always a sucker for receiving surprises from my [Amazon wishlist](http://amzn.to/HRZaNd).
+
+If you do use SGKeychain in your app, please do contact me and [let me know](http://carpeaqua.com/contact/).
+
+All The Other Stuff
+-------------------------
+
+Please report bugs, feature requests and whatnot using Github Issues and (even better) a pull request.
+
+---------------------------------------
+
+* **1.0** Original release
65 SGKeychain.h
@@ -0,0 +1,65 @@
+//
+// SGKeychain.h
+// SGKeychain
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface SGKeychain : NSObject
+
+// Create
++ (BOOL)setPassword:(NSString *)password
+ username:(NSString *)username
+ serviceName:(NSString *)serviceName
+ updateExisting:(BOOL)updateExisting
+ error:(NSError **)error;
+
++ (BOOL)setPassword:(NSString *)password
+ username:(NSString *)username
+ serviceName:(NSString *)serviceName
+ accessGroup:(NSString *)accessGroup
+ updateExisting:(BOOL) updateExisting
+ error:(NSError **)error;
+
+// Read
++ (NSString *)passwordForUsername:(NSString *)username
+ serviceName:(NSString *)serviceName
+ error:(NSError **)error;
+
++ (NSString *)passwordForUsername:(NSString *)username
+ serviceName:(NSString *)serviceName
+ accessGroup:(NSString *)accessGroup
+ error:(NSError **)error;
+
+// Delete
++ (BOOL)deletePasswordForUsername:(NSString *)username
+ serviceName:(NSString *)serviceName
+ error:(NSError **)error;
+
++ (BOOL)deletePasswordForUsername:(NSString *)username
+ serviceName:(NSString *)serviceName
+ accessGroup:(NSString *)accessGroup
+ error:(NSError **)error;
+
+@end
287 SGKeychain.m
@@ -0,0 +1,287 @@
+//
+// SGKeychain.m
+// SGKeychain
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#import <Security/Security.h>
+#import "SGKeychain.h"
+
+enum SGKeychainErrorCodes
+{
+ SGKeychainRequiredValueNotPresentError = -666,
+ SGKeychainPasswordNotFoundError = -777,
+};
+
+static NSString * const SGKeychainErrorDomain = @"com.secondgear.sgkeychain";
+
+@implementation SGKeychain
+
++ (NSString *)passwordForUsername:(NSString *)username serviceName:(NSString *)serviceName error:(NSError **)error
+{
+ return [SGKeychain passwordForUsername:username serviceName:serviceName accessGroup:nil error:error];
+}
+
++ (NSString *)passwordForUsername:(NSString *)username serviceName:(NSString *)serviceName accessGroup:(NSString *)accessGroup error:(NSError **)error
+{
+ BOOL requiredValueIsNil = ((username == nil) || (serviceName == nil));
+ if (requiredValueIsNil == YES)
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:SGKeychainRequiredValueNotPresentError userInfo:nil];
+ return nil;
+ }
+ }
+
+ NSArray *keys = [NSArray arrayWithObjects:(__bridge NSString *) kSecClass,
+ kSecAttrService,
+ kSecAttrAccount, nil];
+
+ NSArray *objects = [NSArray arrayWithObjects:(__bridge NSString *) kSecClassGenericPassword,
+ serviceName,
+ username, nil];
+
+ NSMutableDictionary *attributesQuery = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
+#if !TARGET_IPHONE_SIMULATOR
+ if (accessGroup != nil)
+ {
+ [attributesQuery setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
+ }
+#endif
+
+ OSStatus getPasswordStatus = noErr;
+ NSMutableDictionary *queryDictionary = [attributesQuery mutableCopy];
+ [queryDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
+
+ CFTypeRef result = NULL;
+ getPasswordStatus = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &result);
+
+ if (getPasswordStatus != noErr)
+ {
+ if ((error != nil) && (getPasswordStatus != errSecItemNotFound))
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:getPasswordStatus userInfo:nil];
+ }
+ return nil;
+ }
+
+ NSMutableDictionary *passwordQuery = [attributesQuery mutableCopy];
+ [passwordQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
+ CFTypeRef passwordResult = NULL;
+ getPasswordStatus = SecItemCopyMatching((__bridge CFDictionaryRef)passwordQuery, &passwordResult);
+ NSData *resultData = (__bridge_transfer NSData *)passwordResult;
+
+ if (getPasswordStatus != noErr)
+ {
+ if (getPasswordStatus == errSecItemNotFound)
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:SGKeychainPasswordNotFoundError userInfo: nil];
+ }
+ }
+ else
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:getPasswordStatus userInfo:nil];
+ }
+ }
+ }
+
+ NSString *password = nil;
+ if (resultData != nil)
+ {
+ password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
+ }
+ else
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:SGKeychainPasswordNotFoundError userInfo: nil];
+ }
+ }
+
+ return password;
+}
+
++ (BOOL)setPassword:(NSString *)password username:(NSString *)username serviceName:(NSString *)serviceName updateExisting:(BOOL)updateExisting error:(NSError **)error
+
+{
+ return [SGKeychain setPassword:password username:username serviceName:serviceName accessGroup:nil updateExisting:updateExisting error:error];
+}
+
++ (BOOL)setPassword:(NSString *)password username:(NSString *)username serviceName:(NSString *)serviceName accessGroup:(NSString *)accessGroup updateExisting:(BOOL)updateExisting error:(NSError **)error
+{
+ BOOL requiredValueIsNil = ((password == nil) || (username == nil) || (serviceName == nil));
+ if (requiredValueIsNil == YES)
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:SGKeychainRequiredValueNotPresentError userInfo:nil];
+ }
+ return NO;
+ }
+
+ NSError *getPasswordError = nil;
+ NSString *existingPassword = [SGKeychain passwordForUsername:username serviceName:serviceName accessGroup:accessGroup error:&getPasswordError];
+
+ if ([getPasswordError code] == SGKeychainPasswordNotFoundError)
+ {
+ NSError *deletePasswordError;
+ [self deletePasswordForUsername:username serviceName:serviceName accessGroup:accessGroup error:&deletePasswordError];
+ if ([deletePasswordError code] != noErr)
+ {
+ if (error != nil)
+ {
+ *error = deletePasswordError;
+ }
+
+ return NO;
+ }
+ else if ([deletePasswordError code] != noErr)
+ {
+ if (error != nil)
+ {
+ *error = deletePasswordError;
+ }
+ return NO;
+ }
+ }
+
+ OSStatus setPasswordStatus = noErr;
+ if (existingPassword != nil)
+ {
+ if (([existingPassword isEqualToString:password] == NO) && (updateExisting == YES))
+ {
+ NSArray *keys = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClass,
+ kSecAttrService,
+ kSecAttrLabel,
+ kSecAttrAccount,
+ nil];
+
+ NSArray *objects = [[NSArray alloc] initWithObjects: (__bridge_transfer NSString *) kSecClassGenericPassword,
+ serviceName,
+ serviceName,
+ username,
+ nil];
+
+ NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
+#if !TARGET_IPHONE_SIMULATOR
+ if (accessGroup != nil)
+ {
+ [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
+ }
+#endif
+ NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
+ NSDictionary *newValueDictionary = [NSDictionary dictionaryWithObject:passwordData forKey:(__bridge NSString *) kSecValueData];
+ setPasswordStatus = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)newValueDictionary);
+ }
+ }
+ else
+ {
+ NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
+ NSArray *keys = [[NSArray alloc] initWithObjects:(__bridge NSString *) kSecClass,
+ kSecAttrService,
+ kSecAttrLabel,
+ kSecAttrAccount,
+ kSecValueData, nil];
+
+ NSArray *objects = [[NSArray alloc] initWithObjects:(__bridge NSString *) kSecClassGenericPassword,
+ serviceName,
+ serviceName,
+ username,
+ passwordData, nil];
+
+ NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
+#if !TARGET_IPHONE_SIMULATOR
+ if (accessGroup != nil)
+ {
+ [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
+ }
+#endif
+
+ setPasswordStatus = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
+ }
+
+ if (setPasswordStatus != noErr)
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:setPasswordStatus userInfo:nil];
+ }
+ return NO;
+ }
+
+ return YES;
+}
+
+
++ (BOOL)deletePasswordForUsername:(NSString *)username serviceName:(NSString *)serviceName error:(NSError **)error
+{
+ return [SGKeychain deletePasswordForUsername:username serviceName:serviceName accessGroup:nil error:error];
+}
+
++ (BOOL)deletePasswordForUsername:(NSString *)username serviceName:(NSString *)serviceName accessGroup:(NSString *)accessGroup error:(NSError **)error
+{
+ BOOL requiredValueIsNil = ((username == nil) || (serviceName == nil));
+ if (requiredValueIsNil == YES)
+ {
+ if (error != nil)
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:SGKeychainRequiredValueNotPresentError userInfo:nil];
+ return NO;
+ }
+ }
+
+ NSArray *keys = [NSArray arrayWithObjects:(__bridge NSString *)kSecClass,
+ kSecAttrService,
+ kSecAttrAccount,
+ kSecReturnAttributes, nil];
+
+ NSArray *objects = [NSArray arrayWithObjects:(__bridge NSString *) kSecClassGenericPassword,
+ serviceName,
+ username,
+ kCFBooleanTrue, nil];
+
+ NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
+#if !TARGET_IPHONE_SIMULATOR
+ if (accessGroup != nil)
+ {
+ [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
+ }
+#endif
+
+ OSStatus deleteItemStatus = SecItemDelete((__bridge CFDictionaryRef) query);
+
+ if ((error != nil) && (deleteItemStatus != noErr))
+ {
+ *error = [NSError errorWithDomain:SGKeychainErrorDomain code:deleteItemStatus userInfo:nil];
+ return NO;
+ }
+
+ return YES;
+}
+
+@end
466 SGKeychainExample/SGKeychainExample.xcodeproj/project.pbxproj
@@ -0,0 +1,466 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ D0AA6927152FA73E0076DED1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6926152FA73E0076DED1 /* UIKit.framework */; };
+ D0AA6929152FA73E0076DED1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6928152FA73E0076DED1 /* Foundation.framework */; };
+ D0AA692B152FA73E0076DED1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA692A152FA73E0076DED1 /* CoreGraphics.framework */; };
+ D0AA6931152FA73E0076DED1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0AA692F152FA73E0076DED1 /* InfoPlist.strings */; };
+ D0AA6933152FA73F0076DED1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA6932152FA73F0076DED1 /* main.m */; };
+ D0AA6937152FA73F0076DED1 /* SGAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA6936152FA73F0076DED1 /* SGAppDelegate.m */; };
+ D0AA693A152FA73F0076DED1 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D0AA6938152FA73F0076DED1 /* MainStoryboard.storyboard */; };
+ D0AA693D152FA73F0076DED1 /* SGViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA693C152FA73F0076DED1 /* SGViewController.m */; };
+ D0AA6945152FA73F0076DED1 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6944152FA73F0076DED1 /* SenTestingKit.framework */; };
+ D0AA6946152FA73F0076DED1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6926152FA73E0076DED1 /* UIKit.framework */; };
+ D0AA6947152FA73F0076DED1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6928152FA73E0076DED1 /* Foundation.framework */; };
+ D0AA694F152FA73F0076DED1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0AA694D152FA73F0076DED1 /* InfoPlist.strings */; };
+ D0AA6952152FA73F0076DED1 /* SGKeychainExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA6951152FA73F0076DED1 /* SGKeychainExampleTests.m */; };
+ D0AA6963152FA76F0076DED1 /* SGKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA6962152FA76F0076DED1 /* SGKeychain.m */; };
+ D0AA6964152FA76F0076DED1 /* SGKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AA6962152FA76F0076DED1 /* SGKeychain.m */; };
+ D0AA6967152FAA1C0076DED1 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6966152FAA1C0076DED1 /* Security.framework */; };
+ D0AA6968152FAA200076DED1 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AA6966152FAA1C0076DED1 /* Security.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ D0AA6948152FA73F0076DED1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = D0AA6919152FA73E0076DED1 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D0AA6921152FA73E0076DED1;
+ remoteInfo = SGKeychainExample;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ D0AA6922152FA73E0076DED1 /* SGKeychainExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SGKeychainExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ D0AA6926152FA73E0076DED1 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ D0AA6928152FA73E0076DED1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ D0AA692A152FA73E0076DED1 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ D0AA692E152FA73E0076DED1 /* SGKeychainExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SGKeychainExample-Info.plist"; sourceTree = "<group>"; };
+ D0AA6930152FA73E0076DED1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ D0AA6932152FA73F0076DED1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ D0AA6934152FA73F0076DED1 /* SGKeychainExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SGKeychainExample-Prefix.pch"; sourceTree = "<group>"; };
+ D0AA6935152FA73F0076DED1 /* SGAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGAppDelegate.h; sourceTree = "<group>"; };
+ D0AA6936152FA73F0076DED1 /* SGAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGAppDelegate.m; sourceTree = "<group>"; };
+ D0AA6939152FA73F0076DED1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = "<group>"; };
+ D0AA693B152FA73F0076DED1 /* SGViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGViewController.h; sourceTree = "<group>"; };
+ D0AA693C152FA73F0076DED1 /* SGViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGViewController.m; sourceTree = "<group>"; };
+ D0AA6943152FA73F0076DED1 /* SGKeychainExampleTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SGKeychainExampleTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
+ D0AA6944152FA73F0076DED1 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+ D0AA694C152FA73F0076DED1 /* SGKeychainExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SGKeychainExampleTests-Info.plist"; sourceTree = "<group>"; };
+ D0AA694E152FA73F0076DED1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ D0AA6950152FA73F0076DED1 /* SGKeychainExampleTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGKeychainExampleTests.h; sourceTree = "<group>"; };
+ D0AA6951152FA73F0076DED1 /* SGKeychainExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGKeychainExampleTests.m; sourceTree = "<group>"; };
+ D0AA6961152FA76F0076DED1 /* SGKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SGKeychain.h; path = ../SGKeychain.h; sourceTree = "<group>"; };
+ D0AA6962152FA76F0076DED1 /* SGKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SGKeychain.m; path = ../SGKeychain.m; sourceTree = "<group>"; };
+ D0AA6966152FAA1C0076DED1 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D0AA691F152FA73E0076DED1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA6927152FA73E0076DED1 /* UIKit.framework in Frameworks */,
+ D0AA6929152FA73E0076DED1 /* Foundation.framework in Frameworks */,
+ D0AA692B152FA73E0076DED1 /* CoreGraphics.framework in Frameworks */,
+ D0AA6968152FAA200076DED1 /* Security.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D0AA693F152FA73F0076DED1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA6967152FAA1C0076DED1 /* Security.framework in Frameworks */,
+ D0AA6945152FA73F0076DED1 /* SenTestingKit.framework in Frameworks */,
+ D0AA6946152FA73F0076DED1 /* UIKit.framework in Frameworks */,
+ D0AA6947152FA73F0076DED1 /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ D0AA6917152FA73E0076DED1 = {
+ isa = PBXGroup;
+ children = (
+ D0AA6961152FA76F0076DED1 /* SGKeychain.h */,
+ D0AA6962152FA76F0076DED1 /* SGKeychain.m */,
+ D0AA692C152FA73E0076DED1 /* SGKeychainExample */,
+ D0AA694A152FA73F0076DED1 /* SGKeychainExampleTests */,
+ D0AA6925152FA73E0076DED1 /* Frameworks */,
+ D0AA6923152FA73E0076DED1 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ D0AA6923152FA73E0076DED1 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA6922152FA73E0076DED1 /* SGKeychainExample.app */,
+ D0AA6943152FA73F0076DED1 /* SGKeychainExampleTests.octest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ D0AA6925152FA73E0076DED1 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA6966152FAA1C0076DED1 /* Security.framework */,
+ D0AA6926152FA73E0076DED1 /* UIKit.framework */,
+ D0AA6928152FA73E0076DED1 /* Foundation.framework */,
+ D0AA692A152FA73E0076DED1 /* CoreGraphics.framework */,
+ D0AA6944152FA73F0076DED1 /* SenTestingKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ D0AA692C152FA73E0076DED1 /* SGKeychainExample */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA6935152FA73F0076DED1 /* SGAppDelegate.h */,
+ D0AA6936152FA73F0076DED1 /* SGAppDelegate.m */,
+ D0AA6938152FA73F0076DED1 /* MainStoryboard.storyboard */,
+ D0AA693B152FA73F0076DED1 /* SGViewController.h */,
+ D0AA693C152FA73F0076DED1 /* SGViewController.m */,
+ D0AA692D152FA73E0076DED1 /* Supporting Files */,
+ );
+ path = SGKeychainExample;
+ sourceTree = "<group>";
+ };
+ D0AA692D152FA73E0076DED1 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA692E152FA73E0076DED1 /* SGKeychainExample-Info.plist */,
+ D0AA692F152FA73E0076DED1 /* InfoPlist.strings */,
+ D0AA6932152FA73F0076DED1 /* main.m */,
+ D0AA6934152FA73F0076DED1 /* SGKeychainExample-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ D0AA694A152FA73F0076DED1 /* SGKeychainExampleTests */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA6950152FA73F0076DED1 /* SGKeychainExampleTests.h */,
+ D0AA6951152FA73F0076DED1 /* SGKeychainExampleTests.m */,
+ D0AA694B152FA73F0076DED1 /* Supporting Files */,
+ );
+ path = SGKeychainExampleTests;
+ sourceTree = "<group>";
+ };
+ D0AA694B152FA73F0076DED1 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ D0AA694C152FA73F0076DED1 /* SGKeychainExampleTests-Info.plist */,
+ D0AA694D152FA73F0076DED1 /* InfoPlist.strings */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ D0AA6921152FA73E0076DED1 /* SGKeychainExample */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D0AA6955152FA73F0076DED1 /* Build configuration list for PBXNativeTarget "SGKeychainExample" */;
+ buildPhases = (
+ D0AA691E152FA73E0076DED1 /* Sources */,
+ D0AA691F152FA73E0076DED1 /* Frameworks */,
+ D0AA6920152FA73E0076DED1 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SGKeychainExample;
+ productName = SGKeychainExample;
+ productReference = D0AA6922152FA73E0076DED1 /* SGKeychainExample.app */;
+ productType = "com.apple.product-type.application";
+ };
+ D0AA6942152FA73F0076DED1 /* SGKeychainExampleTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D0AA6958152FA73F0076DED1 /* Build configuration list for PBXNativeTarget "SGKeychainExampleTests" */;
+ buildPhases = (
+ D0AA693E152FA73F0076DED1 /* Sources */,
+ D0AA693F152FA73F0076DED1 /* Frameworks */,
+ D0AA6940152FA73F0076DED1 /* Resources */,
+ D0AA6941152FA73F0076DED1 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ D0AA6949152FA73F0076DED1 /* PBXTargetDependency */,
+ );
+ name = SGKeychainExampleTests;
+ productName = SGKeychainExampleTests;
+ productReference = D0AA6943152FA73F0076DED1 /* SGKeychainExampleTests.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ D0AA6919152FA73E0076DED1 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = SG;
+ LastUpgradeCheck = 0430;
+ ORGANIZATIONNAME = "Second Gear";
+ };
+ buildConfigurationList = D0AA691C152FA73E0076DED1 /* Build configuration list for PBXProject "SGKeychainExample" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = D0AA6917152FA73E0076DED1;
+ productRefGroup = D0AA6923152FA73E0076DED1 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D0AA6921152FA73E0076DED1 /* SGKeychainExample */,
+ D0AA6942152FA73F0076DED1 /* SGKeychainExampleTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ D0AA6920152FA73E0076DED1 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA6931152FA73E0076DED1 /* InfoPlist.strings in Resources */,
+ D0AA693A152FA73F0076DED1 /* MainStoryboard.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D0AA6940152FA73F0076DED1 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA694F152FA73F0076DED1 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ D0AA6941152FA73F0076DED1 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ D0AA691E152FA73E0076DED1 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA6933152FA73F0076DED1 /* main.m in Sources */,
+ D0AA6937152FA73F0076DED1 /* SGAppDelegate.m in Sources */,
+ D0AA693D152FA73F0076DED1 /* SGViewController.m in Sources */,
+ D0AA6963152FA76F0076DED1 /* SGKeychain.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D0AA693E152FA73F0076DED1 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D0AA6952152FA73F0076DED1 /* SGKeychainExampleTests.m in Sources */,
+ D0AA6964152FA76F0076DED1 /* SGKeychain.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ D0AA6949152FA73F0076DED1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D0AA6921152FA73E0076DED1 /* SGKeychainExample */;
+ targetProxy = D0AA6948152FA73F0076DED1 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ D0AA692F152FA73E0076DED1 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ D0AA6930152FA73E0076DED1 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ D0AA6938152FA73F0076DED1 /* MainStoryboard.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ D0AA6939152FA73F0076DED1 /* en */,
+ );
+ name = MainStoryboard.storyboard;
+ sourceTree = "<group>";
+ };
+ D0AA694D152FA73F0076DED1 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ D0AA694E152FA73F0076DED1 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ D0AA6953152FA73F0076DED1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.1;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ D0AA6954152FA73F0076DED1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.1;
+ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ D0AA6956152FA73F0076DED1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Justin Williams (Z96PC63GR8)";
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SGKeychainExample/SGKeychainExample-Prefix.pch";
+ INFOPLIST_FILE = "SGKeychainExample/SGKeychainExample-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "17B1EE22-0384-47EC-A07F-C79146A537E1";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ D0AA6957152FA73F0076DED1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SGKeychainExample/SGKeychainExample-Prefix.pch";
+ INFOPLIST_FILE = "SGKeychainExample/SGKeychainExample-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ D0AA6959152FA73F0076DED1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SGKeychainExample.app/SGKeychainExample";
+ CODE_SIGN_ENTITLEMENTS = SGKeychainExample/SGKeychainExample.entitlements;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(DEVELOPER_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SGKeychainExample/SGKeychainExample-Prefix.pch";
+ INFOPLIST_FILE = "SGKeychainExampleTests/SGKeychainExampleTests-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ D0AA695A152FA73F0076DED1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SGKeychainExample.app/SGKeychainExample";
+ CODE_SIGN_ENTITLEMENTS = SGKeychainExample/SGKeychainExample.entitlements;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(DEVELOPER_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SGKeychainExample/SGKeychainExample-Prefix.pch";
+ INFOPLIST_FILE = "SGKeychainExampleTests/SGKeychainExampleTests-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ D0AA691C152FA73E0076DED1 /* Build configuration list for PBXProject "SGKeychainExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D0AA6953152FA73F0076DED1 /* Debug */,
+ D0AA6954152FA73F0076DED1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D0AA6955152FA73F0076DED1 /* Build configuration list for PBXNativeTarget "SGKeychainExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D0AA6956152FA73F0076DED1 /* Debug */,
+ D0AA6957152FA73F0076DED1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D0AA6958152FA73F0076DED1 /* Build configuration list for PBXNativeTarget "SGKeychainExampleTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D0AA6959152FA73F0076DED1 /* Debug */,
+ D0AA695A152FA73F0076DED1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = D0AA6919152FA73E0076DED1 /* Project object */;
+}
7 SGKeychainExample/SGKeychainExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:SGKeychainExample.xcodeproj">
+ </FileRef>
+</Workspace>
15 SGKeychainExample/SGKeychainExample/SGAppDelegate.h
@@ -0,0 +1,15 @@
+//
+// SGAppDelegate.h
+// SGKeychainExample
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface SGAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
85 SGKeychainExample/SGKeychainExample/SGAppDelegate.m
@@ -0,0 +1,85 @@
+//
+// SGAppDelegate.m
+// SGKeychainExample
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import "SGAppDelegate.h"
+#import "SGKeychain.h"
+
+@implementation SGAppDelegate
+
+@synthesize window = _window;
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ // Store a password
+ NSError *storePasswordError = nil;
+ BOOL passwordSuccessfullyCreated = [SGKeychain setPassword:@"testpassword" username:@"justin" serviceName:@"Twitter" updateExisting:NO error:&storePasswordError];
+
+ if (passwordSuccessfullyCreated == YES)
+ {
+ NSLog(@"Password successfully created");
+ }
+ else
+ {
+ NSLog(@"Password failed to be created with error: %@", storePasswordError);
+ }
+
+ // Fetch the password
+ NSError *fetchPasswordError = nil;
+ NSString *password = [SGKeychain passwordForUsername:@"justin" serviceName:@"Twitter" error:&fetchPasswordError];
+
+ if (password != nil)
+ {
+ NSLog(@"Fetched password = %@", password);
+ }
+ else
+ {
+ NSLog(@"Error fetching password = %@", fetchPasswordError);
+ }
+
+ // Delete the password
+ NSError *deletePasswordError = nil;
+ BOOL passwordSuccessfullyDeleted = [SGKeychain deletePasswordForUsername:@"justin" serviceName:@"Twitter" error:&deletePasswordError];
+ if (passwordSuccessfullyDeleted == YES)
+ {
+ NSLog(@"Password successfully deleted");
+ }
+ else
+ {
+ NSLog(@"Failed to delete password: %@", deletePasswordError);
+ }
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
40 SGKeychainExample/SGKeychainExample/SGKeychainExample-Info.plist
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>secondgear.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIMainStoryboardFile</key>
+ <string>MainStoryboard</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
14 SGKeychainExample/SGKeychainExample/SGKeychainExample-Prefix.pch
@@ -0,0 +1,14 @@
+//
+// Prefix header for all source files of the 'SGKeychainExample' target in the 'SGKeychainExample' project
+//
+
+#import <Availability.h>
+
+#ifndef __IPHONE_5_0
+#warning "This project uses features only available in iOS SDK 5.0 and later."
+#endif
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+#endif
13 SGKeychainExample/SGKeychainExample/SGViewController.h
@@ -0,0 +1,13 @@
+//
+// SGViewController.h
+// SGKeychainExample
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface SGViewController : UIViewController
+
+@end
34 SGKeychainExample/SGKeychainExample/SGViewController.m
@@ -0,0 +1,34 @@
+//
+// SGViewController.m
+// SGKeychainExample
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import "SGViewController.h"
+
+@interface SGViewController ()
+
+@end
+
+@implementation SGViewController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)viewDidUnload
+{
+ [super viewDidUnload];
+ // Release any retained subviews of the main view.
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
+}
+
+@end
2  SGKeychainExample/SGKeychainExample/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
42 SGKeychainExample/SGKeychainExample/en.lproj/MainStoryboard.storyboard
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="1.1" toolsVersion="2182" systemVersion="11D50b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="2">
+ <dependencies>
+ <deployment defaultVersion="1296" identifier="iOS"/>
+ <development defaultVersion="4200" identifier="xcode"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1181"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="5">
+ <objects>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="4" sceneMemberID="firstResponder"/>
+ <viewController id="2" customClass="SGViewController" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="3">
+ <rect key="frame" x="0.0" y="20" width="320" height="460"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <subviews>
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Check Your Console" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="yU9-5h-q4W">
+ <rect key="frame" x="82" y="219" width="157" height="21"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
+ <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ </objects>
+ </scene>
+ </scenes>
+ <classes>
+ <class className="SGViewController" superclassName="UIViewController">
+ <source key="sourceIdentifier" type="project" relativePath="./Classes/SGViewController.h"/>
+ </class>
+ </classes>
+ <simulatedMetricsContainer key="defaultSimulatedMetrics">
+ <simulatedStatusBarMetrics key="statusBar"/>
+ <simulatedOrientationMetrics key="orientation"/>
+ <simulatedScreenMetrics key="destination"/>
+ </simulatedMetricsContainer>
+</document>
18 SGKeychainExample/SGKeychainExample/main.m
@@ -0,0 +1,18 @@
+//
+// main.m
+// SGKeychainExample
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "SGAppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGAppDelegate class]));
+ }
+}
22 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>secondgear.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
22 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests.h
@@ -0,0 +1,22 @@
+//
+// SGKeychainExampleTests.h
+// SGKeychainExampleTests
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+
+@interface SGKeychainExampleTests : SenTestCase
+
+- (void)testPasswordIsSuccessfullyCreated;
+- (void)testErrorReturnedWhenPassingNilValuesOnCreate;
+- (void)testExistingPasswordRecordSuccessfullyUpdated;
+- (void)testPasswordIsSuccessfullyFetched;
+- (void)testPasswordsAreSuccessfullyFetchedFromSameAccessGroup;
+- (void)testErrorReturnedWhenPassingNilValuesOnFetch;
+- (void)testPasswordIsSuccessfullyDeleted;
+- (void)testErrorReturnedWhenPassingNilValuesOnDelete;
+
+@end
133 SGKeychainExample/SGKeychainExampleTests/SGKeychainExampleTests.m
@@ -0,0 +1,133 @@
+//
+// SGKeychainExampleTests.m
+// SGKeychainExampleTests
+//
+// Created by Justin Williams on 4/6/12.
+// Copyright (c) 2012 Second Gear. All rights reserved.
+//
+
+#import "SGKeychainExampleTests.h"
+#import "SGKeychain.h"
+
+@interface SGKeychainExampleTests ()
+@property (nonatomic, copy) NSString *username;
+@property (nonatomic, copy) NSString *service;
+@property (nonatomic, copy) NSString *expectedPassword;
+@end
+
+@implementation SGKeychainExampleTests
+
+@synthesize username;
+@synthesize service;
+@synthesize expectedPassword;
+
+- (void)setUp
+{
+ [super setUp];
+ self.username = @"justin";
+ self.service = @"com.secondgear.testapp";
+ self.expectedPassword = @"testpassword";
+}
+
+- (void)tearDown
+{
+ [super tearDown];
+ // Delete the keychain password before each test.
+ [SGKeychain deletePasswordForUsername:self.username serviceName:self.service error:nil];
+
+ self.username = nil;
+ self.service = nil;
+ self.expectedPassword = nil;
+}
+
+- (void)testPasswordIsSuccessfullyCreated
+{
+ NSString *password = @"testpassword";
+ BOOL successfullyCreated = [SGKeychain setPassword:password username:self.username serviceName:self.service updateExisting:NO error:nil];
+ STAssertTrue(successfullyCreated, @"create password failed");
+ STAssertEqualObjects(password, self.expectedPassword, @"Incorrect password fetched");
+}
+
+- (void)testErrorReturnedWhenPassingNilValuesOnCreate
+{
+ NSError *error = nil;
+ BOOL successfullyCreated = [SGKeychain setPassword:nil username:self.username serviceName:self.service updateExisting:NO error:&error];
+ STAssertFalse(successfullyCreated, @"create password didn't fail as expected");
+
+ STAssertTrue([error code] == -666, @"Error code received not as expected");
+}
+
+- (void)testExistingPasswordRecordSuccessfullyUpdated
+{
+ NSString *oldPassword = @"oldpassword";
+ NSString *newPassword = @"newpassword";
+ BOOL successfullyCreated = [SGKeychain setPassword:oldPassword username:self.username serviceName:self.service updateExisting:NO error:nil];
+ STAssertTrue(successfullyCreated, @"create password failed");
+
+ BOOL successfullyUpdated = [SGKeychain setPassword:newPassword username:self.username serviceName:self.service updateExisting:YES error:nil];
+ STAssertTrue(successfullyUpdated, @"updating an existing password failed");
+
+ NSString *password = [SGKeychain passwordForUsername:self.username serviceName:self.service error:nil];
+ STAssertEqualObjects(password, newPassword, @"Incorrect password fetched after update");
+}
+
+- (void)testPasswordIsSuccessfullyFetched
+{
+ NSString *testpassword = @"testpassword";
+ [SGKeychain setPassword:testpassword username:self.username serviceName:self.service updateExisting:NO error:nil];
+
+ NSString *password = [SGKeychain passwordForUsername:self.username serviceName:self.service error:nil];
+ STAssertEqualObjects(password, expectedPassword, @"Expected password not fetched from keychain.");
+}
+
+- (void)testPasswordsAreSuccessfullyFetchedFromSameAccessGroup
+{
+ NSString *accessGroup = @"com.secondgear";
+
+ // Add a password for justin and justinw to the access group
+ NSString *firstpassword = @"firstpassword";
+ [SGKeychain setPassword:firstpassword username:self.username serviceName:self.service accessGroup:accessGroup updateExisting:NO error:nil];
+
+ NSString *secondpassword = @"secondpassword";
+ [SGKeychain setPassword:secondpassword username:@"justinw" serviceName:self.service accessGroup:accessGroup updateExisting:NO error:nil];
+
+
+ // Ensure that the passwords can be retrieved
+ NSString *password1 = [SGKeychain passwordForUsername:self.username serviceName:self.service accessGroup:accessGroup error:nil];
+ STAssertEqualObjects(password1, firstpassword, @"Expected password for justin not fetched from keychain access group.");
+
+ NSString *password2 = [SGKeychain passwordForUsername:@"justinw" serviceName:self.service accessGroup:accessGroup error:nil];
+ STAssertEqualObjects(password2, secondpassword, @"Expected password for justinw not fetched from keychain access group.");
+
+ // Delete the passwords
+ [SGKeychain deletePasswordForUsername:self.username serviceName:self.service accessGroup:accessGroup error:nil];
+ [SGKeychain deletePasswordForUsername:@"justinw" serviceName:self.service accessGroup:accessGroup error:nil];
+}
+
+- (void)testErrorReturnedWhenPassingNilValuesOnFetch
+{
+ NSError *error = nil;
+ NSString *password = [SGKeychain passwordForUsername:nil serviceName:self.service error:&error];
+ STAssertNil(password, @"didn't expect to get a password back");
+
+ STAssertTrue([error code] == -666, @"Error code received not as expected");
+}
+
+
+- (void)testPasswordIsSuccessfullyDeleted
+{
+ NSString *testpassword = @"testpassword";
+ [SGKeychain setPassword:testpassword username:self.username serviceName:self.service updateExisting:NO error:nil];
+
+ BOOL successfullyDeleted = [SGKeychain deletePasswordForUsername:self.username serviceName:self.service error:nil];
+ STAssertTrue(successfullyDeleted, @"deleting an existing password failed");
+}
+
+- (void)testErrorReturnedWhenPassingNilValuesOnDelete
+{
+ NSError *error = nil;
+ [SGKeychain deletePasswordForUsername:nil serviceName:self.service error:&error];
+ STAssertTrue([error code] == -666, @"Error code received not as expected");
+}
+
+@end
2  SGKeychainExample/SGKeychainExampleTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
Please sign in to comment.
Something went wrong with that request. Please try again.