Hi! I'm working on a widget using the new app intent & widgetkit features. Until now everything works, but I can't figure out how to give for example a line of text or a dummy data when the intent data is empty.
For example in the Widgetsmith app, you can put pictures on your homescreen using widgets. But from the moment that you delete the picture in the app, the widget changes to a text screen where they have placed information about changing the widget to another option.
Can you help me?
My code so far:
//
// TimersIntentWidget.swift
//
// Created by Mees Akveld on 25/09/2023.
//
import Foundation
import AppIntents
import WidgetKit
import SwiftUI
@available(iOS 17, *)
struct PresetIntentWidget: AppIntentTimelineProvider {
func timeline(for configuration: selectPresetIntent, in context: Context) async -> Timeline<PresetEntry> {
let entry = PresetEntry(date: Date(), presetModel: configuration.preset)
let timeline = Timeline(entries: [entry], policy: .never)
return timeline
}
func snapshot(for configuration: selectPresetIntent, in context: Context) async -> PresetEntry {
PresetEntry(date: Date(), presetModel: configuration.preset)
}
func placeholder(in context: Context) -> PresetEntry {
PresetEntry(date: Date(), presetModel: WidgetPreset.allPresets[0])
}
}
@available(iOS 17, *)
struct PresetEntry: TimelineEntry {
let date: Date
let presetModel: WidgetPreset
}
@available(iOS 17, *)
struct PresetWidgetView: View {
let entry: PresetEntry
private func isColorTheme() -> Int {
let defaults = UserDefaults(suiteName: "group.MeesAkveld.ShareDefaults")
let colorTheme = defaults?.integer(forKey: "ColorTheme") ?? 1
if colorTheme == 0 {
return 1
} else { return colorTheme }
}
var body: some View {
GeometryReader { geometry in
ZStack {
Image("bannerT\(isColorTheme())_Small")
.resizable()
.aspectRatio(contentMode: .fill)
.scaleEffect(1.11)
HStack {
Rectangle()
.cornerRadius(20)
.frame(width: geometry.size.width - 20, height: geometry.size.height - 20, alignment: .center)
.foregroundStyle(customBlue2Func())
Spacer()
}
.padding(.leading, 10)
HStack {
VStack(alignment: .leading, spacing: 10) {
Image(systemName: entry.presetModel.icon != "" ? entry.presetModel.icon : "timer")
.font(.system(size: 30))
.multilineTextAlignment(.center)
.frame(width: 18)
.offset(x: 10)
VStack(alignment: .leading){
Text(entry.presetModel.title != "" ? entry.presetModel.title : "")
.bold()
Text(timeToText(hours: entry.presetModel.hours, minutes: entry.presetModel.minutes, seconds: entry.presetModel.seconds))
}
.lineLimit(entry.presetModel.id == "noPresets" ? 2 : 1)
}
.foregroundColor(customBlueFunc())
Spacer()
}
.padding(25)
}
.widgetURL(URL(string:"preset:///\(entry.presetModel.id)"))
}
}
}
@available(iOS 17, *)
struct PresetWidgetConfiguration: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: "presetWidgetIntent",
intent: selectPresetIntent.self,
provider: PresetIntentWidget()
) { entry in
PresetWidgetView(entry: entry)
}
.configurationDisplayName("Presets")
.description("View and activate a preset of your choosing from your homescreen.")
.contentMarginsDisabled()
.supportedFamilies([.systemSmall])
}
}
@available(iOS 17, *)
struct PresetIntentWidget_Previews: PreviewProvider {
static var previews: some View {
PresetWidgetView(entry: PresetEntry(date: Date(), presetModel: WidgetPreset(id: "", title: "Title", icon: "", hours: 1, minutes: 1, seconds: 1, showEditView: false)))
}
}
@available(iOS 17, *)
struct WidgetPreset: AppEntity, Decodable {
var id: String
var title: String
var icon: String
var hours: Int
var minutes: Int
var seconds: Int
var showEditView: Bool
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Preset"
static var defaultQuery = WidgetPresetQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(title)", subtitle: "\(timeToText(hours: hours, minutes: minutes, seconds: seconds))")
}
static var allPresets: [WidgetPreset]{
get {
let defaults = UserDefaults(suiteName: "group.MeesAkveld.ShareDefaults")
let presetsData = defaults!.data(forKey: "presets")
if let data = presetsData,
let loadedPresets = try? JSONDecoder().decode([WidgetPreset].self, from: data) {
return loadedPresets
} else {
return []
}
}
}
}
@available(iOS 17, *)
struct WidgetPresetQuery: EntityQuery {
func entities(for identifiers: [WidgetPreset.ID]) async throws -> [WidgetPreset] {
WidgetPreset.allPresets.filter {
identifiers.contains($0.id)
}
}
func suggestedEntities() async throws -> [WidgetPreset] {
WidgetPreset.allPresets
}
func defaultResult() async -> WidgetPreset? {
WidgetPreset.allPresets.first
}
}
@available(iOS 17, *)
struct selectPresetIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Preset"
static var description: IntentDescription = IntentDescription("Select a preset")
@Parameter(title: "Selected preset:")
var preset: WidgetPreset
}