2.1.0: app de Apple Watch, complicacion y widget semanal en iOS

- Target MealMoodWatch (watchOS 10, SwiftUI): vista de hoy y de la
  semana en TabView vertical; recibe el snapshot del iPhone por
  WatchConnectivity (updateApplicationContext) y lo guarda en su app
  group para la complicacion
- Target MealMoodWatchWidget: complicacion accessoryRectangular/
  Circular/Inline con las comidas de hoy (heuristica comida/cena por
  hora); icono del watch generado desde el logo
- WatchWeekPayload compartido entre iOS app, widget iOS, watch app y
  complicacion — llega ya localizado desde el iPhone
- Widget iOS nuevo "Semana" (systemMedium/Large) con los 7 dias;
  el widget de hoy pasa a WidgetBundle
- Targets creados via gema xcodeproj (sin abrir Xcode); embed del watch
  antes del script de Crashlytics para evitar ciclo de build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
This commit is contained in:
alexandrev-tibco
2026-09-08 16:38:58 +02:00
parent a9a9d6bdf0
commit c0b485387d
18 changed files with 892 additions and 7 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
- **Name**: MealMood: Family Meal Planner - **Name**: MealMood: Family Meal Planner
- **Bundle ID**: com.alexandrevazquez.mealmood - **Bundle ID**: com.alexandrevazquez.mealmood
- **Platform**: iOS/iPadOS (also runs as iOS app on Mac) - **Platform**: iOS/iPadOS (also runs as iOS app on Mac)
- **Current version**: 2.0.5 (dev, beta-bound; 2.0.4 build 72 in App Store review) - **Current version**: 2.1.0 (dev; 2.0.5 build 78 in App Store review)
- **Main branch**: `1.0.1` (production), feature branches named after versions - **Main branch**: `1.0.1` (production), feature branches named after versions
### Branch ↔ version convention (IMPORTANT) ### Branch ↔ version convention (IMPORTANT)
@@ -13,7 +13,7 @@ The active dev branch is named after the **current** `MARKETING_VERSION`, not th
# after bumping MARKETING_VERSION in the Xcode project to e.g. 1.1.6 # after bumping MARKETING_VERSION in the Xcode project to e.g. 1.1.6
git branch -m <old-version> 1.1.6 # e.g. git branch -m 1.1.5 1.1.6 git branch -m <old-version> 1.1.6 # e.g. git branch -m 1.1.5 1.1.6
``` ```
Right now: `MARKETING_VERSION = 2.0.5` → active branch is `2.0.5`. Keep them in sync. (The build number bump within the same version does NOT rename the branch — only a version change does.) Right now: `MARKETING_VERSION = 2.1.0` → active branch is `2.1.0`. Keep them in sync. (The build number bump within the same version does NOT rename the branch — only a version change does.)
**`MARKETING_VERSION` in the Xcode project does NOT drive the shipped version.** Both Info.plists hardcode their values, so bumping the project setting alone builds an IPA with the old version and App Store Connect rejects it (`90062: must contain a higher version than that of the previously approved version`). A version bump means editing three places, and `fastlane`'s `increment_build_number` only handles the app's build number: **`MARKETING_VERSION` in the Xcode project does NOT drive the shipped version.** Both Info.plists hardcode their values, so bumping the project setting alone builds an IPA with the old version and App Store Connect rejects it (`90062: must contain a higher version than that of the previously approved version`). A version bump means editing three places, and `fastlane`'s `increment_build_number` only handles the app's build number:
+332 -2
View File
@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
016D78C0DC1B1F6E671F36F7 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 168A6CA95246E1BB72797D4E /* OnboardingViewModelTests.swift */; }; 016D78C0DC1B1F6E671F36F7 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 168A6CA95246E1BB72797D4E /* OnboardingViewModelTests.swift */; };
0A498D2C7D9F798538471810 /* WeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683D03A3F2E26A2BA627B353 /* WeekView.swift */; };
0EC555FE2F4498780005513D /* aaaa.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 0EC555FD2F4498780005513D /* aaaa.storekit */; }; 0EC555FE2F4498780005513D /* aaaa.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 0EC555FD2F4498780005513D /* aaaa.storekit */; };
0F91C8611D3BBE1FBD51D1E6 /* MealSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797A41D292142240FBDF9F2A /* MealSlot.swift */; }; 0F91C8611D3BBE1FBD51D1E6 /* MealSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797A41D292142240FBDF9F2A /* MealSlot.swift */; };
17E1054EE183486E7BE38C19 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6FC2153490D53B80DBDC034 /* WidgetDataStore.swift */; }; 17E1054EE183486E7BE38C19 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6FC2153490D53B80DBDC034 /* WidgetDataStore.swift */; };
@@ -23,6 +24,7 @@
30540EF74F2C97F741BA6A1A /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049AE68D5B577514ADD6BACB /* Tag.swift */; }; 30540EF74F2C97F741BA6A1A /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049AE68D5B577514ADD6BACB /* Tag.swift */; };
31DFC035AE34F49FB35071A7 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C47DD7B8F4B1441780E611 /* HomeView.swift */; }; 31DFC035AE34F49FB35071A7 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C47DD7B8F4B1441780E611 /* HomeView.swift */; };
37F5CEC4862C0AE2F3385ABA /* DishListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB3BD0E078644749E93B0D2B /* DishListView.swift */; }; 37F5CEC4862C0AE2F3385ABA /* DishListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB3BD0E078644749E93B0D2B /* DishListView.swift */; };
3ACF9F301D6AC8A9A0B1AC3E /* MealMoodWatch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 2CF3C9FF75E9F24B32D54D5A /* MealMoodWatch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
3C5225918DA8DE77F5AC1AA0 /* MealMoodApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C20D9FC522FEBF7206CFD32 /* MealMoodApp.swift */; }; 3C5225918DA8DE77F5AC1AA0 /* MealMoodApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C20D9FC522FEBF7206CFD32 /* MealMoodApp.swift */; };
4218F220B7412965A5001A88 /* CalendarStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76719058DD3A4177B2B4163E /* CalendarStepView.swift */; }; 4218F220B7412965A5001A88 /* CalendarStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76719058DD3A4177B2B4163E /* CalendarStepView.swift */; };
443CC3997A9A4818B51066FB /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8AD94FC874467D863D464C /* StatsView.swift */; }; 443CC3997A9A4818B51066FB /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8AD94FC874467D863D464C /* StatsView.swift */; };
@@ -31,10 +33,13 @@
48F9D3308375F820B2B88D1B /* PremiumUpsellBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CFA001E1BFA916ABC4C44C1 /* PremiumUpsellBanner.swift */; }; 48F9D3308375F820B2B88D1B /* PremiumUpsellBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CFA001E1BFA916ABC4C44C1 /* PremiumUpsellBanner.swift */; };
4D53B5101AE845D7C03A0EB4 /* GoogleMobileAds in Frameworks */ = {isa = PBXBuildFile; productRef = 192E7CF40FD34C3FF4DDFDA3 /* GoogleMobileAds */; }; 4D53B5101AE845D7C03A0EB4 /* GoogleMobileAds in Frameworks */ = {isa = PBXBuildFile; productRef = 192E7CF40FD34C3FF4DDFDA3 /* GoogleMobileAds */; };
511DDB82FF4DD30018101418 /* DishDrawerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23957C735075B0DFB1DF51D7 /* DishDrawerView.swift */; }; 511DDB82FF4DD30018101418 /* DishDrawerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23957C735075B0DFB1DF51D7 /* DishDrawerView.swift */; };
519AA84E7D3B9ED5FC215E4B /* MealMoodWatchWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A43C4A6B38F731B9FE6AACB /* MealMoodWatchWidget.swift */; };
57F1EA6B5E3AF88A61C48354 /* ShoppingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA71D570D9882C3AFAFDC48 /* ShoppingListView.swift */; }; 57F1EA6B5E3AF88A61C48354 /* ShoppingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA71D570D9882C3AFAFDC48 /* ShoppingListView.swift */; };
5828DA9ED4D8B892E9EFC1F4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBB9DAFC0FC788CF491CF9C8 /* Assets.xcassets */; };
5BCED5AFADACD4118AE59AF9 /* Font+MealMood.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE73D5B7AF8B5E2D63105977 /* Font+MealMood.swift */; }; 5BCED5AFADACD4118AE59AF9 /* Font+MealMood.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE73D5B7AF8B5E2D63105977 /* Font+MealMood.swift */; };
5E7322F3F3624211985F2D99 /* ICloudSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED090ADA1FB259AD2AFEDE2 /* ICloudSyncService.swift */; }; 5E7322F3F3624211985F2D99 /* ICloudSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED090ADA1FB259AD2AFEDE2 /* ICloudSyncService.swift */; };
5ECA2501EC257777DBE670AD /* EmptySlotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B68E5C82608F60FF8798BB81 /* EmptySlotView.swift */; }; 5ECA2501EC257777DBE670AD /* EmptySlotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B68E5C82608F60FF8798BB81 /* EmptySlotView.swift */; };
626254AEAA3AF22D24222124 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6971B29A22117CC7621DB938 /* Foundation.framework */; };
64CC61B20F0AF602A6CC1114 /* SpeechDictationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69C0BDFEBA8B692530CB6B5E /* SpeechDictationServiceTests.swift */; }; 64CC61B20F0AF602A6CC1114 /* SpeechDictationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69C0BDFEBA8B692530CB6B5E /* SpeechDictationServiceTests.swift */; };
6553473B007AFD04079878C6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F0A5C2FF1B797A4BB69B39 /* ContentView.swift */; }; 6553473B007AFD04079878C6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F0A5C2FF1B797A4BB69B39 /* ContentView.swift */; };
6685C591F837FAFB29A322F7 /* DeduplicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C3CBAFE191AD6643F861B7 /* DeduplicationService.swift */; }; 6685C591F837FAFB29A322F7 /* DeduplicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C3CBAFE191AD6643F861B7 /* DeduplicationService.swift */; };
@@ -51,6 +56,7 @@
861557A13918B99C421251E0 /* PremiumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324D77FEC4C8C371695F27CC /* PremiumView.swift */; }; 861557A13918B99C421251E0 /* PremiumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324D77FEC4C8C371695F27CC /* PremiumView.swift */; };
867A511F154A33E9DD7FE9BC /* CalendarService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A631C6D0CF7A0724ACF6A5 /* CalendarService.swift */; }; 867A511F154A33E9DD7FE9BC /* CalendarService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A631C6D0CF7A0724ACF6A5 /* CalendarService.swift */; };
87EB371FD3B6C241AC721C55 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E15828872FAEA71A7384099 /* Localizable.strings */; }; 87EB371FD3B6C241AC721C55 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E15828872FAEA71A7384099 /* Localizable.strings */; };
8B4672C76B05BC1DFB5C979D /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018BA4B3DC94CA7443CA9D1 /* TodayView.swift */; };
8CEAC8993D33BFBBEB5319A8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE10285FEE9BE0684CC66A3 /* NotificationService.swift */; }; 8CEAC8993D33BFBBEB5319A8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE10285FEE9BE0684CC66A3 /* NotificationService.swift */; };
8DB624B469178CFD8CD87672 /* OnboardingFlowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */; }; 8DB624B469178CFD8CD87672 /* OnboardingFlowUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */; };
8F739A4496501009B8D829B3 /* Color+MealMood.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB771E85924AE086647A5113 /* Color+MealMood.swift */; }; 8F739A4496501009B8D829B3 /* Color+MealMood.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB771E85924AE086647A5113 /* Color+MealMood.swift */; };
@@ -64,6 +70,7 @@
A4D50C0B4E91B3542C63017A /* MealMoodWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = B042BB2F81973D854E84BABD /* MealMoodWidget.swift */; }; A4D50C0B4E91B3542C63017A /* MealMoodWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = B042BB2F81973D854E84BABD /* MealMoodWidget.swift */; };
A6F130EC376B7BC270AD908D /* TagPill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1C12C3A693D7C41E9F80B /* TagPill.swift */; }; A6F130EC376B7BC270AD908D /* TagPill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1C12C3A693D7C41E9F80B /* TagPill.swift */; };
A7CAD588CF78F404C8065CF1 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */; }; A7CAD588CF78F404C8065CF1 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */; };
A805D5DEA5B7EA1374FE3118 /* MealMoodWatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB16B89093E7AE8B9B05202 /* MealMoodWatchApp.swift */; };
A9278F6559777CB9FA7FB69E /* AdMobConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812303C94C8C1D2FADF3CB37 /* AdMobConfig.swift */; }; A9278F6559777CB9FA7FB69E /* AdMobConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812303C94C8C1D2FADF3CB37 /* AdMobConfig.swift */; };
AB443E1DC9830D13B3E7B837 /* SecondaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1948B5DAEEEA09B78C8A9 /* SecondaryButton.swift */; }; AB443E1DC9830D13B3E7B837 /* SecondaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1948B5DAEEEA09B78C8A9 /* SecondaryButton.swift */; };
AB496674DF3CA41ACBBE7CFC /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5187023D83BBC5E8D304CE3A /* SettingsViewModel.swift */; }; AB496674DF3CA41ACBBE7CFC /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5187023D83BBC5E8D304CE3A /* SettingsViewModel.swift */; };
@@ -75,26 +82,33 @@
B8D7CA62789429C2F693E4F6 /* MealMoodLogoMark.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAC64110469AA91D5F28C423 /* MealMoodLogoMark.swift */; }; B8D7CA62789429C2F693E4F6 /* MealMoodLogoMark.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAC64110469AA91D5F28C423 /* MealMoodLogoMark.swift */; };
B8FDF31D3BD7DD7220CE21A3 /* ShoppingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FC93C653B5540D7205BF294 /* ShoppingItem.swift */; }; B8FDF31D3BD7DD7220CE21A3 /* ShoppingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FC93C653B5540D7205BF294 /* ShoppingItem.swift */; };
BE908B23F64F33D594AB8D82 /* DateHelpersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD33453B7DF86EDBAEB80F1 /* DateHelpersTests.swift */; }; BE908B23F64F33D594AB8D82 /* DateHelpersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD33453B7DF86EDBAEB80F1 /* DateHelpersTests.swift */; };
BF89CD5E8CA0DFFC224CF94D /* WatchWeekPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */; };
C14BDE4C3EF6AF75F378D892 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA97AD958C922D8AFF73314 /* PrimaryButton.swift */; }; C14BDE4C3EF6AF75F378D892 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA97AD958C922D8AFF73314 /* PrimaryButton.swift */; };
C3CB4ACC213DAAD25E610C56 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB4FC89C90928DD833240244 /* WidgetKit.framework */; }; C3CB4ACC213DAAD25E610C56 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB4FC89C90928DD833240244 /* WidgetKit.framework */; };
C565A1975302748A2B2152BE /* DishViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8472961AE65E2A3460B67C3 /* DishViewModel.swift */; }; C565A1975302748A2B2152BE /* DishViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8472961AE65E2A3460B67C3 /* DishViewModel.swift */; };
C6DBE6B5B4BE002A234CBCCA /* WatchSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449577ECE261177D4A1C1572 /* WatchSyncService.swift */; };
C7B1F5A21B5E4A9E91B7F5A2 /* BottomPromoBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E2F6A3F47E5C9F92A8E6B3 /* BottomPromoBanner.swift */; }; C7B1F5A21B5E4A9E91B7F5A2 /* BottomPromoBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E2F6A3F47E5C9F92A8E6B3 /* BottomPromoBanner.swift */; };
C81C935386BE1E939DA55F8D /* MealWindowsStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA11C8A81D5C0CA8A3C13D /* MealWindowsStepView.swift */; }; C81C935386BE1E939DA55F8D /* MealWindowsStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA11C8A81D5C0CA8A3C13D /* MealWindowsStepView.swift */; };
CD174669421FE1FD4315F6E0 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB0D740B79B9E129A3C2F382 /* WidgetKit.framework */; }; CD174669421FE1FD4315F6E0 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB0D740B79B9E129A3C2F382 /* WidgetKit.framework */; };
CD2ADCDADFEC2D562E23D8E6 /* AutocompleteEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7AB2ECCA7D330E50AC549E /* AutocompleteEngine.swift */; }; CD2ADCDADFEC2D562E23D8E6 /* AutocompleteEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7AB2ECCA7D330E50AC549E /* AutocompleteEngine.swift */; };
D043E5DEA954AADC016133BF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6971B29A22117CC7621DB938 /* Foundation.framework */; };
D2840F9D92D16026338CE83C /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7C00EED19123F00AE87FF24 /* AppSettings.swift */; }; D2840F9D92D16026338CE83C /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7C00EED19123F00AE87FF24 /* AppSettings.swift */; };
D39BED40CA59849F181D80B5 /* IngredientGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F0ADA350289AE410D572A7 /* IngredientGenerator.swift */; }; D39BED40CA59849F181D80B5 /* IngredientGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F0ADA350289AE410D572A7 /* IngredientGenerator.swift */; };
D8149D55E74F7BBFC0402F46 /* DishFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CA2A8666E1FBBAD65F141 /* DishFormView.swift */; }; D8149D55E74F7BBFC0402F46 /* DishFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CA2A8666E1FBBAD65F141 /* DishFormView.swift */; };
D89413095E37C3D677856437 /* PremiumAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0200C0C11C912711C724558 /* PremiumAccess.swift */; }; D89413095E37C3D677856437 /* PremiumAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0200C0C11C912711C724558 /* PremiumAccess.swift */; };
DCD51538934AEA15F6E87253 /* WatchWeekPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */; };
DE43F08AAC939C083BF511C1 /* FirstDishesStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10594EA55F6BEFF4241D364C /* FirstDishesStepView.swift */; }; DE43F08AAC939C083BF511C1 /* FirstDishesStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10594EA55F6BEFF4241D364C /* FirstDishesStepView.swift */; };
DE797C90DFDAFAC5C12E3D97 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBCD4FFE0D067A591104B74 /* ToastView.swift */; }; DE797C90DFDAFAC5C12E3D97 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBCD4FFE0D067A591104B74 /* ToastView.swift */; };
DE94A525EE6EB38462F5BB9E /* WatchWeekPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */; };
E12A5EE3D940260902277CC5 /* WeekCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B309A43E486B5A723324BA /* WeekCalendarView.swift */; }; E12A5EE3D940260902277CC5 /* WeekCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B309A43E486B5A723324BA /* WeekCalendarView.swift */; };
E2524335ED3A5DB7813BEA99 /* WelcomeStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70D140A6E835CA6227C2D4D9 /* WelcomeStepView.swift */; }; E2524335ED3A5DB7813BEA99 /* WelcomeStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70D140A6E835CA6227C2D4D9 /* WelcomeStepView.swift */; };
E3BD64E6D32ED7052F174923 /* TagSelectorSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6CF1C9CC9BDB3B20B1834EB /* TagSelectorSheet.swift */; }; E3BD64E6D32ED7052F174923 /* TagSelectorSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6CF1C9CC9BDB3B20B1834EB /* TagSelectorSheet.swift */; };
E5D9C4B783A14E2B9F4D2E68 /* PaywallStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61A8C92B47D5938C61E9A75 /* PaywallStepView.swift */; }; E5D9C4B783A14E2B9F4D2E68 /* PaywallStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61A8C92B47D5938C61E9A75 /* PaywallStepView.swift */; };
E6A57F29C02AE138D705BAF7 /* AppIconPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F2EE5A756C183B6D505403 /* AppIconPlaceholder.swift */; }; E6A57F29C02AE138D705BAF7 /* AppIconPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F2EE5A756C183B6D505403 /* AppIconPlaceholder.swift */; };
E8528D9162976DBEDCEF1822 /* MealMoodWatchWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 6CFA7D73F403353774761AD1 /* MealMoodWatchWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
EA4EC6244EEBE6D568BCA5E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3944B4AD876455057EB2265 /* Assets.xcassets */; }; EA4EC6244EEBE6D568BCA5E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3944B4AD876455057EB2265 /* Assets.xcassets */; };
F65AD80426AF8DA12FBD661C /* WeekendsStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035C6C0B728EB0EA80394CDD /* WeekendsStepView.swift */; }; F65AD80426AF8DA12FBD661C /* WeekendsStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035C6C0B728EB0EA80394CDD /* WeekendsStepView.swift */; };
F8241CE2559483E0B03928E0 /* WatchWeekPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */; };
FB0011111111111111111101 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = FB0011111111111111111105 /* FirebaseAnalytics */; }; FB0011111111111111111101 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = FB0011111111111111111105 /* FirebaseAnalytics */; };
FB0011111111111111111102 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FB0011111111111111111103 /* GoogleService-Info.plist */; }; FB0011111111111111111102 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FB0011111111111111111103 /* GoogleService-Info.plist */; };
FB0011111111111111111106 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB0011111111111111111107 /* AnalyticsService.swift */; }; FB0011111111111111111106 /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB0011111111111111111107 /* AnalyticsService.swift */; };
@@ -103,6 +117,13 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
65677159789AD9CAE36E0F91 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
proxyType = 1;
remoteGlobalIDString = B5CC0681B7BF74D5292A6E13;
remoteInfo = MealMoodWatchWidget;
};
A552B5A17E222A1BF7AF79A0 /* PBXContainerItemProxy */ = { A552B5A17E222A1BF7AF79A0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */; containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
@@ -110,6 +131,13 @@
remoteGlobalIDString = DE69ED74719D07B4A5770741; remoteGlobalIDString = DE69ED74719D07B4A5770741;
remoteInfo = MealMood; remoteInfo = MealMood;
}; };
C6796BCCA4F4B8F53448FBEB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
proxyType = 1;
remoteGlobalIDString = 2CF9CB17B38873677A62C598;
remoteInfo = MealMoodWatch;
};
EFE9F2305F13749C6FCAC8E5 /* PBXContainerItemProxy */ = { EFE9F2305F13749C6FCAC8E5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */; containerPortal = 7C11D794B7E45A145D5ADBFD /* Project object */;
@@ -120,6 +148,28 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
57A64D6BBE09DD4CAB3D04CF /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
3ACF9F301D6AC8A9A0B1AC3E /* MealMoodWatch.app in Embed Watch Content */,
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
A1CEBF7D5D55A335026E5798 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
E8528D9162976DBEDCEF1822 /* MealMoodWatchWidget.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
DF9575A974376FF5C6420060 /* Embed App Extensions */ = { DF9575A974376FF5C6420060 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -155,11 +205,14 @@
23957C735075B0DFB1DF51D7 /* DishDrawerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishDrawerView.swift; sourceTree = "<group>"; }; 23957C735075B0DFB1DF51D7 /* DishDrawerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishDrawerView.swift; sourceTree = "<group>"; };
2C20D9FC522FEBF7206CFD32 /* MealMoodApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealMoodApp.swift; sourceTree = "<group>"; }; 2C20D9FC522FEBF7206CFD32 /* MealMoodApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealMoodApp.swift; sourceTree = "<group>"; };
2C5CD466A50C339519544347 /* TagRulesEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagRulesEditView.swift; sourceTree = "<group>"; }; 2C5CD466A50C339519544347 /* TagRulesEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagRulesEditView.swift; sourceTree = "<group>"; };
2CF3C9FF75E9F24B32D54D5A /* MealMoodWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MealMoodWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
2E113A29DEC0551C41AB7EBB /* WeekPlanShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekPlanShareView.swift; sourceTree = "<group>"; }; 2E113A29DEC0551C41AB7EBB /* WeekPlanShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekPlanShareView.swift; sourceTree = "<group>"; };
324D77FEC4C8C371695F27CC /* PremiumView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumView.swift; sourceTree = "<group>"; }; 324D77FEC4C8C371695F27CC /* PremiumView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumView.swift; sourceTree = "<group>"; };
3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OnboardingFlowUITests.swift; sourceTree = "<group>"; }; 3CBD59AC26D5C6D138D67E35 /* OnboardingFlowUITests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OnboardingFlowUITests.swift; sourceTree = "<group>"; };
3FC93C653B5540D7205BF294 /* ShoppingItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ShoppingItem.swift; sourceTree = "<group>"; }; 3FC93C653B5540D7205BF294 /* ShoppingItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ShoppingItem.swift; sourceTree = "<group>"; };
449577ECE261177D4A1C1572 /* WatchSyncService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WatchSyncService.swift; sourceTree = "<group>"; };
4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; }; 4EDEE086FD20D16DE507B247 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
5018BA4B3DC94CA7443CA9D1 /* TodayView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TodayView.swift; sourceTree = "<group>"; };
50E1C12C3A693D7C41E9F80B /* TagPill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPill.swift; sourceTree = "<group>"; }; 50E1C12C3A693D7C41E9F80B /* TagPill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPill.swift; sourceTree = "<group>"; };
5187023D83BBC5E8D304CE3A /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; }; 5187023D83BBC5E8D304CE3A /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MealMoodWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MealMoodWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -170,8 +223,12 @@
60F0A5C2FF1B797A4BB69B39 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; 60F0A5C2FF1B797A4BB69B39 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
623EDDC4CD134883FC9D242D /* Dish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dish.swift; sourceTree = "<group>"; }; 623EDDC4CD134883FC9D242D /* Dish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dish.swift; sourceTree = "<group>"; };
62E2F6B2694CEC97AE3C1B13 /* PremiumSyncServiceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PremiumSyncServiceTests.swift; sourceTree = "<group>"; }; 62E2F6B2694CEC97AE3C1B13 /* PremiumSyncServiceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PremiumSyncServiceTests.swift; sourceTree = "<group>"; };
67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WatchWeekPayload.swift; sourceTree = "<group>"; };
683D03A3F2E26A2BA627B353 /* WeekView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WeekView.swift; sourceTree = "<group>"; };
6971B29A22117CC7621DB938 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
69B309A43E486B5A723324BA /* WeekCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekCalendarView.swift; sourceTree = "<group>"; }; 69B309A43E486B5A723324BA /* WeekCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekCalendarView.swift; sourceTree = "<group>"; };
69C0BDFEBA8B692530CB6B5E /* SpeechDictationServiceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SpeechDictationServiceTests.swift; sourceTree = "<group>"; }; 69C0BDFEBA8B692530CB6B5E /* SpeechDictationServiceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SpeechDictationServiceTests.swift; sourceTree = "<group>"; };
6CFA7D73F403353774761AD1 /* MealMoodWatchWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MealMoodWatchWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
6D718B2E4F7E6D22E502F0A6 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; }; 6D718B2E4F7E6D22E502F0A6 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
6EA1948B5DAEEEA09B78C8A9 /* SecondaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryButton.swift; sourceTree = "<group>"; }; 6EA1948B5DAEEEA09B78C8A9 /* SecondaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryButton.swift; sourceTree = "<group>"; };
70D140A6E835CA6227C2D4D9 /* WelcomeStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStepView.swift; sourceTree = "<group>"; }; 70D140A6E835CA6227C2D4D9 /* WelcomeStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStepView.swift; sourceTree = "<group>"; };
@@ -188,6 +245,7 @@
928E6D4F6D78AD1840245FB4 /* PremiumSyncService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PremiumSyncService.swift; sourceTree = "<group>"; }; 928E6D4F6D78AD1840245FB4 /* PremiumSyncService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PremiumSyncService.swift; sourceTree = "<group>"; };
93B4B31A8F6E4D7AAB1B6A01 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; }; 93B4B31A8F6E4D7AAB1B6A01 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
965555A7774FF7EB975F0A07 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; }; 965555A7774FF7EB975F0A07 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
9A43C4A6B38F731B9FE6AACB /* MealMoodWatchWidget.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MealMoodWatchWidget.swift; sourceTree = "<group>"; };
9F86EB5DDB126D73486FD956 /* MealMood.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MealMood.entitlements; sourceTree = "<group>"; }; 9F86EB5DDB126D73486FD956 /* MealMood.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MealMood.entitlements; sourceTree = "<group>"; };
9FBA11C8A81D5C0CA8A3C13D /* MealWindowsStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealWindowsStepView.swift; sourceTree = "<group>"; }; 9FBA11C8A81D5C0CA8A3C13D /* MealWindowsStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealWindowsStepView.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IngredientParser.swift; sourceTree = "<group>"; }; A1B2C3D4E5F60718293A4B02 /* IngredientParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IngredientParser.swift; sourceTree = "<group>"; };
@@ -201,6 +259,7 @@
B0A631C6D0CF7A0724ACF6A5 /* CalendarService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarService.swift; sourceTree = "<group>"; }; B0A631C6D0CF7A0724ACF6A5 /* CalendarService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarService.swift; sourceTree = "<group>"; };
B3369706BF941E09F88243F1 /* MealMoodWidget.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = MealMoodWidget.entitlements; sourceTree = "<group>"; }; B3369706BF941E09F88243F1 /* MealMoodWidget.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = MealMoodWidget.entitlements; sourceTree = "<group>"; };
B3944B4AD876455057EB2265 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; B3944B4AD876455057EB2265 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B42B3CBE6C340A58D662713D /* MealMoodWatchWidget.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = MealMoodWatchWidget.entitlements; sourceTree = "<group>"; };
B68E5C82608F60FF8798BB81 /* EmptySlotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptySlotView.swift; sourceTree = "<group>"; }; B68E5C82608F60FF8798BB81 /* EmptySlotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptySlotView.swift; sourceTree = "<group>"; };
B6CF1C9CC9BDB3B20B1834EB /* TagSelectorSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSelectorSheet.swift; sourceTree = "<group>"; }; B6CF1C9CC9BDB3B20B1834EB /* TagSelectorSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSelectorSheet.swift; sourceTree = "<group>"; };
BAC64110469AA91D5F28C423 /* MealMoodLogoMark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealMoodLogoMark.swift; sourceTree = "<group>"; }; BAC64110469AA91D5F28C423 /* MealMoodLogoMark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealMoodLogoMark.swift; sourceTree = "<group>"; };
@@ -211,8 +270,11 @@
C7B13F4D4D362E50CA65A0C1 /* MealMood.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MealMood.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7B13F4D4D362E50CA65A0C1 /* MealMood.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MealMood.app; sourceTree = BUILT_PRODUCTS_DIR; };
C7C00EED19123F00AE87FF24 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; }; C7C00EED19123F00AE87FF24 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
CB3BD0E078644749E93B0D2B /* DishListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishListView.swift; sourceTree = "<group>"; }; CB3BD0E078644749E93B0D2B /* DishListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishListView.swift; sourceTree = "<group>"; };
CBB9DAFC0FC788CF491CF9C8 /* Assets.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
CDB16B89093E7AE8B9B05202 /* MealMoodWatchApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MealMoodWatchApp.swift; sourceTree = "<group>"; };
CE7AB2ECCA7D330E50AC549E /* AutocompleteEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteEngine.swift; sourceTree = "<group>"; }; CE7AB2ECCA7D330E50AC549E /* AutocompleteEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteEngine.swift; sourceTree = "<group>"; };
CFBCD4FFE0D067A591104B74 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; }; CFBCD4FFE0D067A591104B74 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
D4B721AEEE37313806C3D468 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D63CA2A8666E1FBBAD65F141 /* DishFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishFormView.swift; sourceTree = "<group>"; }; D63CA2A8666E1FBBAD65F141 /* DishFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishFormView.swift; sourceTree = "<group>"; };
D8E2F6A3F47E5C9F92A8E6B3 /* BottomPromoBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomPromoBanner.swift; sourceTree = "<group>"; }; D8E2F6A3F47E5C9F92A8E6B3 /* BottomPromoBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomPromoBanner.swift; sourceTree = "<group>"; };
D954415A7AC73D41309D2BDF /* WeekReadyStepView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WeekReadyStepView.swift; sourceTree = "<group>"; }; D954415A7AC73D41309D2BDF /* WeekReadyStepView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WeekReadyStepView.swift; sourceTree = "<group>"; };
@@ -225,6 +287,7 @@
E8472961AE65E2A3460B67C3 /* DishViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishViewModel.swift; sourceTree = "<group>"; }; E8472961AE65E2A3460B67C3 /* DishViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DishViewModel.swift; sourceTree = "<group>"; };
E8825212ACFA0D3F6C716E94 /* ReviewPromptService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewPromptService.swift; sourceTree = "<group>"; }; E8825212ACFA0D3F6C716E94 /* ReviewPromptService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewPromptService.swift; sourceTree = "<group>"; };
EB4FC89C90928DD833240244 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; EB4FC89C90928DD833240244 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
EFA0DF3167A4409CF9C2FBEB /* MealMoodWatch.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = MealMoodWatch.entitlements; sourceTree = "<group>"; };
F0200C0C11C912711C724558 /* PremiumAccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumAccess.swift; sourceTree = "<group>"; }; F0200C0C11C912711C724558 /* PremiumAccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumAccess.swift; sourceTree = "<group>"; };
F61A8C92B47D5938C61E9A75 /* PaywallStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallStepView.swift; sourceTree = "<group>"; }; F61A8C92B47D5938C61E9A75 /* PaywallStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallStepView.swift; sourceTree = "<group>"; };
F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MealMoodTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MealMoodTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -239,6 +302,22 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
13F0BD08040EE74CEBB2E195 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
626254AEAA3AF22D24222124 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
3A0F8BC7BDE8D799F8CE9F6B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D043E5DEA954AADC016133BF /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
81780419712273974230423F /* Frameworks */ = { 81780419712273974230423F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -280,6 +359,8 @@
F4FE25386665429412E51DB4 /* Frameworks */, F4FE25386665429412E51DB4 /* Frameworks */,
2BC91A087BC03FF7810C529F /* MealMoodWidget */, 2BC91A087BC03FF7810C529F /* MealMoodWidget */,
DB44D4C1DDBFC6CE205A5AB1 /* MealMoodUITests */, DB44D4C1DDBFC6CE205A5AB1 /* MealMoodUITests */,
E42390C7051D4D2D55CD42C8 /* MealMoodWatch */,
778E43F543957BBFCD9DCCA8 /* MealMoodWatchWidget */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -389,10 +470,23 @@
F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */, F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */,
574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */, 574CB4BDE993DA0CEC251ABD /* MealMoodWidget.appex */,
212FF80B5B9E9E5243CA79D5 /* MealMoodUITests.xctest */, 212FF80B5B9E9E5243CA79D5 /* MealMoodUITests.xctest */,
2CF3C9FF75E9F24B32D54D5A /* MealMoodWatch.app */,
6CFA7D73F403353774761AD1 /* MealMoodWatchWidget.appex */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
778E43F543957BBFCD9DCCA8 /* MealMoodWatchWidget */ = {
isa = PBXGroup;
children = (
9A43C4A6B38F731B9FE6AACB /* MealMoodWatchWidget.swift */,
B42B3CBE6C340A58D662713D /* MealMoodWatchWidget.entitlements */,
D4B721AEEE37313806C3D468 /* Info.plist */,
);
name = MealMoodWatchWidget;
path = MealMoodWatchWidget;
sourceTree = "<group>";
};
7975F99C73940CF3F40F658E /* MealMoodTests */ = { 7975F99C73940CF3F40F658E /* MealMoodTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -421,6 +515,14 @@
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
8AD96D63F81926EA17540D1D /* watchOS */ = {
isa = PBXGroup;
children = (
6971B29A22117CC7621DB938 /* Foundation.framework */,
);
name = watchOS;
sourceTree = "<group>";
};
8BE925662F55707DF083677A /* Dishes */ = { 8BE925662F55707DF083677A /* Dishes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -487,6 +589,19 @@
path = MealMoodUITests; path = MealMoodUITests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E42390C7051D4D2D55CD42C8 /* MealMoodWatch */ = {
isa = PBXGroup;
children = (
CDB16B89093E7AE8B9B05202 /* MealMoodWatchApp.swift */,
5018BA4B3DC94CA7443CA9D1 /* TodayView.swift */,
683D03A3F2E26A2BA627B353 /* WeekView.swift */,
EFA0DF3167A4409CF9C2FBEB /* MealMoodWatch.entitlements */,
CBB9DAFC0FC788CF491CF9C8 /* Assets.xcassets */,
);
name = MealMoodWatch;
path = MealMoodWatch;
sourceTree = "<group>";
};
E452C59D71AAD6175947503D /* Onboarding */ = { E452C59D71AAD6175947503D /* Onboarding */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -536,6 +651,7 @@
BB0D740B79B9E129A3C2F382 /* WidgetKit.framework */, BB0D740B79B9E129A3C2F382 /* WidgetKit.framework */,
EB4FC89C90928DD833240244 /* WidgetKit.framework */, EB4FC89C90928DD833240244 /* WidgetKit.framework */,
C2D1AAC0DB21EA5E2CCA38AD /* SwiftUI.framework */, C2D1AAC0DB21EA5E2CCA38AD /* SwiftUI.framework */,
8AD96D63F81926EA17540D1D /* watchOS */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -563,6 +679,8 @@
A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */, A1B2C3D4E5F60718293A4B04 /* SpeechDictationService.swift */,
5C512046B1410E236A3F6286 /* ShoppingListService.swift */, 5C512046B1410E236A3F6286 /* ShoppingListService.swift */,
A1C3CBAFE191AD6643F861B7 /* DeduplicationService.swift */, A1C3CBAFE191AD6643F861B7 /* DeduplicationService.swift */,
67CBDF2D2972139881AA59B1 /* WatchWeekPayload.swift */,
449577ECE261177D4A1C1572 /* WatchSyncService.swift */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -570,6 +688,25 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
2CF9CB17B38873677A62C598 /* MealMoodWatch */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7195C2A035F3497FCF531730 /* Build configuration list for PBXNativeTarget "MealMoodWatch" */;
buildPhases = (
9F03F53FADB474C1F7FB3D7E /* Sources */,
3A0F8BC7BDE8D799F8CE9F6B /* Frameworks */,
4E9CFD2F7063DD37E9553C5D /* Resources */,
A1CEBF7D5D55A335026E5798 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
07DF8D10C8945378AF49587A /* PBXTargetDependency */,
);
name = MealMoodWatch;
productName = MealMoodWatch;
productReference = 2CF3C9FF75E9F24B32D54D5A /* MealMoodWatch.app */;
productType = "com.apple.product-type.application";
};
71CBF84DA3DD6F325A40C1A1 /* MealMoodTests */ = { 71CBF84DA3DD6F325A40C1A1 /* MealMoodTests */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 9E9B4B2D60B7D7F8BF816CBA /* Build configuration list for PBXNativeTarget "MealMoodTests" */; buildConfigurationList = 9E9B4B2D60B7D7F8BF816CBA /* Build configuration list for PBXNativeTarget "MealMoodTests" */;
@@ -586,6 +723,23 @@
productReference = F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */; productReference = F766AA7898D6F391DE1B9A81 /* MealMoodTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test"; productType = "com.apple.product-type.bundle.unit-test";
}; };
B5CC0681B7BF74D5292A6E13 /* MealMoodWatchWidget */ = {
isa = PBXNativeTarget;
buildConfigurationList = 73143AB008A07A9F6FEFF740 /* Build configuration list for PBXNativeTarget "MealMoodWatchWidget" */;
buildPhases = (
51AFBA22843E38FACDE8FAEB /* Sources */,
13F0BD08040EE74CEBB2E195 /* Frameworks */,
AA893EB631197B900CD824CD /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = MealMoodWatchWidget;
productName = MealMoodWatchWidget;
productReference = 6CFA7D73F403353774761AD1 /* MealMoodWatchWidget.appex */;
productType = "com.apple.product-type.app-extension";
};
C82BD86FE72A5E319983AEE3 /* MealMoodWidget */ = { C82BD86FE72A5E319983AEE3 /* MealMoodWidget */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 14CECD46CDA9DFDC2D928807 /* Build configuration list for PBXNativeTarget "MealMoodWidget" */; buildConfigurationList = 14CECD46CDA9DFDC2D928807 /* Build configuration list for PBXNativeTarget "MealMoodWidget" */;
@@ -611,11 +765,13 @@
60AC9519630B6008787A8859 /* Resources */, 60AC9519630B6008787A8859 /* Resources */,
9240A703A486116A9871C534 /* Frameworks */, 9240A703A486116A9871C534 /* Frameworks */,
DF9575A974376FF5C6420060 /* Embed App Extensions */, DF9575A974376FF5C6420060 /* Embed App Extensions */,
57A64D6BBE09DD4CAB3D04CF /* Embed Watch Content */,
FB001111111111111111110C /* Upload dSYM to Crashlytics */, FB001111111111111111110C /* Upload dSYM to Crashlytics */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
C89437B93D1C51D2396C1B31 /* PBXTargetDependency */,
); );
name = MealMood; name = MealMood;
packageProductDependencies = ( packageProductDependencies = (
@@ -681,6 +837,8 @@
71CBF84DA3DD6F325A40C1A1 /* MealMoodTests */, 71CBF84DA3DD6F325A40C1A1 /* MealMoodTests */,
C82BD86FE72A5E319983AEE3 /* MealMoodWidget */, C82BD86FE72A5E319983AEE3 /* MealMoodWidget */,
FCA0786EFDD5E822F3A19767 /* MealMoodUITests */, FCA0786EFDD5E822F3A19767 /* MealMoodUITests */,
2CF9CB17B38873677A62C598 /* MealMoodWatch */,
B5CC0681B7BF74D5292A6E13 /* MealMoodWatchWidget */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -693,6 +851,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
4E9CFD2F7063DD37E9553C5D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5828DA9ED4D8B892E9EFC1F4 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
60AC9519630B6008787A8859 /* Resources */ = { 60AC9519630B6008787A8859 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -712,6 +878,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
AA893EB631197B900CD824CD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
@@ -747,11 +920,32 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
51AFBA22843E38FACDE8FAEB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
519AA84E7D3B9ED5FC215E4B /* MealMoodWatchWidget.swift in Sources */,
DCD51538934AEA15F6E87253 /* WatchWeekPayload.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9F03F53FADB474C1F7FB3D7E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A805D5DEA5B7EA1374FE3118 /* MealMoodWatchApp.swift in Sources */,
8B4672C76B05BC1DFB5C979D /* TodayView.swift in Sources */,
0A498D2C7D9F798538471810 /* WeekView.swift in Sources */,
DE94A525EE6EB38462F5BB9E /* WatchWeekPayload.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A6B405631E28429E34B68811 /* Sources */ = { A6B405631E28429E34B68811 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A4D50C0B4E91B3542C63017A /* MealMoodWidget.swift in Sources */, A4D50C0B4E91B3542C63017A /* MealMoodWidget.swift in Sources */,
F8241CE2559483E0B03928E0 /* WatchWeekPayload.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -843,12 +1037,20 @@
44E43D5F00D1B513F9BABE80 /* ShoppingListService.swift in Sources */, 44E43D5F00D1B513F9BABE80 /* ShoppingListService.swift in Sources */,
57F1EA6B5E3AF88A61C48354 /* ShoppingListView.swift in Sources */, 57F1EA6B5E3AF88A61C48354 /* ShoppingListView.swift in Sources */,
6685C591F837FAFB29A322F7 /* DeduplicationService.swift in Sources */, 6685C591F837FAFB29A322F7 /* DeduplicationService.swift in Sources */,
BF89CD5E8CA0DFFC224CF94D /* WatchWeekPayload.swift in Sources */,
C6DBE6B5B4BE002A234CBCCA /* WatchSyncService.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
07DF8D10C8945378AF49587A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MealMoodWatchWidget;
target = B5CC0681B7BF74D5292A6E13 /* MealMoodWatchWidget */;
targetProxy = 65677159789AD9CAE36E0F91 /* PBXContainerItemProxy */;
};
4276CDDEA774B000AE30090D /* PBXTargetDependency */ = { 4276CDDEA774B000AE30090D /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
name = MealMood; name = MealMood;
@@ -860,6 +1062,12 @@
target = DE69ED74719D07B4A5770741 /* MealMood */; target = DE69ED74719D07B4A5770741 /* MealMood */;
targetProxy = EFE9F2305F13749C6FCAC8E5 /* PBXContainerItemProxy */; targetProxy = EFE9F2305F13749C6FCAC8E5 /* PBXContainerItemProxy */;
}; };
C89437B93D1C51D2396C1B31 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MealMoodWatch;
target = 2CF9CB17B38873677A62C598 /* MealMoodWatch */;
targetProxy = C6796BCCA4F4B8F53448FBEB /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
@@ -879,6 +1087,33 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
09BB2FC251B9F7EC8971E3F7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = MealMoodWatch/MealMoodWatch.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = 2825Q76T7H;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = MealMood;
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.alexandrevazquez.mealmood;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.watchkitapp;
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 10.0;
};
name = Debug;
};
0DF34D8B955C5894B2816878 /* Release */ = { 0DF34D8B955C5894B2816878 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -952,6 +1187,83 @@
}; };
name = Release; name = Release;
}; };
51375C48710F15802F82375B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = MealMoodWatch/MealMoodWatch.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = 2825Q76T7H;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = MealMood;
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = com.alexandrevazquez.mealmood;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.watchkitapp;
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
VALIDATE_PRODUCT = YES;
WATCHOS_DEPLOYMENT_TARGET = 10.0;
};
name = Release;
};
5802B9DCA57D9C76B6F6297E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = MealMoodWatchWidget/MealMoodWatchWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MealMoodWatchWidget/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.watchkitapp.widget;
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 10.0;
};
name = Debug;
};
708F80AFABC2BBF5E8B02549 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = MealMoodWatchWidget/MealMoodWatchWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 79;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MealMoodWatchWidget/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood.watchkitapp.widget;
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
VALIDATE_PRODUCT = YES;
WATCHOS_DEPLOYMENT_TARGET = 10.0;
};
name = Release;
};
70E1B27C392FF6A967A2CEF0 /* Release */ = { 70E1B27C392FF6A967A2CEF0 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -1030,7 +1342,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2.0.5; MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood;
PRODUCT_NAME = MealMood; PRODUCT_NAME = MealMood;
PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood AppStore"; PROVISIONING_PROFILE_SPECIFIER = "com.alexandrevazquez.mealmood AppStore";
@@ -1153,7 +1465,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2.0.5; MARKETING_VERSION = 2.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood;
PRODUCT_NAME = MealMood; PRODUCT_NAME = MealMood;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1182,6 +1494,24 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
7195C2A035F3497FCF531730 /* Build configuration list for PBXNativeTarget "MealMoodWatch" */ = {
isa = XCConfigurationList;
buildConfigurations = (
51375C48710F15802F82375B /* Release */,
09BB2FC251B9F7EC8971E3F7 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
73143AB008A07A9F6FEFF740 /* Build configuration list for PBXNativeTarget "MealMoodWatchWidget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
708F80AFABC2BBF5E8B02549 /* Release */,
5802B9DCA57D9C76B6F6297E /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
82E16632C9B76AC33DE64CBB /* Build configuration list for PBXProject "MealMood" */ = { 82E16632C9B76AC33DE64CBB /* Build configuration list for PBXProject "MealMood" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.0.5</string> <string>2.1.0</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
+82
View File
@@ -0,0 +1,82 @@
import Foundation
import WatchConnectivity
import SwiftData
/// iPhone Watch push of the week snapshot. Uses `updateApplicationContext`,
/// which delivers the latest value even if the watch is unreachable right now.
final class WatchSyncService: NSObject, WCSessionDelegate, @unchecked Sendable {
// WCSession requires a stable delegate; state is confined to WCSession's
// internal queue, hence the @unchecked Sendable.
static let shared = WatchSyncService()
private override init() {
super.init()
guard WCSession.isSupported() else { return }
WCSession.default.delegate = self
WCSession.default.activate()
}
func activate() { /* init side effect */ }
/// Builds and pushes the current + visible week. Call wherever the iOS
/// widget is refreshed so both stay in sync.
func push(plan: WeekPlan?, dishes: [Dish], settings: AppSettings) {
guard WCSession.isSupported(), WCSession.default.activationState == .activated else { return }
guard let payload = Self.makePayload(plan: plan, dishes: dishes, settings: settings),
let data = payload.encoded() else { return }
try? WCSession.default.updateApplicationContext([WatchWeekPayload.storageKey: data])
}
static func makePayload(plan: WeekPlan?, dishes: [Dish], settings: AppSettings) -> WatchWeekPayload? {
guard let plan else { return nil }
let locale = Locale(identifier: settings.languageEnum.resolved().localeIdentifier)
let dishById = Dictionary(dishes.map { ($0.id, $0) }, uniquingKeysWith: { first, _ in first })
let dayFormatter = DateFormatter()
dayFormatter.locale = locale
dayFormatter.setLocalizedDateFormatFromTemplate("EEE d")
let todayOffset = (Calendar.current.component(.weekday, from: Date()) + 5) % 7
let isCurrentWeek = plan.weekStartDate == Date().startOfWeek()
let range = settings.includeWeekends ? 0...6 : 0...4
let days: [WatchWeekPayload.Day] = range.map { day in
let meals: [WatchWeekPayload.Meal] = settings.activeMealTypes.map { meal in
let slot = plan.slotList.first { $0.dayOfWeek == day && $0.mealType == meal.rawValue }
let label = String(localized: String.LocalizationValue(meal.localizedKey))
var name: String?
if let slot {
if slot.isEatingOut {
name = String(localized: "slot_eating_out")
} else if slot.isSkipped {
name = ""
} else if let dishId = slot.dishId, let dish = dishById[dishId] {
name = dish.name
if let secondaryId = slot.secondaryDishId, let secondary = dishById[secondaryId] {
name! += " + \(secondary.name)"
}
}
}
return WatchWeekPayload.Meal(type: meal.rawValue, label: label, name: name)
}
let dayDate = plan.weekStartDate.addingDays(day)
return WatchWeekPayload.Day(
dayOfWeek: day,
title: dayFormatter.string(from: dayDate).capitalized(with: locale),
isToday: isCurrentWeek && day == todayOffset,
meals: meals
)
}
return WatchWeekPayload(
weekTitle: plan.weekStartDate.formattedWeekRange(),
days: days,
updatedAt: Date()
)
}
// MARK: - WCSessionDelegate
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) { session.activate() }
}
+53
View File
@@ -0,0 +1,53 @@
import Foundation
/// Snapshot of the week the iPhone pushes to the Watch (WatchConnectivity) and
/// the watch widget reads from the watch-side app group. Labels and day titles
/// arrive already localized the watch never formats, only renders.
/// Member of the iOS app, watch app and watch widget targets.
struct WatchWeekPayload: Codable {
struct Meal: Codable {
let type: String // MealType rawValue, for the icon
let label: String // localized ("Comida", "Cena")
let name: String? // dish (+ secondary), "Comer fuera", or nil if empty
}
struct Day: Codable {
let dayOfWeek: Int // 0=Monday 6=Sunday
let title: String // localized short title ("Lun 8")
let isToday: Bool
let meals: [Meal]
}
let weekTitle: String // localized week range ("8 14 sep")
let days: [Day]
let updatedAt: Date
static let appGroupID = "group.com.alexandrevazquez.mealmood"
static let storageKey = "watch_week_payload_v1"
func encoded() -> Data? { try? JSONEncoder().encode(self) }
static func decode(_ data: Data) -> WatchWeekPayload? {
try? JSONDecoder().decode(WatchWeekPayload.self, from: data)
}
/// Reads the last snapshot stored on this device's app group.
static func stored() -> WatchWeekPayload? {
guard let data = UserDefaults(suiteName: appGroupID)?.data(forKey: storageKey) else { return nil }
return decode(data)
}
func store() {
UserDefaults(suiteName: Self.appGroupID)?.set(encoded(), forKey: Self.storageKey)
}
static func icon(for mealType: String) -> String {
switch mealType {
case "breakfast": return "cup.and.saucer.fill"
case "lunch": return "sun.max.fill"
case "snack": return "carrot.fill"
case "dinner": return "moon.stars.fill"
default: return "fork.knife"
}
}
}
+6
View File
@@ -999,6 +999,12 @@ struct HomeView: View {
private func updateWidget(settings: AppSettings) { private func updateWidget(settings: AppSettings) {
let todayPlan = fetchWeekPlan(for: Date().startOfWeek()) let todayPlan = fetchWeekPlan(for: Date().startOfWeek())
WidgetDataStore.update(plan: todayPlan, dishes: dishes, settings: settings) WidgetDataStore.update(plan: todayPlan, dishes: dishes, settings: settings)
// Week snapshot: stored locally for the iOS week widget and pushed to
// the watch app/complication via WatchConnectivity.
if let payload = WatchSyncService.makePayload(plan: todayPlan, dishes: dishes, settings: settings) {
payload.store()
}
WatchSyncService.shared.push(plan: todayPlan, dishes: dishes, settings: settings)
} }
@ViewBuilder @ViewBuilder
@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "icon-1024.png",
"idiom" : "universal",
"platform" : "watchos",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 681 KiB

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.alexandrevazquez.mealmood</string>
</array>
</dict>
</plist>
+57
View File
@@ -0,0 +1,57 @@
import SwiftUI
import WatchConnectivity
import WidgetKit
@main
struct MealMoodWatchApp: App {
@StateObject private var store = WatchWeekStore.shared
var body: some Scene {
WindowGroup {
TabView {
TodayView()
WeekView()
}
.tabViewStyle(.verticalPage)
.environmentObject(store)
}
}
}
/// Receives the week snapshot from the iPhone and persists it in the watch's
/// app group so the complication can read it too.
final class WatchWeekStore: NSObject, ObservableObject, WCSessionDelegate {
static let shared = WatchWeekStore()
@Published var payload: WatchWeekPayload?
private override init() {
super.init()
payload = WatchWeekPayload.stored()
guard WCSession.isSupported() else { return }
WCSession.default.delegate = self
WCSession.default.activate()
}
private func apply(_ data: Data) {
guard let received = WatchWeekPayload.decode(data) else { return }
DispatchQueue.main.async {
self.payload = received
received.store()
WidgetCenter.shared.reloadAllTimelines()
}
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
// Pick up a context delivered while the app wasn't running.
if let data = session.receivedApplicationContext[WatchWeekPayload.storageKey] as? Data {
apply(data)
}
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
if let data = applicationContext[WatchWeekPayload.storageKey] as? Data {
apply(data)
}
}
}
+54
View File
@@ -0,0 +1,54 @@
import SwiftUI
struct TodayView: View {
@EnvironmentObject private var store: WatchWeekStore
private var today: WatchWeekPayload.Day? {
store.payload?.days.first(where: \.isToday)
}
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 8) {
if let today {
Text(today.title)
.font(.headline)
.foregroundStyle(Color(red: 1.0, green: 0.45, blue: 0.35))
ForEach(Array(today.meals.enumerated()), id: \.offset) { _, meal in
VStack(alignment: .leading, spacing: 2) {
Label(meal.label, systemImage: WatchWeekPayload.icon(for: meal.type))
.font(.caption2)
.foregroundStyle(.secondary)
Text(meal.name ?? "")
.font(.body.weight(.medium))
.lineLimit(2)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
}
} else {
EmptySyncView()
}
}
}
.navigationTitle("MealMood")
}
}
struct EmptySyncView: View {
var body: some View {
VStack(spacing: 8) {
Image(systemName: "iphone.and.arrow.forward")
.font(.title3)
.foregroundStyle(.secondary)
// The payload arrives fully localized from the iPhone; the watch
// bundle carries no strings of its own, so this stays neutral.
Text(verbatim: "MealMood")
.font(.caption2)
.foregroundStyle(.secondary)
}
.padding(.top, 20)
}
}
+37
View File
@@ -0,0 +1,37 @@
import SwiftUI
struct WeekView: View {
@EnvironmentObject private var store: WatchWeekStore
var body: some View {
Group {
if let payload = store.payload {
List {
Section(payload.weekTitle) {
ForEach(payload.days, id: \.dayOfWeek) { day in
VStack(alignment: .leading, spacing: 3) {
Text(day.title)
.font(.caption.weight(.semibold))
.foregroundStyle(day.isToday
? Color(red: 1.0, green: 0.45, blue: 0.35)
: .primary)
ForEach(Array(day.meals.enumerated()), id: \.offset) { _, meal in
HStack(spacing: 4) {
Image(systemName: WatchWeekPayload.icon(for: meal.type))
.font(.system(size: 10))
.foregroundStyle(.secondary)
Text(meal.name ?? "")
.font(.caption2)
.lineLimit(1)
}
}
}
}
}
}
} else {
EmptySyncView()
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>MealMood</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2.1.0</string>
<key>CFBundleVersion</key>
<string>79</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.alexandrevazquez.mealmood</string>
</array>
</dict>
</plist>
@@ -0,0 +1,114 @@
import WidgetKit
import SwiftUI
/// Watch complication: today's meals from the last snapshot the watch app
/// stored in the app group. Refreshed via WidgetCenter whenever the watch app
/// receives new data from the iPhone.
@main
struct MealMoodWatchWidgetBundle: WidgetBundle {
var body: some Widget {
MealMoodWatchWidget()
}
}
struct TodayEntry: TimelineEntry {
let date: Date
let day: WatchWeekPayload.Day?
}
struct TodayProvider: TimelineProvider {
func placeholder(in context: Context) -> TodayEntry {
TodayEntry(date: Date(), day: sampleDay)
}
func getSnapshot(in context: Context, completion: @escaping (TodayEntry) -> Void) {
completion(TodayEntry(date: Date(), day: currentDay ?? sampleDay))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<TodayEntry>) -> Void) {
let entry = TodayEntry(date: Date(), day: currentDay)
// Refresh after midnight so "today" rolls over even without new pushes.
let nextMidnight = Calendar.current.startOfDay(for: Date()).addingTimeInterval(86_400 + 300)
completion(Timeline(entries: [entry], policy: .after(nextMidnight)))
}
private var currentDay: WatchWeekPayload.Day? {
guard let payload = WatchWeekPayload.stored() else { return nil }
let todayOffset = (Calendar.current.component(.weekday, from: Date()) + 5) % 7
return payload.days.first { $0.dayOfWeek == todayOffset } ?? payload.days.first(where: \.isToday)
}
private var sampleDay: WatchWeekPayload.Day {
WatchWeekPayload.Day(
dayOfWeek: 0, title: "Lun 8", isToday: true,
meals: [
.init(type: "lunch", label: "Comida", name: "Ensalada César"),
.init(type: "dinner", label: "Cena", name: "Salmón al horno")
]
)
}
}
struct MealMoodWatchWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "MealMoodWatchToday", provider: TodayProvider()) { entry in
TodayComplicationView(entry: entry)
.containerBackground(.clear, for: .widget)
}
.configurationDisplayName("MealMood")
.supportedFamilies([.accessoryRectangular, .accessoryInline, .accessoryCircular])
}
}
struct TodayComplicationView: View {
@Environment(\.widgetFamily) private var family
let entry: TodayEntry
var body: some View {
switch family {
case .accessoryInline:
if let meal = nextMeal {
Text("\(meal.label): \(meal.name ?? "")")
} else {
Text("MealMood")
}
case .accessoryCircular:
VStack(spacing: 0) {
Image(systemName: WatchWeekPayload.icon(for: nextMeal?.type ?? "dinner"))
.font(.system(size: 14, weight: .semibold))
Text(nextMeal?.name?.prefix(6) ?? "")
.font(.system(size: 9))
.lineLimit(1)
}
default:
VStack(alignment: .leading, spacing: 2) {
if let day = entry.day {
ForEach(Array(day.meals.prefix(3).enumerated()), id: \.offset) { _, meal in
HStack(spacing: 3) {
Image(systemName: WatchWeekPayload.icon(for: meal.type))
.font(.system(size: 9))
Text(meal.name ?? "")
.font(.system(size: 12, weight: .medium))
.lineLimit(1)
}
}
} else {
Text(verbatim: "MealMood")
.font(.system(size: 12, weight: .semibold))
Image(systemName: "iphone.and.arrow.forward")
.font(.system(size: 10))
.foregroundStyle(.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
/// Lunch until 16:00 local, dinner after simple heuristic for the small families.
private var nextMeal: WatchWeekPayload.Meal? {
guard let meals = entry.day?.meals, !meals.isEmpty else { return nil }
let hour = Calendar.current.component(.hour, from: entry.date)
if hour >= 16, let dinner = meals.last(where: { $0.name != nil }) { return dinner }
return meals.first(where: { $0.name != nil }) ?? meals.first
}
}
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.0.5</string> <string>2.1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>78</string> <string>79</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
+85
View File
@@ -332,6 +332,13 @@ struct MealMoodWidgetView: View {
// MARK: - Widget definition // MARK: - Widget definition
@main @main
struct MealMoodWidgetBundle: WidgetBundle {
var body: some Widget {
MealMoodWidget()
MealMoodWeekWidget()
}
}
struct MealMoodWidget: Widget { struct MealMoodWidget: Widget {
let kind = "MealMoodWidget" let kind = "MealMoodWidget"
@@ -344,3 +351,81 @@ struct MealMoodWidget: Widget {
.supportedFamilies([.systemSmall, .systemMedium, .accessoryRectangular, .accessoryInline]) .supportedFamilies([.systemSmall, .systemMedium, .accessoryRectangular, .accessoryInline])
} }
} }
// MARK: - Week widget (2.1.0)
struct WeekEntry: TimelineEntry {
let date: Date
let payload: WatchWeekPayload?
}
struct WeekProvider: TimelineProvider {
func placeholder(in context: Context) -> WeekEntry {
WeekEntry(date: Date(), payload: WatchWeekPayload.stored())
}
func getSnapshot(in context: Context, completion: @escaping (WeekEntry) -> Void) {
completion(WeekEntry(date: Date(), payload: WatchWeekPayload.stored()))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeekEntry>) -> Void) {
let entry = WeekEntry(date: Date(), payload: WatchWeekPayload.stored())
let nextMidnight = Calendar.current.startOfDay(for: Date()).addingTimeInterval(86_400 + 300)
completion(Timeline(entries: [entry], policy: .after(nextMidnight)))
}
}
struct MealMoodWeekWidget: Widget {
let kind = "MealMoodWeekWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: WeekProvider()) { entry in
WeekWidgetView(entry: entry)
.containerBackground(Color(red: 1.0, green: 0.97, blue: 0.94), for: .widget)
}
.configurationDisplayName("MealMood — Semana")
.description("Your whole week at a glance.")
.supportedFamilies([.systemMedium, .systemLarge])
}
}
struct WeekWidgetView: View {
@Environment(\.widgetFamily) private var family
let entry: WeekEntry
var body: some View {
Group {
if let payload = entry.payload, !payload.days.isEmpty {
VStack(alignment: .leading, spacing: family == .systemLarge ? 6 : 3) {
ForEach(payload.days, id: \.dayOfWeek) { day in
HStack(alignment: .firstTextBaseline, spacing: 6) {
Text(day.title)
.font(.system(size: family == .systemLarge ? 13 : 10, weight: .bold, design: .rounded))
.foregroundStyle(day.isToday ? Color(red: 1.0, green: 0.45, blue: 0.35) : .primary)
.frame(width: family == .systemLarge ? 62 : 48, alignment: .leading)
Text(mealsLine(day))
.font(.system(size: family == .systemLarge ? 12 : 10, design: .rounded))
.foregroundStyle(.secondary)
.lineLimit(family == .systemLarge ? 2 : 1)
Spacer(minLength: 0)
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
} else {
VStack(spacing: 4) {
Image(systemName: "calendar")
.foregroundStyle(.secondary)
Text(verbatim: "MealMood")
.font(.caption2)
.foregroundStyle(.secondary)
}
}
}
.widgetURL(URL(string: "mealmood://today"))
}
private func mealsLine(_ day: WatchWeekPayload.Day) -> String {
day.meals.compactMap(\.name).joined(separator: " · ")
}
}