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()
}
}
}



