iOS Keychain 保存 UUID

介绍

在 iOS 开发中,我们经常需要保存一些敏感信息,比如用户的登录状态、设备的唯一标识等。为了保护这些敏感信息,苹果提供了一个安全的存储方式,即 Keychain。Keychain 是一个加密的存储空间,可以安全地保存密码、证书、私钥等敏感数据。本文将介绍如何使用 Keychain 来保存 UUID。

UUID

UUID(Universally Unique Identifier)是一种由 32 个字符组成的唯一标识符。它在许多场景中被广泛使用,比如设备标识、会话标识等。在 iOS 开发中,我们可以使用 UUID 类来生成 UUID。

import UIKit

let uuid = UUID()
print(uuid.uuidString)

以上代码会生成一个随机的 UUID,并将其打印出来。

Keychain

Keychain 是 iOS 系统提供的一种安全存储机制,可以用于保存敏感信息。在使用 Keychain 之前,我们需要导入 Security.framework

import UIKit
import Security

// 定义 Keychain 错误类型
enum KeychainError: Error {
    case noValue
    case unexpectedValueData
    case unexpectedItemData
    case itemNotFound
    case unableToConvertToString
    case unableToConvertToData
    case unableToSave
    case unableToDelete
}

// 定义 Keychain 常量
let service = "com.example.app"
let account = "uuid"

// 保存 UUID 到 Keychain
func saveUUID(uuidString: String) throws {
    guard let uuidData = uuidString.data(using: .utf8) else {
        throw KeychainError.unableToConvertToData
    }
    
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: service,
        kSecAttrAccount as String: account,
        kSecValueData as String: uuidData
    ]
    
    let status = SecItemAdd(query as CFDictionary, nil)
    if status != errSecSuccess {
        throw KeychainError.unableToSave
    }
}

// 从 Keychain 加载 UUID
func loadUUID() throws -> String {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: service,
        kSecAttrAccount as String: account,
        kSecReturnData as String: kCFBooleanTrue!,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]
    
    var result: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &result)
    guard status != errSecItemNotFound else {
        throw KeychainError.itemNotFound
    }
    guard status == errSecSuccess else {
        throw KeychainError.unexpectedItemData
    }
    
    guard let data = result as? Data, let uuid = String(data: data, encoding: .utf8) else {
        throw KeychainError.unableToConvertToString
    }
    
    return uuid
}

// 删除 Keychain 中的 UUID
func deleteUUID() throws {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: service,
        kSecAttrAccount as String: account
    ]
    
    let status = SecItemDelete(query as CFDictionary)
    guard status == errSecSuccess || status == errSecItemNotFound else {
        throw KeychainError.unableToDelete
    }
}

// 使用示例
do {
    let uuid = UUID().uuidString
    
    try saveUUID(uuidString: uuid)
    let loadedUUID = try loadUUID()
    print("Loaded UUID: \(loadedUUID)")
    
    try deleteUUID()
    let deletedUUID = try loadUUID()
    print("Deleted UUID: \(deletedUUID)")
} catch {
    print("Error: \(error)")
}

以上代码定义了几个函数,分别用于保存、加载和删除 Keychain 中的 UUID。具体来说,saveUUID 函数将 UUID 转换成 Data,并通过 Keychain API 将其保存到 Keychain 中;loadUUID 函数从 Keychain 中加载 UUID,并将其转换成 String;deleteUUID 函数从 Keychain 中删除 UUID。

在使用 Keychain 时,我们需要提供一个 Service 名称和一个 Account 名称。Service 名称可以是任意字符串,用于区分不同的服务;Account 名称用于唯一标识存储的数据。在上述代码中,我们使用 com.example.app 作为 Service 名称,使用 uuid 作为 Account 名称。