Siri connaît enfin l'app, et la montre sait noter un verre
Demandé par Sylvain : des widgets, puis « comment lier Siri à mon appli ». Constat avant de commencer : App Intents déclarés : 1 (LogDrinkIntent) AppShortcutsProvider : 0 -> Siri n'avait AUCUNE phrase à écouter Saisie depuis la montre : 0 -> complications d'affichage seulement Et la cause commune : `LogDrinkIntent` vivait dans `CoachQuickWidget.swift`, qui n'appartient qu'à l'extension widget iPhone. Ni l'app (d'où Siri découvre les raccourcis), ni la cible Watch ne pouvaient le voir. Ce qui est ajouté ------------------ - `CoachIntents.swift` — les intents, partagés entre les TROIS cibles. `LogDrinkIntent` y est déplacé (pas dupliqué : deux définitions du même symbole dans une cible qui compile les deux fichiers casseraient le build), plus `HydrationStatusIntent` et `TodaySessionIntent`. - `CoachAppShortcuts.swift` — 5 raccourcis, 16 phrases françaises. Cible App UNIQUEMENT : Apple attend un seul AppShortcutsProvider par application. - `CoachWatchHydration.swift` — widget Smart Stack de la montre : total du jour, jauge, et deux boutons +cl. Le geste se fait au poignet. Choix qui méritent d'être dits ------------------------------- ⛔ Pas de saisie des repas sur la montre. Pas de caméra, pas de scanner, pas de saisie de texte confortable : le bouton ne ferait que renvoyer vers l'iPhone. Tranché avec Sylvain. ⛔ Pas de volume libre dans les phrases Siri. Un intent paramétré oblige Siri à poser une question — plus long que de sortir le téléphone, et le raccourci perd sa raison d'être. Volumes fixés : 250 ml, 500 ml, café 60 ml. ⛔ Aucune borne en bpm dans les réponses vocales. `TodaySession` n'en porte pas (vérifié : sport, title, subtitle, done) et il ne faut pas en ajouter — une fourchette cardiaque annoncée par haut-parleur, hors de la recommandation du jour et de ses réserves, reviendrait à prescrire une intensité à un post-infarctus. Siri annonce QUOI ; l'app dit COMMENT. Deux boutons et pas trois sur la montre : sur 160 pt, un troisième descend les cibles sous le seuil tactile, pour un geste qu'on fait en marchant. ⚠️ RIEN DE CE CODE N'EST COMPILÉ --------------------------------- AppIntents, WidgetKit et SwiftUI sont absents de Swift pour Linux. Ce qui a été vérifié à la place, par lecture croisée des sources : que chaque symbole appelé existe, et qu'aucun n'est déclaré deux fois. Deux erreurs ont d'ailleurs été trouvées ainsi avant commit — `asOf()` ne rend pas un optionnel, et `TodaySession` n'a pas de `durationMin`. Les erreurs de type, de concurrence et d'API n'apparaîtront qu'au build. `docs/siri-et-widget-watch-runbook-mac.md` porte les étapes Xcode, le tableau de Target Membership, les cinq vérifications à faire sur l'appareil, et les trois questions que seul le build tranchera — dont celle-ci : une complication de cadran accepte-t-elle un bouton interactif, ou est-ce réservé à la Smart Stack ? Par prudence, le cadran n'est ici qu'un affichage. 91 tests Swift Linux au vert (non-régression CoachQuickLog / snapshot). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,41 +17,14 @@ import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
// MARK: - Intents
|
||||
|
||||
/// Ajoute une boisson sans quitter l'écran d'accueil.
|
||||
@available(iOS 17.0, watchOS 10.0, *)
|
||||
struct LogDrinkIntent: AppIntent {
|
||||
static var title: LocalizedStringResource = "Noter une boisson"
|
||||
/// `false` : tout l'intérêt est de NE PAS ouvrir l'app.
|
||||
static var openAppWhenRun: Bool = false
|
||||
|
||||
@Parameter(title: "Type") var ref: String
|
||||
@Parameter(title: "Volume (ml)") var amountMl: Double
|
||||
|
||||
init() {}
|
||||
init(ref: String, amountMl: Double) {
|
||||
self.ref = ref
|
||||
self.amountMl = amountMl
|
||||
}
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
let ok = CoachQuickLog.append(
|
||||
CoachQuickEntry(kind: .drink, ref: ref, amount: amountMl)
|
||||
)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
// Un échec d'App Group doit remonter : afficher « ajouté » sur une
|
||||
// saisie perdue est pire que ne rien afficher.
|
||||
guard ok else { throw CoachQuickError.unavailable }
|
||||
return .result()
|
||||
}
|
||||
}
|
||||
|
||||
enum CoachQuickError: Error, CustomLocalizedStringResourceConvertible {
|
||||
case unavailable
|
||||
var localizedStringResource: LocalizedStringResource {
|
||||
"Stockage partagé indisponible — ouvrez l'app une fois."
|
||||
}
|
||||
}
|
||||
//
|
||||
// ⚠️ `LogDrinkIntent` et `CoachQuickError` ont DÉMÉNAGÉ dans
|
||||
// `ios/App/App/CoachIntents.swift` le 25/09/2026, pour être partagés entre
|
||||
// trois cibles : ce widget, l'app (d'où Siri les découvre) et le widget Watch.
|
||||
// Les laisser ici les rendait invisibles aux deux autres — Siri n'avait aucun
|
||||
// intent à écouter, et la montre ne savait pas noter un verre.
|
||||
// ⛔ Ne pas les redéclarer ici : deux définitions du même symbole dans une
|
||||
// cible qui compile les deux fichiers, et le build casse.
|
||||
|
||||
// MARK: - Timeline
|
||||
|
||||
|
||||
Reference in New Issue
Block a user