Initiales HabitTracker-Projekt: SwiftUI + SwiftData Gewohnheiten-Tracker
Natives iOS-App-Gerüst (Xcode 26, synchronisierte Ordner, iOS 18+). Features: - Gewohnheiten anlegen (Name, SF-Symbol, Farbe), heute abhaken, Streaks, Löschen - Detailansicht mit Monatskalender (Tage nachtragbar) und Statistiken - Tägliche Erinnerungen via lokale Notifications - Home-Screen-Widget (klein/mittel) mit App-Group-Datenaustausch
This commit is contained in:
commit
22b8f5d806
24 changed files with 1448 additions and 0 deletions
61
Shared/Shared.swift
Normal file
61
Shared/Shared.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import SwiftUI
|
||||
|
||||
// MARK: - Snapshot shared between app and widget
|
||||
|
||||
struct WidgetSnapshot: Codable {
|
||||
struct Item: Codable, Identifiable {
|
||||
var id: UUID
|
||||
var name: String
|
||||
var symbolName: String
|
||||
var colorHex: String
|
||||
var isDoneToday: Bool
|
||||
}
|
||||
|
||||
var generatedAt: Date
|
||||
var items: [Item]
|
||||
|
||||
static let empty = WidgetSnapshot(generatedAt: .now, items: [])
|
||||
}
|
||||
|
||||
// MARK: - App Group backed store
|
||||
|
||||
enum SharedStore {
|
||||
static let appGroupID = "group.de.motocamp.HabitTracker"
|
||||
private static let key = "widgetSnapshot"
|
||||
|
||||
private static var defaults: UserDefaults? {
|
||||
UserDefaults(suiteName: appGroupID)
|
||||
}
|
||||
|
||||
static func save(_ snapshot: WidgetSnapshot) {
|
||||
guard let data = try? JSONEncoder().encode(snapshot) else { return }
|
||||
defaults?.set(data, forKey: key)
|
||||
}
|
||||
|
||||
static func load() -> WidgetSnapshot {
|
||||
guard let data = defaults?.data(forKey: key),
|
||||
let snapshot = try? JSONDecoder().decode(WidgetSnapshot.self, from: data)
|
||||
else { return .empty }
|
||||
return snapshot
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Color from hex (used by both targets)
|
||||
|
||||
extension Color {
|
||||
/// Creates a color from a "#RRGGBB" hex string. Falls back to system green on bad input.
|
||||
init(hex: String) {
|
||||
let cleaned = hex.trimmingCharacters(in: CharacterSet(charactersIn: "#"))
|
||||
var value: UInt64 = 0
|
||||
Scanner(string: cleaned).scanHexInt64(&value)
|
||||
|
||||
if cleaned.count == 6 {
|
||||
let r = Double((value >> 16) & 0xFF) / 255
|
||||
let g = Double((value >> 8) & 0xFF) / 255
|
||||
let b = Double(value & 0xFF) / 255
|
||||
self.init(.sRGB, red: r, green: g, blue: b)
|
||||
} else {
|
||||
self.init(.sRGB, red: 52 / 255, green: 199 / 255, blue: 89 / 255)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue