坚持 成长 每日一篇
在开发过程中我们有时候在用户删除时候保存一些信息在用户下次安装应用时候使用,这个时候我们可以使用剪切版UIPasteboard的FindUIPasteboard和钥匙串keychain的使用
剪切版主要分为以下两种
UIPasteboardNameGeneral和UIPasteboardNameFind
两种都是系统级的可以在应用删除后仍然保留数据
开发中我们常常使用UIPasteboard的Find UIPasteboard来保存一些用户删除应用后需要保留的数据如UUID,用户名,密码等
下面是一个保存uuid到UIPasteboard的代码
创建UDID
+ (NSString*) _uuidCreateNewUDID {
NSString* _openUDID = nil;
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef cfstring = CFUUIDCreateString(kCFAllocatorDefault, uuid);
const char *cStr = CFStringGetCStringPtr(cfstring, CFStringGetFastestEncoding(cfstring));
unsigned char result[16];
CC_MD5(cStr, (unsigned int)strlen(cStr), result);
CFRelease(uuid);
_openUDID = [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%08x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15],
(unsigned int)(arc4random() % NSUIntegerMax)];
CFRelease(cfstring);
return _openUDID;
}
保存UDID到剪切板
NSString *openudid = [self _uuidCreateNewUDID];
NSString *checkOpenUDID = [openudid stringByAppendingFormat:kOpenUDIDAdd, nil];
//先把UDID存入字典
NSMutableDictionary *localDict = [NSMutableDictionary dictionaryWithCapacity:2];
[localDict setObject:openudid forKey:kOpenUDIDKey];
[localDict setObject:[checkOpenUDID md5] forKey:kCheckOpenUDIDKey];
// 同步数据到剪切板
UIPasteboard *slotPB = [UIPasteboard pasteboardWithName:kOpenUDIDPB create:YES];
// 设置为永久保存
[slotPB setPersistent:YES];
//把字典存入剪切版
[slotPB setData:[NSKeyedArchiver archivedDataWithRootObject:localDict] forPasteboardType:kOpenUDIDDict];
注意:在用户使用产品过程中我们发现,当用户在隐私里面关闭限制广告跟踪,然后用户删除应用的同时,剪切版的数据会被清空(也有可能因为硬盘容量紧张也回清除)
iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式。每个ios程序都有一个独立的keychain存储。从ios 3.0开始,跨程序分享keychain变得可行。
下面就使用keychain来实现存取用户名和密码。
苹果已经有现成的类封装好了keychain,KeychainItemWrapper.h和KeychainItemWrapper.m文件,可以在GenericKeychain实例里找到。
但是这里我只需要存取用户名和密码,就不用苹果提供的类了,自己写个简单的类来实现就好。
KeychainManager.h
#import
@interface KeychainManager : NSObject
+(KeychainManager*)default;
//根据字典存储对象到钥匙串
- (void)save:(NSString *)service data:(id)data;
//根据字典读取钥匙串里的对象
- (id)load:(NSString *)service;
//删除钥匙串里的数据
- (void)delete:(NSString *)service;
@end
KeychainManager.m
#import "KeychainManager.h"
@implementation KeychainManager
+(KeychainManager*)default
{
static KeychainManager *keychainManager = nil;
if(keychainManager == nil)
{
keychainManager = [[KeychainManager alloc] init];
}
return keychainManager;
}
- (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
- (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
- (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
- (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
在app delegate的使用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
KeychainManager *manager = [KeychainManager default];
NSString *data = [manager load:@"myName"];
if (data == nil) {
NSLog(@"Save");
NSString *dataString = @"我是谁";
[manager save:@"myName" data:dataString];
}
NSLog(@"data = %@",data);
return YES;
}
运行app你会发现Save只有在你第一次运行app时候打印,如果你把app删除后在运行,也不会清除数据。