Files
KisaniCal/Shared/WidgetDataStore.swift
kutesir 5e232f2d81 feat: add widget extension — small, medium, large
- KisaniCalWidget: 3 sizes sharing the app's design language
  Small: date badge + kisaniCAL. wordmark + task count
  Medium: badge + task list (3 items) + workout row
  Large: badge + full task list (6 items) + workout row

- WidgetDataStore (Shared/): lightweight Codable types for App Group
  UserDefaults cross-process data exchange

- WidgetBridge: TaskViewModel/WorkoutViewModel extensions that sync
  data into App Group and call WidgetCenter.reloadAllTimelines()

- ContentView: calls syncWidget() on appear and scene becoming active

- project.yml + entitlements updated; project regenerated via xcodegen
2026-05-28 19:48:01 +03:00

62 lines
1.9 KiB
Swift

import Foundation
// Shared between app and widget extension via App Group UserDefaults.
// Types are pure Codable no UIKit / AppColors dependency.
struct WidgetDataStore {
static let suiteName = "group.com.kutesir.KisaniCal"
static let tasksKey = "kisani.widget.tasks"
static let workoutKey = "kisani.widget.workout"
// MARK: - Shared types
struct WidgetTask: Codable, Identifiable {
let id: UUID
let title: String
let dueDate: Date?
let isToday: Bool
let isOverdue: Bool
}
struct WidgetWorkout: Codable {
let name: String
let sectionsCount: Int
let hasWorkoutToday: Bool
}
// MARK: - Write (app target)
static func saveTasks(_ tasks: [WidgetTask]) {
guard let data = try? JSONEncoder().encode(tasks),
let ud = UserDefaults(suiteName: suiteName) else { return }
ud.set(data, forKey: tasksKey)
}
static func saveWorkout(_ workout: WidgetWorkout?) {
guard let ud = UserDefaults(suiteName: suiteName) else { return }
if let workout, let data = try? JSONEncoder().encode(workout) {
ud.set(data, forKey: workoutKey)
} else {
ud.removeObject(forKey: workoutKey)
}
}
// MARK: - Read (widget target)
static func loadTasks() -> [WidgetTask] {
guard let ud = UserDefaults(suiteName: suiteName),
let data = ud.data(forKey: tasksKey),
let list = try? JSONDecoder().decode([WidgetTask].self, from: data)
else { return [] }
return list
}
static func loadWorkout() -> WidgetWorkout? {
guard let ud = UserDefaults(suiteName: suiteName),
let data = ud.data(forKey: workoutKey),
let workout = try? JSONDecoder().decode(WidgetWorkout.self, from: data)
else { return nil }
return workout
}
}