読者です 読者をやめる 読者になる 読者になる

ふるすたっくえんじにあっぽい人の日記

ASP .NET MVC (C#)、.NET Framework、iOS (Objective-c) アプリ、Androidアプリ (Java)、AWS、たまにLAMPとかプロジェクトマネジメントあたりのお話

【iOS/Objective-c】キーチェーンラッパー

iOS Objective-c

まぁオープンソースとかでもいろいろあるんだけどさ。
ソース汚いし。
諸事情で使えないので作った。
俺が書いたらこんな短いのに。
同じ処理を3倍以上の行数で書いてるのがgithubとかにあって、あーぁってなる。

KeyChainWrapper.h

#import <Foundation/Foundation.h>

/*!
 @class KeyChainWrapper
 @brief キーチェーンラッパー
 */
@interface KeyChainClient : NSObject

/*!
 @brief インスタンス生成
 @param classValue Class Value Constants
 @param accessible Attribute Key Constants
 */
- (id)initWithClassValue:(CFTypeRef)classValue accessible:(CFTypeRef)accessible;

/*!
 @brief KeyChainに値を保存
 @param value 値
 @param key キー
 */
- (BOOL)saveValueToKeyChain:(id)value forKey:(NSString *)key;

/*!
 @brief KeyChainから値を削除
 @param key キー
 */
- (BOOL)removeValueFromKeyChainForKey:(NSString *)key;

/*!
 @brief KeyChainから値を取得
 @param key キー
 */
- (id)valueFromKeyChainForKey:(NSString *)key;

@end

KeyChainWrapper.m

#import "KeyChainWrapper.h"

@interface KeyChainWrapper()

@property (nonatomic) CFTypeRef classValue;
@property (nonatomic) CFTypeRef accessible;

@end

@implementation KeyChainWrapper

- (id)init {
    [self doesNotRecognizeSelector:_cmd];
    return nil;
}

- (id)initWithClassValue:(CFTypeRef)classValue accessible:(CFTypeRef)accessible {
    self = [super init];
    if (self) {
        self.classValue = classValue;
        self.accessible = accessible;
    }
    return self;
}

- (BOOL)saveValueToKeyChain:(id)value forKey:(NSString *)key {
    [self removeValueFromKeyChainForKey:key];
    NSDictionary *optional = @{ (__bridge id)kSecValueData : [NSKeyedArchiver archivedDataWithRootObject:value] };
    return SecItemAdd([self keyChainAttributesForKey:key optional:optional], NULL) == noErr;
}

- (BOOL)removeValueFromKeyChainForKey:(NSString *)key {
    return SecItemDelete([self keyChainAttributesForKey:key]) == noErr;
}

- (id)valueFromKeyChainForKey:(NSString *)key {
    NSDictionary *optional = @{
                               (__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue,
                               (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne
                               };
    id value;
    CFDataRef keyData;
    if (SecItemCopyMatching([self keyChainAttributesForKey:key optional:optional], (CFTypeRef *)&keyData) == noErr) {
        @try {
            value = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        }
        @catch (NSException *e) {
            value = nil;
        }
        @finally {}
    }
    
    if (keyData)
        CFRelease(keyData);
    
    return value;
}

- (CFDictionaryRef)keyChainAttributesForKey:(NSString *)key {
    return (__bridge CFDictionaryRef)
    [NSMutableDictionary dictionaryWithDictionary:
     @{
       (__bridge id)kSecClass : (__bridge id)self.classValue,
       (__bridge id)kSecAttrService : key,
       (__bridge id)kSecAttrAccount : key,
       (__bridge id)kSecAttrAccessible : (__bridge id)self.accessible,
       }];
}

- (CFDictionaryRef)keyChainAttributesForKey:(NSString *)key optional:(NSDictionary *)optional {
    NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:
                                (__bridge NSDictionary*)[self keyChainAttributesForKey:key]];
    for (NSString *optionalKey in optional.allKeys)
        [dic setObject:optional[optionalKey] forKey:optionalKey];
    
    return (__bridge CFDictionaryRef)dic;
}

@end

つかいかた!

        KeyChainWrapper *keyChainWrapper = [[KeyChainWrapper alloc] initWithClassValue:kSecClassGenericPassword accessible:kSecAttrAccessibleAfterFirstUnlock];
        [keyChainWrapper saveValueToKeyChain:@"sdjfhjsdfhksdfkds" forKey:@"key"];
        NSLog(@"%@", [keyChainWrapper valueFromKeyChainForKey:@"key"]);

        [keyChainWrapper removeValueFromKeyChainForKey:@"key"];