diff options
author | Jonathan Willing <jwilling@me.com> | 2013-04-08 14:31:23 -0500 |
---|---|---|
committer | Jonathan Willing <jwilling@me.com> | 2013-04-08 14:31:23 -0500 |
commit | 351f468c24d0e42f168232528b59f2a4ad47919f (patch) | |
tree | e4fb3bd20536abca4724f779b140ac7b0ee4861a /example/common | |
parent | e5541c30ca54aee7392d14591d54338cc1782e81 (diff) |
add preliminary iOS example
Diffstat (limited to 'example/common')
-rwxr-xr-x | example/common/FXKeychain.h | 54 | ||||
-rwxr-xr-x | example/common/FXKeychain.m | 197 | ||||
-rwxr-xr-x | example/common/FXKeychain_LICENCE.md | 21 |
3 files changed, 272 insertions, 0 deletions
diff --git a/example/common/FXKeychain.h b/example/common/FXKeychain.h new file mode 100755 index 00000000..3e80c817 --- /dev/null +++ b/example/common/FXKeychain.h @@ -0,0 +1,54 @@ +// +// FXKeychain.h +// +// Version 1.3.1 +// +// Created by Nick Lockwood on 29/12/2012. +// Copyright 2012 Charcoal Design +// +// Distributed under the permissive zlib License +// Get the latest version from here: +// +// https://github.com/nicklockwood/FXKeychain +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + + +#import <Foundation/Foundation.h> +#import <Security/Security.h> + + +@interface FXKeychain : NSObject + ++ (instancetype)defaultKeychain; + +@property (nonatomic, copy, readonly) NSString *service; +@property (nonatomic, copy, readonly) NSString *accessGroup; + +- (id)initWithService:(NSString *)service + accessGroup:(NSString *)accessGroup; + +- (BOOL)setObject:(id)object forKey:(id)key; +- (BOOL)setObject:(id)object forKeyedSubscript:(id)key; +- (BOOL)removeObjectForKey:(id)key; +- (id)objectForKey:(id)key; +- (id)objectForKeyedSubscript:(id)key; + +@end diff --git a/example/common/FXKeychain.m b/example/common/FXKeychain.m new file mode 100755 index 00000000..f4cb4388 --- /dev/null +++ b/example/common/FXKeychain.m @@ -0,0 +1,197 @@ +// +// FXKeychain.m +// +// Version 1.3.2 +// +// Created by Nick Lockwood on 29/12/2012. +// Copyright 2012 Charcoal Design +// +// Distributed under the permissive zlib License +// Get the latest version from here: +// +// https://github.com/nicklockwood/FXKeychain +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + + +#import "FXKeychain.h" + + +#import <Availability.h> +#if !__has_feature(objc_arc) +#error This class requires automatic reference counting +#endif + + +@implementation FXKeychain + ++ (instancetype)defaultKeychain +{ + static id sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey]; + sharedInstance = [[FXKeychain alloc] initWithService:bundleID + accessGroup:nil]; + }); + + return sharedInstance; +} + +- (id)init +{ + return [self initWithService:nil accessGroup:nil]; +} + +- (id)initWithService:(NSString *)service + accessGroup:(NSString *)accessGroup +{ + if ((self = [super init])) + { + _service = [service copy]; + _accessGroup = [accessGroup copy]; + } + return self; +} + +- (BOOL)setObject:(id)object forKey:(id)key +{ + NSParameterAssert(key); + + //generate query + NSMutableDictionary *query = [NSMutableDictionary dictionary]; + if ([_service length]) query[(__bridge NSString *)kSecAttrService] = _service; + query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword; + query[(__bridge NSString *)kSecAttrAccount] = [key description]; + +#if defined __IPHONE_OS_VERSION_MAX_ALLOWED && !TARGET_IPHONE_SIMULATOR + if ([_accessGroup length]) query[(__bridge NSString *)kSecAttrAccessGroup] = _accessGroup; +#endif + + //encode object + NSData *data = nil; + NSError *error = nil; + if ([(id)object isKindOfClass:[NSString class]]) + { + data = [(NSString *)object dataUsingEncoding:NSUTF8StringEncoding]; + } + else if (object) + { + data = [NSPropertyListSerialization dataWithPropertyList:object + format:NSPropertyListBinaryFormat_v1_0 + options:0 + error:&error]; + } + + //fail if object is invalid + NSAssert(!object || (object && data), @"FXKeychain failed to encode object for key '%@', error: %@", key, error); + + //delete existing data + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + + //write data + if (data) + { + query[(__bridge NSString *)kSecValueData] = data; + status = SecItemAdd ((__bridge CFDictionaryRef)query, NULL); + if (status != errSecSuccess) + { + NSLog(@"FXKeychain failed to store data for key '%@', error: %ld", key, (long)status); + return NO; + } + } + else if (status != errSecSuccess) + { + NSLog(@"FXKeychain failed to delete data for key '%@', error: %ld", key, (long)status); + return NO; + } + return YES; +} + +- (BOOL)setObject:(id)object forKeyedSubscript:(id)key +{ + return [self setObject:object forKey:key]; +} + +- (BOOL)removeObjectForKey:(id)key +{ + return [self setObject:nil forKey:key]; +} + +- (id)objectForKey:(id)key +{ + NSParameterAssert(key); + + //generate query + NSMutableDictionary *query = [NSMutableDictionary dictionary]; + if ([_service length]) query[(__bridge NSString *)kSecAttrService] = _service; + query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword; + query[(__bridge NSString *)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; + query[(__bridge NSString *)kSecReturnData] = (__bridge id)kCFBooleanTrue; + query[(__bridge NSString *)kSecAttrAccount] = [key description]; + +#if defined __IPHONE_OS_VERSION_MAX_ALLOWED && !TARGET_IPHONE_SIMULATOR + if ([_accessGroup length]) query[(__bridge NSString *)kSecAttrAccessGroup] = _accessGroup; +#endif + + //recover data + id object = nil; + NSError *error = nil; + CFDataRef data = nil; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&data); + if (status == errSecSuccess && data) + { + //attempt to decode as a plist + object = [NSPropertyListSerialization propertyListWithData:(__bridge NSData *)data + options:NSPropertyListImmutable + format:NULL + error:&error]; + + if ([object respondsToSelector:@selector(objectForKey:)] && object[@"$archiver"]) + { + //data represents an NSCoded archive. don't trust it + object = nil; + } + else if (!object) + { + //may be a string + object = [[NSString alloc] initWithData:(__bridge NSData *)data encoding:NSUTF8StringEncoding]; + } + if (!object) + { + NSLog(@"FXKeychain failed to decode data for key '%@', error: %@", key, error); + } + CFRelease(data); + return object; + } + else + { + //no value found + return nil; + } +} + +- (id)objectForKeyedSubscript:(id)key +{ + return [self objectForKey:key]; +} + +@end diff --git a/example/common/FXKeychain_LICENCE.md b/example/common/FXKeychain_LICENCE.md new file mode 100755 index 00000000..68a5f4f5 --- /dev/null +++ b/example/common/FXKeychain_LICENCE.md @@ -0,0 +1,21 @@ +FXKeychain + +Version 1.3.2, April 4th, 2013 + +Copyright (C) 2012 Charcoal Design + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution.
\ No newline at end of file |