前言
对于iOS的数据持久化,小的数据我们使用最多的肯定是UserDefaults,但是UserDefaults的弊端也比较明显:存取都是重写整个文件,效率低下、无法存储太大的数据、安全性低,很容易被攻破;当然这些只是题外话,我们今天要讲的KeyChain钥匙串,虽然也不能存储太大的数据,但是它的安全性很高,还可以随着iCould同步,苹果官方也推荐我们使用这个 来存储账号密码等重要的信息,那我们来看看怎么使用KeyChain。
keyChain
image.pngkeyChain的存储的信息都是一个个的字典,每个字典作为一个Item,item可以指定为以上类型,官方的类型解释为:
@enum Class Value Constants
@discussion Predefined item class constants used to get or set values in a dictionary. The kSecClass constant is the key and its value is one of the constants defined here. Note: on Mac OS X 10.6, only items of class kSecClassInternetPassword are supported.
@constant kSecClassInternetPassword Specifies Internet password items.
@constant kSecClassGenericPassword Specifies generic password items.
@constant kSecClassCertificate Specifies certificate items.
@constant kSecClassKey Specifies key items.
@constant kSecClassIdentity Specifies identity items.
我们普通账号密码就使用kSecClassGenericPassword(一般密码)就好,keyChain的常用方法如下
public func SecItemCopyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CoreFoundation.CFTypeRef?>?) -> OSStatus
public func SecItemAdd(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CoreFoundation.CFTypeRef?>?) -> OSStatus
public func SecItemUpdate(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus
public func SecItemDelete(_ query: CFDictionary) -> OSStatus
顾名思义,分别是查找、添加、更新和删除,直接将我们设置好的Item的字段作为参数传入就可以调用,不过我们这里适当封装一下,便于我们使用
工具封装
代码如下
//
// KeyChainTool.swift
//
// Created by Fxxx on 2017/12/6.
// Copyright © 2017年 Fxxx. All rights reserved.
//
import UIKit
class KeyChainTool: NSObject {
//工具单例类
static let shared = KeyChainTool()
//item字典
private func keyChainQueryDictionaryWithKey(key: String) -> NSMutableDictionary {
let dic = NSMutableDictionary()
dic.setObject(kSecClassGenericPassword, forKey: kSecClass as! NSCopying)
dic.setObject(kSecAttrAccessibleAfterFirstUnlock, forKey: kSecAttrAccessible as! NSCopying)
dic.setObject(key, forKey: kSecAttrService as! NSCopying)
dic.setObject(key, forKey: kSecAttrAccount as! NSCopying)
return dic
}
//新增
func addToKeyChain(data: Any, key: String) -> Bool {
let dic = self.keyChainQueryDictionaryWithKey(key: key)
SecItemDelete(dic)
dic.setObject(NSKeyedArchiver.archivedData(withRootObject: data), forKey: kSecValueData as! NSCopying)
let status = SecItemAdd(dic, nil)
if status == noErr {
return true
}
return false
}
//更新
func updateData(data: Any, key: String) -> Bool {
let oldDic = self.keyChainQueryDictionaryWithKey(key: key)
guard SecItemCopyMatching(oldDic, nil) == noErr else {
return false
}
let updateDic = NSMutableDictionary()
updateDic.setObject(NSKeyedArchiver.archivedData(withRootObject: data), forKey: kSecValueData as! NSCopying)
let status = SecItemUpdate(oldDic, updateDic)
if status == errSecSuccess {
return true
}
return false
}
//获取
func getDataForKey(key: String) -> Any? {
var result: Any?
let dic = self.keyChainQueryDictionaryWithKey(key: key)
dic.setObject(kCFBooleanTrue, forKey: kSecReturnData as! NSCopying)
dic.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit as! NSCopying)
var queryResult: CFTypeRef?
if SecItemCopyMatching(dic, &queryResult) == noErr {
if queryResult != nil {
result = NSKeyedUnarchiver.unarchiveObject(with: queryResult! as! Data)
}
}
return result
}
//删除
func removeDataForKey(key: String) -> Bool {
let dic = self.keyChainQueryDictionaryWithKey(key: key)
let status = SecItemDelete(dic)
if status == noErr {
return true
}
return false
}
}
这里我使用了单例模式来优化,将主要的功能进行了封装,具体的使用操作在结尾Demo里,欢迎下载查看
Group分享
image.png要想在不同的应用之间共享我们钥匙串中的数据,必须为我们的Keychain设置Group,在Capabilities里,打开
Keychain Sharing
,打开后我可以看到Group默认就是我们的Bundle ID,如果要分享给其他应用,在这里点击“+”添加那个应用的Bundle ID就好。
PS:之前看到很多教程,使用Keychain即使不分享也要打开Keychain Sharing,这里我试过了,不打开也不影响本地的存储和使用的,我使用的是Xcode9和swift4.
网友评论