<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>Tests/GHEMKeychainStoreTest.m</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -28,13 +28,49 @@
 
 #import &lt;Foundation/Foundation.h&gt;
 
+extern NSString *const GHEMKeychainStoreErrorDomain;
+
+typedef enum {
+	GHEMKeychainStoreErrorCodeInvalidSecret = 1
+} GHEMKeychainStoreErrorCode;
+
 /*!
  Protocol for keychain storage. 
  */
 @protocol GHKeychainStore &lt;NSObject&gt;
-+ (id)keychain;
-- (NSString *)secretFromKeychain:(NSString *)key serviceName:(NSString *)serviceName error:(NSError **)error;
-- (void)saveToKeychain:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error;
+
+/*!
+ Get secret from keychain.
+ @param key
+ @param serviceName
+ @param error
+ @result Secret
+ */
+- (NSString *)secretFromKeychainForServiceName:(NSString *)serviceName key:(NSString *)key error:(NSError **)error;
+
+/*!
+ Save secret to keychain.
+ @param serviceName
+ @param key
+ @param secret
+ @param error
+ @result NO if there was an error, YES otherwise
+ */
+- (BOOL)saveToKeychainWithServiceName:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error;
+
+@end
+
+/*!
+ Runtime based keychain store.
+ 
+ Forwards to:
+	- GHEMKeychainStore for Mac OS X.
+	- GHSFHFKeychainStore for iPhone.
+
+ */
+@interface GHKeychainStore : NSObject &lt;GHKeychainStore&gt; {
+	id&lt;GHKeychainStore&gt; _keychainStore;
+}
 
 @end
 </diff>
      <filename>Classes/GHKeychainStore.h</filename>
    </modified>
    <modified>
      <diff>@@ -28,54 +28,100 @@
 
 #import &quot;GHKeychainStore.h&quot;
 
-//
-// For Cocoa target, for GHKeychainStore uses EMKeychainProxy.
-// For iPhone target, for GHKeychainStore uses SFHFKeychainUtils.
-//
+#import &quot;GHNSError+Utils.h&quot;
 
-#ifndef TARGET_OS_IPHONE
+NSString *const GHEMKeychainStoreErrorDomain = @&quot;GHEMKeychainStoreErrorDomain&quot;;
 
-#import &quot;EMKeychainProxy.h&quot;
+@implementation GHKeychainStore
 
-@implementation GHEMKeychainStore
+- (id)init {
+	if ((self = [super init])) {
+#if TARGET_OS_IPHONE
+		_keychainStore = [[GHSFHFKeychainStore alloc] init];
+#else
+		_keychainStore = [[GHEMKeychainStore alloc] init];
+#endif
+	}
+	return self;
+}
 
-+ (id)keychain {
-	return [[[GHEMKeychainStore alloc] init] autorelease];
+- (void)dealloc {
+	[_keychainStore release];
+	[super dealloc];
 }
 
-// TODO: Set error on failure modes
-- (NSString *)secretFromKeychain:(NSString *)accessKey serviceName:(NSString *)serviceName error:(NSError **)error {
-  EMGenericKeychainItem *keychainItem = [[EMKeychainProxy sharedProxy] genericKeychainItemForService:serviceName withUsername:accessKey];    
+- (NSString *)secretFromKeychainForServiceName:(NSString *)serviceName key:(NSString *)key error:(NSError **)error {
+	return [_keychainStore secretFromKeychainForServiceName:serviceName key:key error:error];
+}
+
+- (BOOL)saveToKeychainWithServiceName:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error {
+	return [_keychainStore saveToKeychainWithServiceName:serviceName key:key secret:secret error:error];
+}
+
+
+@end
+
+#if !TARGET_OS_IPHONE
+
+#import &quot;EMKeychainProxy.h&quot;
+
+@implementation GHEMKeychainStore
+
+- (NSString *)secretFromKeychainForServiceName:(NSString *)serviceName key:(NSString *)key error:(NSError **)error {
+	// TODO: Set error on failure modes from EMGenericKeychainItem (which doesn't have any)
+  EMGenericKeychainItem *keychainItem = [[EMKeychainProxy sharedProxy] genericKeychainItemForService:serviceName withUsername:key];    
   return [keychainItem password];  
 }
 
-- (void)saveToKeychain:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error {  
-  if (!secret) return; // TODO: Handle as error
+- (BOOL)saveToKeychainWithServiceName:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error {  
+  if (!secret) {
+		if (error) *error = [NSError gh_errorWithDomain:GHEMKeychainStoreErrorDomain code:GHEMKeychainStoreErrorCodeInvalidSecret localizedDescription:@&quot;Invalid secret&quot;];
+		return NO;
+	}
   EMGenericKeychainItem *keychainItem = [[EMKeychainProxy sharedProxy] genericKeychainItemForService:serviceName withUsername:key];
   if (!keychainItem) {
     [[EMKeychainProxy sharedProxy] addGenericKeychainItemForService:serviceName withUsername:key password:secret];
   } else
-    [keychainItem setPassword:secret];  
+    [keychainItem setPassword:secret];
+  
+	return YES;
 }
 
 @end
 
-#else
+#endif
+
+
+#if TARGET_OS_IPHONE
 
 #import &quot;SFHFKeychainUtils.h&quot;
 
 @implementation GHSFHFKeychainStore
 
-+ (id)keychain {
-	return [[[GHSFHFKeychainStore alloc] init] autorelease];
-}
-
-- (NSString *)secretFromKeychain:(NSString *)key serviceName:(NSString *)serviceName error:(NSError **)error {
+- (NSString *)secretFromKeychainForServiceName:(NSString *)serviceName key:(NSString *)key error:(NSError **)error {
+	
+	if (!error) {
+		NSError *errorTmp = nil;
+		error = &amp;errorTmp;
+	}
+	
 	return [SFHFKeychainUtils getPasswordForUsername:key andServiceName:serviceName error:error];
 }
 
-- (void)saveToKeychain:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error {
+- (BOOL)saveToKeychainWithServiceName:(NSString *)serviceName key:(NSString *)key secret:(NSString *)secret error:(NSError **)error {
+	if (!secret) {
+		if (error) *error = [NSError gh_errorWithDomain:GHEMKeychainStoreErrorDomain code:GHEMKeychainStoreErrorCodeInvalidSecret localizedDescription:@&quot;Invalid secret&quot;];
+		return NO;
+	}
+	
+	if (!error) {
+		NSError *errorTmp = nil;
+		error = &amp;errorTmp;
+	}
+	
 	[SFHFKeychainUtils storeUsername:key andPassword:secret forServiceName:serviceName updateExisting:YES error:error];
+	if (error &amp;&amp; *error) return NO;
+	return YES;
 }
 
 @end</diff>
      <filename>Classes/GHKeychainStore.m</filename>
    </modified>
    <modified>
      <diff>@@ -32,6 +32,7 @@
 		00150F300FAD1281009A8633 /* GHLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 00150F2E0FAD1281009A8633 /* GHLogger.m */; };
 		00150F330FAD128D009A8633 /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 00150F310FAD128D009A8633 /* GTMLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		00150F340FAD128D009A8633 /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 00150F320FAD128D009A8633 /* GTMLogger.m */; };
+		001C8966105CB1FB005E25A1 /* GHEMKeychainStoreTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 001C8965105CB1FB005E25A1 /* GHEMKeychainStoreTest.m */; };
 		002239CC0FD07D62005645DE /* ReadonlyCopyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 002239CB0FD07D62005645DE /* ReadonlyCopyTest.m */; };
 		0024E7950F65942F007E5C51 /* GHNSError+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0024E7890F659428007E5C51 /* GHNSError+Utils.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		0024E7960F65942F007E5C51 /* GHNSError+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 0024E78A0F659428007E5C51 /* GHNSError+Utils.m */; };
@@ -170,6 +171,7 @@
 		00150F2E0FAD1281009A8633 /* GHLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHLogger.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		00150F310FAD128D009A8633 /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = &quot;&lt;group&gt;&quot;; };
 		00150F320FAD128D009A8633 /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = &quot;&lt;group&gt;&quot;; };
+		001C8965105CB1FB005E25A1 /* GHEMKeychainStoreTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHEMKeychainStoreTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		002239CB0FD07D62005645DE /* ReadonlyCopyTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReadonlyCopyTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		0024E7890F659428007E5C51 /* GHNSError+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = &quot;GHNSError+Utils.h&quot;; sourceTree = &quot;&lt;group&gt;&quot;; tabWidth = 2; };
 		0024E78A0F659428007E5C51 /* GHNSError+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = &quot;GHNSError+Utils.m&quot;; sourceTree = &quot;&lt;group&gt;&quot;; tabWidth = 2; };
@@ -545,6 +547,7 @@
 				00AA12B91005586B00667B93 /* NSObjectUtilsTest.m */,
 				0075FB4110066DEE00F6ED5A /* NSArrayUtilsTest.m */,
 				00640FC2100717F60052BB61 /* NSNumberUtilsTest.m */,
+				001C8965105CB1FB005E25A1 /* GHEMKeychainStoreTest.m */,
 			);
 			path = Tests;
 			sourceTree = &quot;&lt;group&gt;&quot;;
@@ -726,6 +729,7 @@
 				00AA12BA1005586B00667B93 /* NSObjectUtilsTest.m in Sources */,
 				0075FB4210066DEE00F6ED5A /* NSArrayUtilsTest.m in Sources */,
 				00640FC3100717F60052BB61 /* NSNumberUtilsTest.m in Sources */,
+				001C8966105CB1FB005E25A1 /* GHEMKeychainStoreTest.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};</diff>
      <filename>GHKit.xcodeproj/project.pbxproj</filename>
    </modified>
    <modified>
      <diff>@@ -13,6 +13,7 @@
 		00025C860EFB654C007EA0A8 /* GHUIColorUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00025C850EFB654C007EA0A8 /* GHUIColorUtilsTest.m */; };
 		00178C450FC37D5200CA6A07 /* GHNSInvocationProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 00178C440FC37D5200CA6A07 /* GHNSInvocationProxy.m */; };
 		00178E810FC39A1900CA6A07 /* NSInvocationUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00178E800FC39A1900CA6A07 /* NSInvocationUtilsTest.m */; };
+		001C89C3105CB7D7005E25A1 /* GHEMKeychainStoreTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 001C89C2105CB7D7005E25A1 /* GHEMKeychainStoreTest.m */; };
 		0022F2A2105C809500EF9FC5 /* NSURLUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0022F290105C809500EF9FC5 /* NSURLUtilsTest.m */; };
 		0022F2A3105C809500EF9FC5 /* NSErrorUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0022F291105C809500EF9FC5 /* NSErrorUtilsTest.m */; };
 		0022F2A4105C809500EF9FC5 /* NSArrayUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0022F292105C809500EF9FC5 /* NSArrayUtilsTest.m */; };
@@ -99,6 +100,7 @@
 		00178C430FC37D5200CA6A07 /* GHNSInvocationProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GHNSInvocationProxy.h; sourceTree = &quot;&lt;group&gt;&quot;; };
 		00178C440FC37D5200CA6A07 /* GHNSInvocationProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHNSInvocationProxy.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		00178E800FC39A1900CA6A07 /* NSInvocationUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSInvocationUtilsTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
+		001C89C2105CB7D7005E25A1 /* GHEMKeychainStoreTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GHEMKeychainStoreTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		0022F290105C809500EF9FC5 /* NSURLUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLUtilsTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		0022F291105C809500EF9FC5 /* NSErrorUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSErrorUtilsTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		0022F292105C809500EF9FC5 /* NSArrayUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSArrayUtilsTest.m; sourceTree = &quot;&lt;group&gt;&quot;; };
@@ -438,6 +440,7 @@
 		00DB2AE10EECA3BA00383FB6 /* Tests */ = {
 			isa = PBXGroup;
 			children = (
+				001C89C2105CB7D7005E25A1 /* GHEMKeychainStoreTest.m */,
 				0022F290105C809500EF9FC5 /* NSURLUtilsTest.m */,
 				0022F291105C809500EF9FC5 /* NSErrorUtilsTest.m */,
 				0022F292105C809500EF9FC5 /* NSArrayUtilsTest.m */,
@@ -685,6 +688,7 @@
 				0022F2BF105C80C100EF9FC5 /* GHNSStringEnumerator.m in Sources */,
 				0022F2C0105C80C100EF9FC5 /* GHNSMutableArray+Utils.m in Sources */,
 				00ED868C105C8CF600EEA5AB /* GHUnitIPhoneTestMain.m in Sources */,
+				001C89C3105CB7D7005E25A1 /* GHEMKeychainStoreTest.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};</diff>
      <filename>GHKitIPhone.xcodeproj/project.pbxproj</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>5d8eb25ee97c315a397da1062f522552084a7d2e</id>
    </parent>
  </parents>
  <author>
    <name>Gabriel Handford</name>
    <email>gabrielh@gmail.com</email>
  </author>
  <url>http://github.com/gabriel/gh-kit/commit/121e469c73662b7d4ecba4f4d0dc20a02f7c16d9</url>
  <id>121e469c73662b7d4ecba4f4d0dc20a02f7c16d9</id>
  <committed-date>2009-09-12T22:27:27-07:00</committed-date>
  <authored-date>2009-09-12T22:27:27-07:00</authored-date>
  <message>Re-working GHKeychainStore</message>
  <tree>e41752c27cd1bf50d10ca9bc22b2f0a88fb1f6e0</tree>
  <committer>
    <name>Gabriel Handford</name>
    <email>gabrielh@gmail.com</email>
  </committer>
</commit>
