import SwiftUI import CloudKit
// MARK: – Habit Model struct Habit: Identifiable { var id: CKRecord.ID? var title: String var streak: Int var isCompleted: Bool init(record: CKRecord) { self.id = record.recordID self.title = record["title"] as? String ?? "" self.streak = record["streak"] as? Int ?? 0 self.isCompleted = record["isCompleted"] as? Bool ?? false } func toCKRecord() -> CKRecord { let record = CKRecord(recordType: "Habit") record["title"] = title as CKRecordValue record["streak"] = streak as CKRecordValue record["isCompleted"] = isCompleted as CKRecordValue return record } }
// MARK: – CloudKit Manager class CloudKitManager: ObservableObject { private var database = CKContainer.default().publicCloudDatabase @Published var habits: [Habit] = [] func fetchHabits() { let query = CKQuery(recordType: "Habit", predicate: NSPredicate(value: true)) database.perform(query, inZoneWith: nil) { [weak self] records, error in if let error = error { print("Error fetching habits: \(error.localizedDescription)") return } DispatchQueue.main.async { self?.habits = records?.compactMap { Habit(record: $0) } ?? [] } } } func addHabit(title: String) { let habit = Habit(id: nil, title: title, streak: 0, isCompleted: false) let record = habit.toCKRecord() database.save(record) { [weak self] _, error in if let error = error { print("Error saving habit: \(error.localizedDescription)") } else { self?.fetchHabits() } } } func updateHabit(_ habit: Habit) { guard let recordID = habit.id else { return } database.fetch(withRecordID: recordID) { [weak self] record, error in if let record = record, error == nil { record["streak"] = habit.streak as CKRecordValue record["isCompleted"] = habit.isCompleted as CKRecordValue self?.database.save(record) { _, error in if let error = error { print("Error updating habit: \(error.localizedDescription)") } else { self?.fetchHabits() } } } } } }
// MARK: – Habit Row View struct HabitRowView: View { var habit: Habit @ObservedObject var manager: CloudKitManager var body: some View { HStack { Text(habit.title) Spacer() Text("Streak: \(habit.streak)") Button(action: { var updatedHabit = habit if !updatedHabit.isCompleted { updatedHabit.isCompleted = true updatedHabit.streak += 1 } manager.updateHabit(updatedHabit) }) { Image(systemName: habit.isCompleted ? "checkmark.circle.fill" : "circle") } } } }
// MARK: – Habit List View struct HabitListView: View { @ObservedObject var manager = CloudKitManager() @State private var newHabitTitle = "" var body: some View { NavigationView { VStack { HStack { TextField("New Habit", text: $newHabitTitle) .textFieldStyle(RoundedBorderTextFieldStyle()) Button(action: { if !newHabitTitle.isEmpty { manager.addHabit(title: newHabitTitle) newHabitTitle = "" } }) { Text("Add") } }.padding() List(manager.habits) { habit in HabitRowView(habit: habit, manager: manager) } } .navigationTitle("Micro-Habits") .onAppear { manager.fetchHabits() } } } }
// MARK: – App Entry Point @main struct HabitAppApp: App { var body: some Scene { WindowGroup { HabitListView() } } }