Last active
May 16, 2025 17:38
-
-
Save davidsteppenbeck/50496d11babc7c1c5d2838a3c6930fcf to your computer and use it in GitHub Desktop.
Shortcuts: Invalid action metadata
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is the problematic intent that consistently fails. | |
struct HabitEntryAppIntent: AppIntent { | |
static let openAppWhenRun: Bool = false | |
static let title = LocalizedStringResource("Add a Habit Entry", table: "AppIntents") | |
static let description = IntentDescription( | |
LocalizedStringResource("Log an entry after completing a task.", table: "AppIntents"), | |
categoryName: LocalizedStringResource("Log Habits", table: "AppIntents"), | |
searchKeywords: [LocalizedStringResource("log", table: "AppIntents")], | |
resultValueName: LocalizedStringResource("Habit", table: "AppIntents") | |
) | |
static var parameterSummary: some ParameterSummary { | |
Summary("Add an entry to \(\.$habitEntity)", table: "AppIntents") | |
} | |
@Parameter( | |
title: LocalizedStringResource("Habit", table: "AppIntents"), | |
requestValueDialog: .init(LocalizedStringResource("Which habit would you like to add an entry to?", table: "AppIntents")) | |
) var habitEntity: HabitEntity | |
@MainActor func perform() async throws -> some ProvidesDialog & ReturnsValue<HabitEntity> { | |
let context = ModelContainer.shared.mainContext | |
let logger = AppIntentHabitEntryLogger() | |
try logger.addEntry(for: habitEntity.id, in: context) | |
let localizedActionName = String(localized: "Logged “\(habitEntity.name)” Entry") | |
context.undoManager?.setActionName(localizedActionName) | |
WidgetManager.shared.reload(.all) | |
return .result( | |
value: habitEntity, | |
dialog: .init(LocalizedStringResource("OK, added an entry to “\(habitEntity.name)”.", table: "AppIntents")) | |
) | |
} | |
} | |
extension HabitEntryAppIntent { | |
init(habit: HabitEntity) { | |
self.habitEntity = habit | |
} | |
} | |
// This is the non-problematic intent that consistently succeeds. | |
struct HabitEntryCounterForTodayAppIntent: AppIntent, AppIntentDialogDateFormatting { | |
static let openAppWhenRun: Bool = false | |
static let title = LocalizedStringResource("Number of Entries Logged in a Habit Today", table: "AppIntents") | |
static let description = IntentDescription( | |
LocalizedStringResource("The total number of entries logged so far today for a habit.", table: "AppIntents"), | |
categoryName: LocalizedStringResource("Habit Stats", table: "AppIntents"), | |
searchKeywords: [LocalizedStringResource("count", table: "AppIntents")], | |
resultValueName: LocalizedStringResource("Habit Statistics", table: "AppIntents") | |
) | |
static var parameterSummary: some ParameterSummary { | |
Summary("Get the total number of \(\.$habitEntity) entries logged so far today", table: "AppIntents") | |
} | |
@Parameter( | |
title: LocalizedStringResource("Habit", table: "AppIntents"), | |
requestValueDialog: .init(LocalizedStringResource("Which habit would you like to count entries in?", table: "AppIntents")) | |
) var habitEntity: HabitEntity | |
@MainActor func perform() async throws -> some ProvidesDialog & ReturnsValue<Int> { | |
let habitID = habitEntity.id | |
let context = ModelContainer.shared.mainContext | |
let date = Date.now | |
let startOfToday = Calendar.shared.startOfDay(for: date) | |
let startOfTomorrow = Calendar.shared.startOfNextDay(for: date) | |
var descriptor = FetchDescriptor<HabitEntry>(predicate: #Predicate { | |
($0.habitID == habitID) && ($0.date >= startOfToday) && ($0.date < startOfTomorrow) && ($0.count > 0) | |
}) | |
descriptor.propertiesToFetch = [\HabitEntry.count] | |
let matches = try context.fetch(descriptor) | |
let totalCount: Int = matches.reduce(.zero) { $0 + $1.count } | |
return .result( | |
value: totalCount, | |
dialog: .init(LocalizedStringResource("There are \(totalCount) entries recorded so far today for “\(habitEntity.name)”.", table: "AppIntents")) | |
) | |
} | |
} | |
extension HabitEntryCounterForTodayAppIntent { | |
init(habit: HabitEntity) { | |
self.habitEntity = habit | |
} | |
} | |
// This is the code that logs the habit in the problematic intent. | |
@MainActor struct AppIntentHabitEntryLogger { | |
func addEntry(for habitID: UUID, in context: ModelContext) throws { | |
let currentDate = Date.now | |
let startOfToday = Calendar.shared.startOfDay(for: currentDate) | |
let startOfTomorrow = Calendar.shared.startOfNextDay(for: currentDate) | |
var descriptor = FetchDescriptor<HabitEntry>(predicate: #Predicate { | |
($0.habitID == habitID) && ($0.date >= startOfToday) && ($0.date < startOfTomorrow) | |
}, sortBy: [ | |
SortDescriptor<HabitEntry>(\.date, order: .forward) // Oldest to newest. | |
]) | |
descriptor.propertiesToFetch = [\HabitEntry.count] | |
let matches = try context.fetch(descriptor) | |
// The last match is the most recent one for the current day. | |
if let match = matches.last { | |
match.increaseCount() | |
} else { | |
let habit = try Habit.match(for: habitID, in: context) | |
let newEntry = HabitEntry(habit: habit) | |
context.insert(newEntry) | |
} | |
if context.hasChanges { | |
try context.save() | |
} | |
} | |
} | |
struct HabitShortcuts: AppShortcutsProvider { | |
static var appShortcuts: [AppShortcut] { | |
AppShortcut( | |
intent: HabitEntryAppIntent(), | |
phrases: [ | |
"Add an entry to \(\.$habitEntity) with \(.applicationName)", | |
"Add an entry to \(\.$habitEntity) in \(.applicationName)", | |
"Add entry \(\.$habitEntity) with \(.applicationName)", | |
"Add entry \(\.$habitEntity) in \(.applicationName)", | |
"Add an entry with \(.applicationName)", | |
"Add an entry in \(.applicationName)", | |
"Add an entry to \(.applicationName)", | |
"Log an entry with \(.applicationName)" | |
], | |
shortTitle: LocalizedStringResource("Add an Entry", table: "AppIntents"), | |
systemImageName: "checkmark.circle.fill", | |
parameterPresentation: ParameterPresentation( | |
for: \.$habitEntity, | |
summary: Summary("Add \(\.$habitEntity) entries", table: "AppIntents"), | |
optionsCollections: { | |
OptionsCollection(HabitEntityQuery(), title: LocalizedStringResource("Log Completed Habits", table: "AppIntents"), systemImageName: "checkmark") | |
} | |
) | |
) | |
AppShortcut( | |
intent: HabitEntryCounterForTodayAppIntent(), | |
phrases: [ | |
"Count \(\.$habitEntity) entries logged today with \(.applicationName)", | |
"Count \(\.$habitEntity) entries logged today in \(.applicationName)", | |
"How many \(\.$habitEntity) entries have been logged today in \(.applicationName)?", | |
"Count habit entries logged today with \(.applicationName)", | |
"Count habit entries logged today in \(.applicationName)", | |
"Count entries logged today for a habit with \(.applicationName)", | |
"Count entries logged today for a habit in \(.applicationName)", | |
"Count \(.applicationName) entries logged today" | |
], | |
shortTitle: LocalizedStringResource("Count Entries Logged Today", table: "AppIntents"), | |
systemImageName: "number.circle.fill", | |
parameterPresentation: ParameterPresentation( | |
for: \.$habitEntity, | |
summary: Summary("The total number of \(\.$habitEntity) entries logged so far today", table: "AppIntents"), | |
optionsCollections: { | |
OptionsCollection(HabitEntityQuery(), title: LocalizedStringResource("Count Entries Logged Today in a Habit", table: "AppIntents"), systemImageName: "number") | |
} | |
) | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment