String νμ μ κ° ν€μ²΄μΈμ μ μ₯ν λ μ¬μ©νλ €κ³ κ΅¬νν΄λμ ν€μ²΄μΈ μ νΈ ν΄λμ€μ λλ€. μ±κΈν€μΌλ‘ ꡬνλμ΄ μμ΅λλ€. store μ μ΄λ―Έ μ‘΄μ¬νλ κ°μ΄λ©΄ μλμΌλ‘ μ λ°μ΄νΈκ° λλλ‘ κ΅¬ννμ΅λλ€. λ°μνλ κ°μ’ μ€λ₯μ λν μ€λͺ μ μ£ΌμμΌλ‘ λ¬μμ΅λλ€.
import Foundation
import Security
enum KeyChainType: String {
case tokenKey = "Token_Key"
// ...
}
enum KeyChainError: Error {
case invalidBundleID
case invalidData
case recognizedKeyChainError(OSStatus, CFString?)
case unrecognizedKeyChainError
case dataCannotConvertString
case dataNotFound
}
class KeyChainUtil {
private let bundleID = Bundle.main.bundleIdentifier
// singleton
static let shared = KeyChainUtil()
private init() { }
// MARK: Create = Store
/**
ν€μ²΄μΈμ λ°μ΄ν° μ μ₯νλ ν¨μ. κΈ°μ‘΄μ μ‘΄μ¬νλ λ°μ΄ν°μ¬λ μ΄ ν¨μλ‘ μ μ₯ν΄μ£Όλ©΄ λ¨.
λ°μν μ μλ μλ¬
1. invalidBundleID -> μ ν¨νμ§ μμ bundle idμ κ²½μ°
2. invalidData -> μ μ₯ λ°μ΄ν°μ μ μ₯ νμ λ³νμ΄ λΆκ°λ₯ν κ²½μ°
3. recognizedKeyChainError -> λ°μ κ°λ₯
- Parameters:
- key: enum KeyChainTypeμ μ‘΄μ¬νλ μ μ₯ν κ°μ νμ
- data: μ μ₯ν κ°
*/
func storeStringTypeInKeyChain(
type key: KeyChainType,
data: String
) throws {
guard let service = self.bundleID else {
throw KeyChainError.invalidBundleID
}
guard let validData = data.data(using: .utf8, allowLossyConversion: false) else {
throw KeyChainError.invalidData
}
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: key.rawValue,
kSecValueData: validData
]
let status: OSStatus = SecItemAdd(query as CFDictionary, nil)
if status == errSecSuccess {
print("key chain stored well")
} else {
if status == errSecDuplicateItem {
do {
try updateStringTypeInKeyChain(type: key, data: data)
} catch KeyChainError.recognizedKeyChainError(let status, let err) {
throw KeyChainError.recognizedKeyChainError(status, err)
}
}
let err = SecCopyErrorMessageString(status, nil)
throw KeyChainError.recognizedKeyChainError(status, err)
}
}
// MARK: Update
/**
ν€μ²΄μΈμ λ°μ΄ν° μ
λ°μ΄νΈνλ ν¨μ. μΈλΆμμ μ κ·Ό λΆκ°λ₯. Store μ μλμΌλ‘ λΆλ¦Ό
λ°μν μ μλ μλ¬
1. invalidBundleID -> λ°μνμ§ μμ (storeμμ ν λ² κ²μ¦)
2. invalidData -> λ°μνμ§ μμ (storeμμ ν λ² κ²μ¦)
3. recognizedKeyChainError -> λ°μ κ°λ₯
*/
fileprivate func updateStringTypeInKeyChain(
type key: KeyChainType,
data: String
) throws {
guard let service = self.bundleID else {
throw KeyChainError.invalidBundleID
}
guard let validData = data.data(using: .utf8, allowLossyConversion: false) else {
throw KeyChainError.invalidData
}
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service
]
let attributes: [CFString: Any] = [
kSecAttrAccount: key.rawValue,
kSecValueData: validData
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
if status == errSecSuccess {
print("key chain updated well")
} else {
let err = SecCopyErrorMessageString(status, nil)
throw KeyChainError.recognizedKeyChainError(status, err)
}
}
// MARK: Read
/**
ν€μ²΄μΈμμ λ°μ΄ν° κ° λΆλ¬μ€λ ν¨μ
λ°μν μ μλ μλ¬
1. invalidBundleID -> μ ν¨νμ§ μμ bundle idμ κ²½μ°
2. dataCannotConvertString -> μ μ₯ λ°μ΄ν°μ String λ³νμ΄ λΆκ°λ₯ν κ²½μ°
3. dataNotFound -> key κ°μΌλ‘ μ κ·Όν λ°μ΄ν°κ° μ‘΄μ¬νμ§ μλ κ²½μ°
4. recognizedKeyChainError -> λ°μ κ°λ₯
- Parameter key: enum KeyChainTypeμ μ‘΄μ¬νλ μ μ₯ν κ°μ νμ
- Returns: KeyChainμ μ μ₯λ κ°
*/
func getStringTypeFromKeyChain(type key: KeyChainType) throws -> String {
guard let service = self.bundleID else {
throw KeyChainError.invalidBundleID
}
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: key.rawValue,
kSecReturnData: true,
kSecReturnAttributes: true,
kSecMatchLimit: kSecMatchLimitOne
]
var dataTypeRef: CFTypeRef?
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == errSecSuccess {
guard let item = dataTypeRef as? [String: Any] else {
throw KeyChainError.dataCannotConvertString
}
guard let deviceData = item[kSecValueData as String] as? Data else {
throw KeyChainError.dataCannotConvertString
}
guard let result = String(data: deviceData, encoding: .utf8) else {
throw KeyChainError.dataCannotConvertString
}
return result
} else if status == errSecItemNotFound || status == -25300 {
throw KeyChainError.dataNotFound
} else {
let err = SecCopyErrorMessageString(status, nil)
throw KeyChainError.recognizedKeyChainError(status, err)
}
}
}
fileprivate extension KeyChainUtil {
}
'CS > iOS, Swift' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[iOS] μλ² ν΅μ Base Service (0) | 2023.04.07 |
---|---|
[iOS] μΉλ·° μΏ ν€ κ΄λ ¨ μ νΈ (0) | 2023.04.07 |
[iOS] ν΄μ± (0) | 2023.04.07 |
[iOS] Framework, Library RnD (0) | 2023.04.07 |
[iOS] static framework to xcframework, cocoapod λ°°ν¬ (0) | 2023.04.07 |