키체인을 사용 할 일이있어 블러그를 검색해서 약간의 수정해서 Wrapper Class로 작성했다.

//  Created by DongOh Lim on 2023/04/18.
//  참고사이트 : https://developerbee.tistory.com/56

import Foundation
import Security

class KeyChainWrapper: NSObject {
    /*
     * 외부로 제공되는 메소드
     * serviceIdentifier: 키체인에서 해당 앱을 식별하는 값으로 앱만의 고유한 값을 써야합니다. (데이터를 해당 앱에서만 사용하기 위해)
     * userAccount: 앱 내에서 데이터를 식별하기 위한 키에 해당하는 값입니다.
     */
    
    public class func saveData(serviceIdentifier:NSString, userAccount:NSString, data: String) {
        self.save(service: serviceIdentifier, userAccount: userAccount, data: data)
    }
    
    public class func loadData(serviceIdentifier:NSString, userAccount:NSString) -> String? {
        let data = self.load(service: serviceIdentifier, userAccount: userAccount)
        
        return data
    }
    
    public class func updateData(serviceIdentifier:NSString, userAccount:NSString, data: String) {
        self.update(service: serviceIdentifier, userAccount: userAccount, data: data)
    }
    
    public class func dataDelete(serviceIdentifier:NSString, userAccount:NSString) {
        self.delete(service: serviceIdentifier, userAccount: userAccount)
    }
    
    /*
     * Keychain 에 실제 접근하는 내부 메소드
     */
    
    private class func save(service: NSString, userAccount:NSString, data: String) {
        let dataFromString: Data = data.data(using: String.Encoding.utf8)!
        
        // Instantiate a new default keychain query
        let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
                                    kSecAttrService as String: service,
                                    kSecAttrAccount as String: userAccount,
                                    kSecValueData as String: dataFromString]
        
        // Delete any existing items
        SecItemDelete(query as CFDictionary)
        
        // Add the new keychain item
        SecItemAdd(query as CFDictionary, nil)
    }
    
    private class func load(service: NSString, userAccount:NSString) -> String? {
        // Instantiate a new default keychain query
        // Tell the query to return a result
        // Limit our results to one item
        let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
                                    kSecAttrService as String: service,
                                    kSecAttrAccount as String: userAccount,
                                    kSecReturnData as String: kCFBooleanTrue!,
                                    kSecMatchLimit as String: kSecMatchLimitOne as String]
        
        var retrievedData: NSData?
        var dataTypeRef:AnyObject?
        var contentsOfKeychain: String?
        
        let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
        
        if (status == errSecSuccess) {
            retrievedData = dataTypeRef as? NSData
            contentsOfKeychain = String(data: retrievedData! as Data, encoding: String.Encoding.utf8)
        }
        else
        {
            print("Nothing was retrieved from the keychain. Status code \(status)")
            contentsOfKeychain = nil
        }
        
        return contentsOfKeychain
    }
    
    private class func update(service: NSString, userAccount: NSString, data: String) {
        let dataFromString: Data = data.data(using: String.Encoding.utf8)!
        
        let previousQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
                                              kSecAttrService as String: service,
                                              kSecAttrAccount as String: userAccount]
        let updateQuery: [CFString: Any] = [kSecValueData: dataFromString]
        let status = SecItemUpdate(previousQuery as CFDictionary, updateQuery as CFDictionary)
        if status == errSecSuccess {
            print("update complete")
        } else {
            print("not finished update")
        }
    }
    
    private class func delete(service: NSString, userAccount: NSString) {
        let deleteQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
                                            kSecAttrService as String: service,
                                            kSecAttrAccount as String: userAccount]
        let status = SecItemDelete(deleteQuery as CFDictionary)
        if status == errSecSuccess {
            print("remove key-data complete")
        } else {
            print("remove key-data failed")
        }
    }
}

 

사용방법

		// 디바이스 UUID 키체인 체크 및 저장 예제
        let bundleID = appBundleID() as? NSString ?? "번들아이디"
        
        let deviceUUID = KeyChainWrapper.loadData(serviceIdentifier: bundleID, userAccount: "uuid")
        
        if deviceUUID != nil {
            log(direction: .ETC, ofType: self, datas: "UUID : \(String(describing: deviceUUID))")
            
            uuid = deviceUUID!
        } else {
            
            if let device_uuid = UIDevice.deviceUUID {
                log(direction: .ETC, ofType: self, datas: "UUID : \(String(describing: device_uuid))")
                KeyChainWrapper.saveData(serviceIdentifier: bundleID, userAccount: "uuid", data: device_uuid)
            } else {
                log(direction: .ERROR, ofType: self, datas: "UUID NULL")
            }
            
        }

+ Recent posts