iOS’te UserDefaults ve KeyChain Veri Kalıcılığı

dilarany
LCW Digital
Published in
3 min readNov 19, 2023
Password GIF By BrainPOP

Günümüz mobil uygulamalarında kullanıcı tercihlerinin ve hassas verilerin saklanması, geliştiriciler için önemli bir konudur. iOS geliştirme ekosisteminde, bu ihtiyaçları karşılamak için UserDefaults ve KeyChain gibi iki farklı araç bulunmaktadır.

Bu yazımda, her iki yöntemin nasıl kullanılacağını, hangi senaryolarda tercih edilmesi gerektiğini ele alacağım. Hadi başlayalım!

UserDefaults: Uygulama Tercihlerini Saklama

UserDefaults; küçük verileri kalıcı olarak saklamak için kullanılan bir arayüzdür. Bu veriler, genellikle kullanıcı tercihleri, uygulama ayarları gibi kullanıcının uygulama içindeki davranışlarına bağlı bilgileri içerir.

UserDefaults, key-value şeklinde çalışır. Her value bir key ile ilişkilendirilir ve bu key kullanılarak value saklanır, okunur veya silinir.

Ancak, büyük veriler veya hassas bilgiler için önerilmez. Uygulama her başlatıldığında UserDefaults’a kaydedilen veriler yüklendiğinden dolayı uygulamanın açılışını yavaşlatmamak için içerisine çok fazla veri saklanmasından kaçınılmalıdır.

Basit veri tipleri olan Bool, Float, Double, Int, String veya URL gibi temel türleri kolayca kaydedebilir ve okuyabilirsiniz. Hatta Array, Dictionary, Date ve Data gibi daha karmaşık değerleri de kaydedip okumak mümkün.

Örneğin, kullanıcının tercih ettiği dili kaydetmek için:

UserDefaults.standard.set(“en-US”, forKey: “preferredLanguage”) // String

ve değeri okumak için:

let preferredLanguage = UserDefaults.standard.string(forKey: “preferredLanguage”)

Diğer veri türleriyle ilgili örnekler:

// Int
UserDefaults.standard.set(25, forKey: "age")
let age = UserDefaults.standard.integer(forKey: "age")

// Bool
UserDefaults.standard.set(true, forKey: "isTutorialSeen")
let isTutorialSeen = UserDefaults.standard.bool(forKey: "isTutorialSeen")

// CGFloat
UserDefaults.standard.set(CGFloat.pi, forKey: "pi")
let piValue = UserDefaults.standard.float(forKey: "pi")

// Float
UserDefaults.standard.set(0.8, forKey: "volume")
let volume = UserDefaults.standard.float(forKey: "volume")

// Date
UserDefaults.standard.set(Date(), forKey: "appLastOpenedTime")
UserDefaults.standard.object(forKey: "appLastOpenedTime")

// Array
let array = ["Hello", "World", "!"]
UserDefaults.standard.set(array, forKey: "savedArray")
let savedArray = UserDefaults.standard.array(forKey: "savedArray") as? [String]

// Dictionary
let dict = ["name": "Dilara", "country": "TR", "bloodType": "A"]
UserDefaults.standard.set(dict, forKey: "savedDictionary")
let savedDictionary = UserDefaults.standard.dictionary(forKey: "savedDictionary") as? [String: String]

iOS 14 ve üzeri için SwiftUI, @AppStorage adında bir property wrapper sunmaktadır. Bu, UserDefaults ile doğrudan bağlantı kurarak veri saklamayı daha da basitleştirir. Bir @AppStorage ile tanımlanmış değişkenin değeri değiştirildiğinde, bu değişiklik otomatik olarak UserDefaults’a kaydedilir.

@AppStorage(“preferredLanguage”) var preferredLanguage: String = “en-US”

KeyChain: Hassas Verilerin Güvenli Saklanması

Keychain; kullanıcı şifreleri, sertifikalar ve diğer güvenlikle ilgili verilerin saklanması için tasarlanmıştır. Bu veriler, cihazın kilidinin açılmasıyla erişilebilir hale gelir ve uygulamalar arasında paylaşılabilir.

Keychain wrapper’ının basit bir örneği:

import Security

class KeyChainManager {
static let shared = KeyChainManager()

private init() {}

@discardableResult
func set(password: String, account: String) -> Bool {
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: passwordData
]

// Aynı hesap için kayıtlı bir şifre varsa silinir.
SecItemDelete(query as CFDictionary)

// Yeni şifre eklenir.
let status = SecItemAdd(query as CFDictionary, nil)

// Statü koduna göre başarı durumu döndürülür.
return status == errSecSuccess
}

func getPassword(forAccount account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: kCFBooleanTrue!,
kSecMatchLimit as String: kSecMatchLimitOne
]

var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)

// Statü başarılıysa, şifre çözülüp döndürülür.
if status == errSecSuccess, let passwordData = result as? Data {
return String(data: passwordData, encoding: .utf8)
} else {
// Hata durumunda nil döndürülür.
return nil
}
}
}
// Bir şifreyi KeyChain'e kaydetmek için:
KeyChainManager.shared.set(password: "sher-locked", account: "someUserName")

// Bir şifreyi KeyChain'den okumak için:
if let retrievedPassword = KeyChainManager.shared.getPassword(forAccount: "someUserName") {
print("Şifre başarıyla alındı: \(retrievedPassword)")
} else {
print("Şifre alınamadı.")
}

UserDefaults, uygulama içi kullanıcı tercihleri, ayarlar ve durum bilgisi gibi hassas olmayan veriler için idealdir. Bu API, basit kullanımı ve hızlı erişimi ile uygulama geliştiricilere büyük kolaylık sağlar. Ancak güvenlik konusunda sınırlamaları olduğundan, kullanıcı kimlik bilgileri veya özel veriler için uygun bir seçim değildir. Veriler şifrelenmediğinden dolayı jailbreak yapılmış cihazlarda veya cihazın yedeklenmesi durumunda veriler kolayca okunabilir.

KeyChain, kullanımı daha karmaşık olabilir, sağladığı güvenlik ve veri koruma özellikleri ile hassas verileri saklama konusunda güçlü bir çözüm sunmasıyla bu karmaşıklığa değer kılar. Şifreler, özel anahtarlar ve kullanıcı kimlik bilgileri gibi veriler için en güvenli seçenektir. Veriler uygulama silinse bile korunur, bu da kullanıcı verilerinin güvenliğini artırır.

Bir sonraki yazıda görüşmek üzere :)

--

--