Last active
December 20, 2016 09:41
-
-
Save hide1202/8cc61b4e837263fbde98151dd22344c5 to your computer and use it in GitHub Desktop.
iOS 키체인을 위한 헬퍼 클래스 입니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// KeyChain.h | |
// AppGroupProducer | |
// | |
// Created by VIEWPOINT on 2016. 12. 20.. | |
// Copyright © 2016년 VIEWPOINT. All rights reserved. | |
// | |
#ifndef KeyChain_h | |
#define KeyChain_h | |
#import <Foundation/Foundation.h> | |
#import <Security/Security.h> | |
// Reference to http://hayageek.com/ios-keychain-tutorial/ | |
@interface KeyChain : NSObject | |
+ (NSString *)groupIdWithAppIdPrefixWithKey:(NSString *)key; | |
- (id)initWithService:(NSString *)service_ withGroup:(NSString *)group_; | |
- (NSMutableDictionary *)prepareDict:(NSString *)key; | |
- (BOOL)insertWithKey:(NSString *)key :(NSData *)data; | |
- (NSData *)dataWithKey:(NSString *)key; | |
@end | |
@implementation KeyChain { | |
NSString *service; | |
NSString *group; | |
} | |
+ (NSString *)bundleSeedID { | |
NSDictionary *query = @{ | |
(__bridge NSString *) kSecClass: (__bridge NSString *) kSecClassGenericPassword, | |
(__bridge NSString *) kSecAttrAccount: @"bundleSeedID", | |
(__bridge NSString *) kSecAttrService: @"", | |
(__bridge NSString *) kSecReturnAttributes: (id) kCFBooleanTrue | |
}; | |
CFDictionaryRef result = nil; | |
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef *) &result); | |
if (status == errSecItemNotFound) | |
status = SecItemAdd((__bridge CFDictionaryRef) query, (CFTypeRef *) &result); | |
if (status != errSecSuccess) | |
return nil; | |
NSString *accessGroup = ((__bridge NSDictionary *) result)[(__bridge NSString *) kSecAttrAccessGroup]; | |
NSArray *components = [accessGroup componentsSeparatedByString:@"."]; | |
NSString *bundleSeedID = [[components objectEnumerator] nextObject]; | |
CFRelease(result); | |
return bundleSeedID; | |
} | |
/** | |
* Return a group identifier appended with Application Identifier Prefix. | |
* @param key Identifier except for Application Identifier Prefix | |
* @return Group Identifier | |
*/ | |
+ (NSString *)groupIdWithAppIdPrefixWithKey:(NSString *)key { | |
NSString *seedId = [KeyChain bundleSeedID]; | |
NSString *result = [NSString stringWithFormat:@"%@.%@", seedId, key]; | |
NSLog(@"SeedId : %@, GroupId : %@", seedId, result); | |
return result; | |
} | |
- (id)initWithService:(NSString *)service_ withGroup:(NSString *)group_ { | |
self = [super init]; | |
if (self) { | |
service = [NSString stringWithString:service_]; | |
if (group_) | |
group = [NSString stringWithString:group_]; | |
} | |
return self; | |
} | |
- (NSMutableDictionary *)prepareDict:(NSString *)key { | |
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; | |
dict[(__bridge id) kSecClass] = (__bridge id) kSecClassGenericPassword; | |
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding]; | |
dict[(__bridge id) kSecAttrGeneric] = encodedKey; | |
dict[(__bridge id) kSecAttrAccount] = encodedKey; | |
dict[(__bridge id) kSecAttrService] = service; | |
dict[(__bridge id) kSecAttrAccessible] = (__bridge id) kSecAttrAccessibleAlwaysThisDeviceOnly; | |
//This is for sharing data across apps | |
if (group != nil) | |
dict[(__bridge id) kSecAttrAccessGroup] = group; | |
return dict; | |
} | |
- (BOOL)insertWithKey:(NSString *)key :(NSData *)data { | |
NSMutableDictionary *dict = [self prepareDict:key]; | |
dict[(__bridge id) kSecValueData] = data; | |
OSStatus status = SecItemAdd((__bridge CFDictionaryRef) dict, NULL); | |
if (errSecSuccess != status) { | |
NSLog(@"Unable add item with key =%@ error:%d", key, (int) status); | |
} | |
return (errSecSuccess == status); | |
} | |
- (BOOL)update:(NSString *)key :(NSData *)data { | |
NSMutableDictionary *dictKey = [self prepareDict:key]; | |
NSMutableDictionary *dictUpdate = [[NSMutableDictionary alloc] init]; | |
dictUpdate[(__bridge id) kSecValueData] = data; | |
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef) dictKey, (__bridge CFDictionaryRef) dictUpdate); | |
if (errSecSuccess != status) { | |
NSLog(@"Unable add update with key =%@ error:%ld", key, status); | |
} | |
return (errSecSuccess == status); | |
return YES; | |
} | |
- (BOOL)remove:(NSString *)key { | |
NSMutableDictionary *dict = [self prepareDict:key]; | |
OSStatus status = SecItemDelete((__bridge CFDictionaryRef) dict); | |
if (status != errSecSuccess) { | |
NSLog(@"Unable to remove item for key %@ with error:%ld", key, status); | |
return NO; | |
} | |
return YES; | |
} | |
- (NSData *)dataWithKey:(NSString *)key { | |
NSMutableDictionary *dict = [self prepareDict:key]; | |
dict[(__bridge id) kSecMatchLimit] = (__bridge id) kSecMatchLimitOne; | |
dict[(__bridge id) kSecReturnData] = (id) kCFBooleanTrue; | |
CFTypeRef result = NULL; | |
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) dict, &result); | |
if (status != errSecSuccess) { | |
NSLog(@"Unable to fetch item for key %@ with error:%d", key, (int) status); | |
return nil; | |
} | |
return (__bridge NSData *) result; | |
} | |
@end | |
#endif /* KeyChain_h */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.m | |
// AppGroupProducer | |
// | |
// Created by VIEWPOINT on 2016. 12. 16.. | |
// Copyright © 2016년 VIEWPOINT. All rights reserved. | |
// | |
#import "ViewController.h" | |
#import "KeyChain.h" | |
#define KEYCHAIN | |
//#define APP_GROUP | |
@interface ViewController () | |
@property(weak, nonatomic) IBOutlet UITextField *textField; | |
@end | |
NSString *kDefaultSuiteName = @"group.viewpoint.test"; | |
@implementation ViewController | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
// Do any additional setup after loading the view, typically from a nib. | |
[self load]; | |
} | |
- (void)load { | |
#ifdef APP_GROUP | |
NSUserDefaults* sharedDefault = [[NSUserDefaults alloc] initWithSuiteName:kDefaultSuiteName]; | |
NSString* text = [sharedDefault objectForKey:@"text"]; | |
if(text) | |
self.textField.text = text; | |
#else | |
KeyChain *chain = [[KeyChain alloc] initWithService:@"Service" | |
withGroup:[KeyChain groupIdWithAppIdPrefixWithKey:@"com.viewpoint.AppGroupProducer"]]; | |
NSData *data = [chain dataWithKey:@"text"]; | |
if (data) | |
self.textField.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; | |
#endif | |
} | |
- (IBAction)onClickSave:(id)sender { | |
#ifdef APP_GROUP | |
NSString* text = [self.textField text]; | |
NSUserDefaults* sharedDefault = [[NSUserDefaults alloc] initWithSuiteName:kDefaultSuiteName]; | |
[sharedDefault setObject:text forKey:@"text"]; | |
[sharedDefault synchronize]; | |
#else | |
KeyChain *chain = [[KeyChain alloc] initWithService:@"Service" | |
withGroup:[KeyChain groupIdWithAppIdPrefixWithKey:@"com.viewpoint.AppGroupProducer"]]; | |
if ([chain dataWithKey:@"text"] == nil) | |
[chain insertWithKey:@"text" :[[self.textField text] dataUsingEncoding:NSUTF8StringEncoding]]; | |
else | |
[chain update:@"text" :[[self.textField text] dataUsingEncoding:NSUTF8StringEncoding]]; | |
#endif | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment