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.
88 lines
3.3 KiB
Swift
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])
|
|
}
|
|
}
|