# Data Persistence Cheat Sheet

iOS ilovalarda ma'lumotlarni lokal saqlash: `UserDefaults`, `Keychain`, `FileManager`, `SwiftData` va `Core Data` bo'yicha amaliy ma'lumotnoma.

---

## 1. Texnologiyalar Taqqoslash Jadvali

| Texnologiya | Qachon ishlatiladi? | Xavfsizlik darajasi | Ma'lumot hajmi |
|---|---|---|---|
| **UserDefaults** | Kichik sozlamalar, flaglar (`isOnboardingShown`) | Past (shifrlanmagan `.plist`) | < 1-2 MB |
| **Keychain** | Parollar, API/JWT tokenlar, biometriya sirlari | Eng yuqori (Hardware Secure Enclave) | Kichik matnlar |
| **FileManager** | Fayllar, PDF, rasmlar, offline audio/video | Standart fayl tizimi | Cheklanmagan (Disk hajmicha) |
| **SwiftData** | Yangi loyihalar (iOS 17+), relyatsion ma'lumotlar | Standart SQLite shifri | Katta hajmlar |
| **Core Data** | Katta korporativ loyihalar, iOS 16 va undan oldingi | Standart SQLite shifri | Katta hajmlar |

---

## 2. UserDefaults va `@AppStorage`

```swift
// Swift Standard
UserDefaults.standard.set(true, forKey: "is_logged_in")
UserDefaults.standard.set("dark", forKey: "app_theme")

let isLoggedIn = UserDefaults.standard.bool(forKey: "is_logged_in")
let theme = UserDefaults.standard.string(forKey: "app_theme") ?? "light"

// SwiftUI @AppStorage
struct SettingsView: View {
    @AppStorage("hasSeenOnboarding") private var hasSeenOnboarding = false

    var body: some View {
        Toggle("Onboarding", isOn: $hasSeenOnboarding)
    }
}
```

---

## 3. Keychain (Maxfiy Ma'lumotlar)

Keychain ma'lumotlari ilova o'chirilganda ham o'chib ketmaydi va zaxira nusxalarda shifrlangan bo'ladi.

```swift
import Security

enum KeychainService {
    static func save(key: String, data: Data) -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: data,
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
        ]
        SecItemDelete(query as CFDictionary) // Eskisini tozalash
        return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
    }

    static func load(key: String) -> Data? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        var item: CFTypeRef?
        guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess else { return nil }
        return item as? Data
    }

    static func delete(key: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key
        ]
        SecItemDelete(query as CFDictionary)
    }
}
```

---

## 4. FileManager (Fayl Tizimi)

```swift
final class LocalFileManager {
    static let shared = LocalFileManager()

    // Hujjatlar papkasi (iCloud backup'ga kiradi)
    private var documentsDirectory: URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }

    // Kesh papkasi (Tizim joy yetishmaganda avtomatik tozalashi mumkin)
    private var cachesDirectory: URL {
        FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
    }

    func saveImage(image: UIImage, fileName: String) throws {
        guard let data = image.jpegData(compressionQuality: 0.8) else { return }
        let url = documentsDirectory.appendingPathComponent(fileName)
        try data.write(to: url, options: .atomic)
    }

    func loadImage(fileName: String) -> UIImage? {
        let url = documentsDirectory.appendingPathComponent(fileName)
        guard FileManager.default.fileExists(atPath: url.path) else { return nil }
        return UIImage(contentsOfFile: url.path)
    }
}
```

---

## 5. SwiftData (iOS 17+)

SwiftData — Apple'ning eng yangi deklarativ ORM tizimi bo'lib, Swift makrolariga asoslangan.

### Model E'lon Qilish:
```swift
import SwiftData

@Model
final class TaskItem {
    var id: UUID
    var title: String
    var isCompleted: Bool
    var createdAt: Date

    init(title: String, isCompleted: Bool = false) {
        self.id = UUID()
        self.title = title
        self.isCompleted = isCompleted
        self.createdAt = Date()
    }
}
```

### SwiftUI'da Ishlatish:
```swift
import SwiftUI
import SwiftData

struct TaskListView: View {
    @Environment(\.modelContext) private var modelContext
    
    // So'rov (Filter va Sort bilan)
    @Query(filter: #Predicate<TaskItem> { !$0.isCompleted }, sort: \.createdAt, order: .reverse)
    private var tasks: [TaskItem]

    var body: some View {
        List {
            ForEach(tasks) { task in
                Text(task.title)
            }
            .onDelete(perform: deleteTasks)
        }
        .toolbar {
            Button("Qo'shish") {
                let newTask = TaskItem(title: "Yangi vazifa")
                modelContext.insert(newTask)
            }
        }
    }

    private func deleteTasks(at offsets: IndexSet) {
        for index in offsets {
            modelContext.delete(tasks[index])
        }
    }
}
```

---

## 6. Core Data (Klassik Stack)

```swift
import CoreData

final class CoreDataManager {
    static let shared = CoreDataManager()

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "AppModel")
        container.loadPersistentStores { _, error in
            if let error = error {
                fatalError("CoreData yuklanmadi: \(error)")
            }
        }
        return container
    }()

    var context: NSManagedObjectContext {
        persistentContainer.viewContext
    }

    // Saqlash
    func save() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("Saqlashda xato:", error)
            }
        }
    }

    // Qidiruv (Fetch Request)
    func fetchTasks() -> [TaskEntity] {
        let request: NSFetchRequest<TaskEntity> = TaskEntity.fetchRequest()
        request.predicate = NSPredicate(format: "isCompleted == NO")
        request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]

        do {
            return try context.fetch(request)
        } catch {
            return []
        }
    }
}
```
