Lesson 3 of 4

Data Management in iOS

Master data persistence in iOS apps using UserDefaults, Core Data, and modern data modeling patterns with SwiftUI.

32 minutes

Data Management in iOS

Effective data management is crucial for building robust iOS applications that persist user data and maintain state.

Data Persistence Options

iOS provides several ways to store data:

  • UserDefaults: Simple key-value storage for preferences
  • Core Data: Apple's object graph and persistence framework
  • File System: Direct file reading and writing
  • Keychain: Secure storage for sensitive data
  • CloudKit: Sync data across user devices

UserDefaults

Perfect for simple preferences and settings:

let defaults = UserDefaults.standard
defaults.set("Sarah", forKey: "userName")
let name = defaults.string(forKey: "userName")

Best for:

  • User preferences and settings
  • Small amounts of data
  • Simple key-value pairs

Core Data

Apple's powerful framework for complex data models:

  • Object-oriented database
  • Relationships between entities
  • Query and filter capabilities
  • Automatic change tracking
  • iCloud synchronization support

Data Models with SwiftUI

SwiftUI works seamlessly with data models:

  • Use @State for simple local data
  • Use ObservableObject for complex models
  • Leverage @Published for automatic UI updates
  • Use @FetchRequest with Core Data

Codable Protocol

Swift's Codable makes JSON serialization easy:

struct User: Codable {
    let name: String
    let email: String
}
  • Automatically encode/decode JSON
  • Works with UserDefaults and file storage
  • Type-safe data transformation

Best Practices

  • Choose the right storage solution for your needs
  • Keep data models simple and focused
  • Use Codable for JSON serialization
  • Consider data privacy and security
  • Implement proper error handling
  • Test data persistence thoroughly

Code Example

import SwiftUI

// Codable Data Model
struct Task: Identifiable, Codable {
    let id: UUID
    var title: String
    var isCompleted: Bool
    var createdAt: Date
    
    init(title: String) {
        self.id = UUID()
        self.title = title
        self.isCompleted = false
        self.createdAt = Date()
    }
}

// Observable ViewModel
class TaskManager: ObservableObject {
    @Published var tasks: [Task] = [] {
        didSet {
            saveTasks()
        }
    }
    
    private let tasksKey = "savedTasks"
    
    init() {
        loadTasks()
    }
    
    func addTask(_ title: String) {
        let newTask = Task(title: title)
        tasks.append(newTask)
    }
    
    func toggleTask(_ task: Task) {
        if let index = tasks.firstIndex(where: { $0.id == task.id }) {
            tasks[index].isCompleted.toggle()
        }
    }
    
    private func saveTasks() {
        if let encoded = try? JSONEncoder().encode(tasks) {
            UserDefaults.standard.set(encoded, forKey: tasksKey)
        }
    }
    
    private func loadTasks() {
        if let data = UserDefaults.standard.data(forKey: tasksKey),
           let decoded = try? JSONDecoder().decode([Task].self, from: data) {
            tasks = decoded
        }
    }
}

// SwiftUI View
struct TaskListView: View {
    @StateObject private var taskManager = TaskManager()
    @State private var newTaskTitle = ""
    
    var body: some View {
        NavigationView {
            VStack {
                HStack {
                    TextField("New task", text: $newTaskTitle)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    
                    Button("Add") {
                        taskManager.addTask(newTaskTitle)
                        newTaskTitle = ""
                    }
                    .disabled(newTaskTitle.isEmpty)
                }
                .padding()
                
                List(taskManager.tasks) { task in
                    HStack {
                        Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                            .foregroundColor(task.isCompleted ? .green : .gray)
                        Text(task.title)
                            .strikethrough(task.isCompleted)
                    }
                    .onTapGesture {
                        taskManager.toggleTask(task)
                    }
                }
            }
            .navigationTitle("My Tasks")
        }
    }
}