banyaro-ios/BanYaroGoWidget/BanYaroHomeWidget.swift
rene d807db57a2 1.1: Home-Screen-Widget + Siri-Kurzbefehl „Gassi gehen"
App Group group.app.banyaro.ios verbindet App und Widget-Extension
(Entitlements in beiden Targets, CODE_SIGN_ENTITLEMENTS fürs Widget).

Home-Screen-Widget (D):
- BanYaroHomeWidget (klein + mittel): Tagesfoto, Hundename, nächster Termin.
- App schreibt beim Heim-Laden einen Snapshot (HomeWidgetData) in die App
  Group und triggert WidgetCenter-Reload; Snapshot wird bei Logout/401 geleert.

Siri-/Kurzbefehl (E):
- StartWalkIntent „Gassi gehen" + AppShortcutsProvider (öffnet die App).
- WalkLauncher überbrückt Intent → UI: Flag in der App Group, beim Aktivwerden
  eingelöst → Aufnehmen-Tab + Aufnahme-Start (TrackingView.startFresh).
- MainTabView mit Tab-Auswahl (Tags), BanYaroGoApp liest scenePhase.
2026-06-02 20:01:16 +02:00

88 lines
3.3 KiB
Swift

import WidgetKit
import SwiftUI
import UIKit
struct BanYaroEntry: TimelineEntry {
let date: Date
let data: HomeWidgetData?
}
struct BanYaroProvider: TimelineProvider {
func placeholder(in context: Context) -> BanYaroEntry {
BanYaroEntry(date: Date(), data: nil)
}
func getSnapshot(in context: Context, completion: @escaping (BanYaroEntry) -> Void) {
completion(BanYaroEntry(date: Date(), data: HomeWidgetStore.load()))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<BanYaroEntry>) -> Void) {
let entry = BanYaroEntry(date: Date(), data: HomeWidgetStore.load())
// Die App pusht bei Updates reloadAllTimelines(); als Sicherheitsnetz
// stündlich neu laden.
let next = Calendar.current.date(byAdding: .hour, value: 1, to: Date())
?? Date().addingTimeInterval(3600)
completion(Timeline(entries: [entry], policy: .after(next)))
}
}
struct BanYaroHomeWidgetEntryView: View {
@Environment(\.widgetFamily) private var family
let entry: BanYaroEntry
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Spacer()
Text(entry.data?.dogName ?? "Ban Yaro")
.font(family == .systemSmall ? .headline : .title3)
.bold()
.foregroundStyle(.white)
.shadow(radius: 3)
if family != .systemSmall {
if let appt = entry.data?.nextAppointment, !appt.isEmpty {
Label(appt, systemImage: "calendar")
.font(.caption)
.foregroundStyle(.white)
.shadow(radius: 3)
} else if let n = entry.data?.diaryCount, n > 0 {
Label("\(n) Tagebuch-Einträge", systemImage: "book")
.font(.caption)
.foregroundStyle(.white.opacity(0.9))
.shadow(radius: 3)
} else {
Text("Schön, dass du da bist 🐾")
.font(.caption)
.foregroundStyle(.white.opacity(0.9))
.shadow(radius: 3)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct BanYaroHomeWidget: Widget {
let kind = "BanYaroHomeWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: BanYaroProvider()) { entry in
BanYaroHomeWidgetEntryView(entry: entry)
.containerBackground(for: .widget) {
if let jpeg = entry.data?.photoJPEG, let ui = UIImage(data: jpeg) {
ZStack {
Image(uiImage: ui).resizable().scaledToFill()
LinearGradient(
colors: [.black.opacity(0.0), .black.opacity(0.55)],
startPoint: .center, endPoint: .bottom
)
}
} else {
Color.accentColor.opacity(0.25)
}
}
}
.configurationDisplayName("Ban Yaro")
.description("Tagesfoto deines Hundes und nächster Termin.")
.supportedFamilies([.systemSmall, .systemMedium])
}
}