技術ブログ

プログラミング、IT関連の記事中心

キーチェーンの共通クラスの作成【Swift】

■はじめに

ライブラリは使用しないで、キーチェーンを使用する共通処理を記載します。

なるべく保守性の高いクラスにしようとして作成しました。

(今後、修正するかも。。。)

あくまで、参考までに。

■手順

「KeyChain.swift」クラスを作成する。

以下を丸々コピペで「KeyChain.swift」に貼り付けてください。

※KeyChainの一意のKeyはBundleIDにしています。

import Foundation

class KeyChain {
    // KeyChainクラスのインスタンス取得
    public static let shared = KeyChain()
    // KeyChainの一意のKey情報(BundleID)
    fileprivate let privateKey: String = Bundle.main.bundleIdentifier ?? ""

    // 保存
    func setKeyChain(_ value: String?, key: String) {
        let data = value?.data(using: .utf8)

        guard let _data = data else {
            deleteKeyChain(key: key)
            return
        }

        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                  kSecAttrGeneric as String: key,
                                  kSecAttrAccount as String: privateKey,
                                  kSecValueData as String: _data]

        var itemAddStatus: OSStatus?
        // 保存データが存在するかの確認
        let matchingStatus = SecItemCopyMatching(dic as CFDictionary, nil)
        if matchingStatus == errSecItemNotFound {
            // 保存
            itemAddStatus = SecItemAdd(dic as CFDictionary, nil)
        } else if matchingStatus == errSecSuccess {
            // 更新
            itemAddStatus = SecItemUpdate(dic as CFDictionary, [kSecValueData as String: _data] as CFDictionary)
        } else {
            print("保存失敗1")
        }
        // 保存・更新ステータス確認
        if itemAddStatus == errSecSuccess {
            print("正常終了")
        } else {
            print("保存失敗")
        }
    }
    
    // 取得
    func getKeyChain(key: String) -> String? {

        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                  kSecAttrGeneric as String: key,
                                  kSecAttrAccount as String: privateKey,
                                  kSecReturnData as String: kCFBooleanTrue]

        var data: AnyObject?
        let matchingStatus = withUnsafeMutablePointer(to: &data){
            SecItemCopyMatching(dic as CFDictionary, UnsafeMutablePointer($0))
        }

        if matchingStatus == errSecSuccess {
            print("取得成功")
            if let getData = data as? Data,
                let getStr = String(data: getData, encoding: .utf8) {
                return getStr
            }
            print("取得失敗: Dataが不正")
            return nil
        } else {
            print("取得失敗")
            return nil
        }
    }
    
    // 削除
    func deleteKeyChain(key: String) {
        // 削除するqueryを設定
        let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
                                  kSecAttrGeneric as String: key,
                                  kSecAttrAccount as String: privateKey]

        if SecItemDelete(dic as CFDictionary) == errSecSuccess {
            print("削除成功")
        } else {
            print("削除失敗")
        }
    }
}

このクラスの使用方法は以下です。

(なるべく、UserDefaultsに近い書き方ができるようにしています。)

// 値の取得
print(KeyChain.shared.getKeyChain(key: "key"))

// 値の設定
KeyChain.shared.setKeyChain("値", key: "key")

// 値の削除(以下のどちらでも可)
KeyChain.shared.deleteKeyChain(key: "kkkk")
KeyChain.shared.setKeyChain(nil, key: "key")