diff --git a/CLAUDE.md b/CLAUDE.md
index 3ada835..8c38e5f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,7 +4,7 @@
- **Name**: MealMood: Family Meal Planner
- **Bundle ID**: com.alexandrevazquez.mealmood
- **Platform**: iOS/iPadOS (also runs as iOS app on Mac)
-- **Current version**: 1.0.4
+- **Current version**: 1.1.0
- **Main branch**: `1.0.1` (production), feature branches named after versions
## Credentials — all managed via `pass` (GPG-encrypted, syncs to Gitea)
diff --git a/MealMood.xcodeproj/project.pbxproj b/MealMood.xcodeproj/project.pbxproj
index 4f75ac7..3123a01 100644
--- a/MealMood.xcodeproj/project.pbxproj
+++ b/MealMood.xcodeproj/project.pbxproj
@@ -860,7 +860,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = MealMood/Resources/MealMood.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 31;
+ CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MealMood/Resources/Info.plist;
@@ -869,7 +869,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.5;
+ MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood;
PRODUCT_NAME = MealMood;
SDKROOT = iphoneos;
@@ -965,7 +965,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = MealMood/Resources/MealMood.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 31;
+ CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MealMood/Resources/Info.plist;
@@ -974,7 +974,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.5;
+ MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.mealmood;
PRODUCT_NAME = MealMood;
SDKROOT = iphoneos;
diff --git a/MealMood/Configuration/MealMood.storekit b/MealMood/Configuration/MealMood.storekit
index db1b232..59427ae 100644
--- a/MealMood/Configuration/MealMood.storekit
+++ b/MealMood/Configuration/MealMood.storekit
@@ -14,7 +14,21 @@
],
"products" : [
-
+ {
+ "displayPrice" : "2.99",
+ "familyShareable" : false,
+ "internalID" : "B2F4D1A0-3E7C-4F8B-9D2E-1A5C6B0E3F7D",
+ "localizations" : [
+ {
+ "description" : "Lifetime access to all premium features.",
+ "displayName" : "MealMood Premium (Legacy)",
+ "locale" : "en_US"
+ }
+ ],
+ "productID" : "com.mealmood.premium.monthly",
+ "referenceName" : "MealMood Premium Legacy",
+ "type" : "NonConsumable"
+ }
],
"settings" : {
"_applicationInternalID" : "2147483647",
@@ -60,7 +74,7 @@
"locale" : "en_US"
}
],
- "productID" : "com.mealmood.premium.monthly",
+ "productID" : "com.mealmood.premium.monthly.sub",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "MealMood Premium Monthly",
"subscriptionPricePointID" : "0"
diff --git a/MealMood/Resources/Info.plist b/MealMood/Resources/Info.plist
index 4c7b410..0aa15f9 100644
--- a/MealMood/Resources/Info.plist
+++ b/MealMood/Resources/Info.plist
@@ -15,11 +15,13 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.0.6
+ 1.1.1
CFBundleVersion
- 41
+ 46
GADApplicationIdentifier
ca-app-pub-1549720748100858~9985112590
+ ITSAppUsesNonExemptEncryption
+
LSRequiresIPhoneOS
NSCalendarsFullAccessUsageDescription
diff --git a/MealMood/Services/StoreManager.swift b/MealMood/Services/StoreManager.swift
index 44b5c8f..6ffb1a6 100644
--- a/MealMood/Services/StoreManager.swift
+++ b/MealMood/Services/StoreManager.swift
@@ -27,11 +27,13 @@ final class StoreManager: ObservableObject {
@Published var debugLoadedProductIds: [String] = []
@Published var debugLoadedProducts: [String] = []
- static let monthlyProductId = "com.mealmood.premium.monthly"
+ static let monthlyProductId = "com.mealmood.premium.monthly.sub"
+ static let legacyOneTimeProductId = "com.mealmood.premium.monthly"
private var productIds: [String] {
- var ids = [Self.monthlyProductId]
+ var ids = [Self.monthlyProductId, Self.legacyOneTimeProductId]
if let bundleId = Bundle.main.bundleIdentifier {
+ ids.append("\(bundleId).premium.monthly.sub")
ids.append("\(bundleId).premium.monthly")
}
@@ -115,42 +117,35 @@ final class StoreManager: ObservableObject {
}
var monthlyProduct: Product? {
- if let match = products.first(where: { product in
- product.id == Self.monthlyProductId && product.type == .autoRenewable
- }) {
+ if let match = products.first(where: { $0.id == Self.monthlyProductId && $0.type == .autoRenewable }) {
return match
}
-
- if let match = products.first(where: { $0.id == Self.monthlyProductId }) {
- return match
- }
-
- if let match = products.first(where: { $0.id.contains("monthly") && $0.type == .autoRenewable }) {
- return match
- }
-
if let match = products.first(where: { $0.type == .autoRenewable }) {
return match
}
-
+ if let match = products.first(where: { $0.id == Self.legacyOneTimeProductId }) {
+ return match
+ }
return products.first
}
var debugProductIds: [String] { productIds }
static func hasActiveSubscription() async -> Bool {
- // Check all known product ID variants to handle both the canonical ID
- // and any bundle-prefixed IDs that may have been used at purchase time.
- var knownIds: [String] = [monthlyProductId]
- if let bundleId = Bundle.main.bundleIdentifier {
- knownIds.append("\(bundleId).premium.monthly")
- }
- let ids = Set(knownIds)
+ let bundleId = Bundle.main.bundleIdentifier ?? ""
+ let subscriptionIds: Set = [monthlyProductId, "\(bundleId).premium.monthly.sub"]
+ // Legacy one-time purchases grant lifetime access — no expiration check needed.
+ let legacyIds: Set = [legacyOneTimeProductId, "\(bundleId).premium.monthly"]
for await result in Transaction.currentEntitlements {
- if case .verified(let transaction) = result,
- ids.contains(transaction.productID),
- transaction.revocationDate == nil {
+ guard case .verified(let transaction) = result,
+ transaction.revocationDate == nil else { continue }
+
+ if legacyIds.contains(transaction.productID) {
+ return true
+ }
+
+ if subscriptionIds.contains(transaction.productID) {
if let expirationDate = transaction.expirationDate, expirationDate < Date() {
continue
}
diff --git a/MealMoodWidget/Info.plist b/MealMoodWidget/Info.plist
index f43ee24..8256103 100644
--- a/MealMoodWidget/Info.plist
+++ b/MealMoodWidget/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 1.0.6
+ 1.1.1
CFBundleVersion
- 41
+ 46
NSExtension
NSExtensionPointIdentifier
diff --git a/fastlane/Deliverfile b/fastlane/Deliverfile
index 1a8c05a..eceeead 100644
--- a/fastlane/Deliverfile
+++ b/fastlane/Deliverfile
@@ -1 +1,2 @@
run_precheck_before_submit(false)
+force(true)
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 8ffc8c0..17cee9a 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -22,7 +22,7 @@ platform :ios do
build_app(
scheme: "MealMood",
export_method: "app-store",
- sdk: "iphoneos26.4",
+ sdk: "iphoneos26.5",
export_options: EXPORT_OPTIONS
)
upload_to_testflight(skip_waiting_for_build_processing: true)
@@ -33,7 +33,7 @@ platform :ios do
increment_build_number(xcodeproj: "MealMood.xcodeproj")
build_app(
scheme: "MealMood",
- sdk: "iphoneos26.4",
+ sdk: "iphoneos26.5",
export_options: EXPORT_OPTIONS
)
upload_to_app_store(force: true)
diff --git a/fastlane/metadata/de-DE/promotional_text.txt b/fastlane/metadata/de-DE/promotional_text.txt
index 9911558..71106ff 100644
--- a/fastlane/metadata/de-DE/promotional_text.txt
+++ b/fastlane/metadata/de-DE/promotional_text.txt
@@ -1 +1 @@
-NEU in 1.0.6: Prioritätsgerichte, tagesbasierte Regeln, Gerichte direkt ersetzen und unvollständige Woche exportieren.
+NEU in 1.1.0: Premium ist jetzt ein Monatsabo. Bestehende Einmalkäufer behalten ihren vollen Zugang automatisch.
diff --git a/fastlane/metadata/de-DE/release_notes.txt b/fastlane/metadata/de-DE/release_notes.txt
index dc91469..8e9bbe2 100644
--- a/fastlane/metadata/de-DE/release_notes.txt
+++ b/fastlane/metadata/de-DE/release_notes.txt
@@ -1,8 +1,4 @@
-Neu in Version 1.0.6:
+Neu in Version 1.1.0:
-• Prioritätsgerichte — markiere ein Gericht mit einem Stern, damit die Auto-Vervollständigung es zuerst wählt.
-• Tagesregeln — Tag-Einschränkungen unterstützen jetzt "nur Wochentage" oder "nur Wochenende".
-• Auto-ausfüllen-Schaltfläche — jetzt direkt sichtbar, wenn leere Slots vorhanden sind.
-• Tippen zum Ersetzen — tippe auf einen gefüllten Slot, um das Gericht direkt zu tauschen.
-• Jederzeit exportieren — teile deine Woche auch wenn sie unvollständig ist; leere Slots zeigen "Noch offen".
-• Sonntags-Erinnerung — neue Benachrichtigung zum Planen der Woche vor Montag.
+• Premium ist jetzt ein monatliches Abo — unbegrenzte Gerichte, Planung und Export für 2,99 €/Monat.
+• Bestehende Einmalkäufer behalten ihren vollen Zugang automatisch — keine Aktion erforderlich.
diff --git a/fastlane/metadata/en-US/promotional_text.txt b/fastlane/metadata/en-US/promotional_text.txt
index 727f1ca..93b630f 100644
--- a/fastlane/metadata/en-US/promotional_text.txt
+++ b/fastlane/metadata/en-US/promotional_text.txt
@@ -1 +1 @@
-NEW in 1.0.6: Priority dishes, day-based tag rules, tap to replace meals, and export your week even when it's not complete.
+NEW in 1.1.0: Premium is now a monthly subscription. Existing lifetime purchasers keep full access automatically.
diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt
index 1915f93..b8c03a9 100644
--- a/fastlane/metadata/en-US/release_notes.txt
+++ b/fastlane/metadata/en-US/release_notes.txt
@@ -1,8 +1,4 @@
-New in 1.0.6:
+New in 1.1.0:
-• Priority dishes — star a dish to make auto-assign pick it first.
-• Day rules — tag restrictions now support "weekdays only" or "weekend only".
-• Auto-assign button — now prominently shown whenever there are empty slots.
-• Tap to replace — tap any filled slot to swap the dish directly.
-• Export anytime — share your week even if it's not complete yet; empty slots show "TBD".
-• Sunday reminder — new notification to plan next week before Monday.
+• Premium is now a monthly subscription — unlimited dishes, planning, and export for $2.99/month.
+• Existing lifetime purchasers keep full access automatically, no action needed.
diff --git a/fastlane/metadata/es-ES/promotional_text.txt b/fastlane/metadata/es-ES/promotional_text.txt
index 1af5ef6..3587945 100644
--- a/fastlane/metadata/es-ES/promotional_text.txt
+++ b/fastlane/metadata/es-ES/promotional_text.txt
@@ -1 +1 @@
-NUEVO en 1.0.6: Platos prioritarios, reglas por días, toca para reemplazar y exporta tu semana aunque no esté completa.
+NUEVO en 1.1.0: Premium ahora es una suscripción mensual. Los compradores previos conservan su acceso completo automáticamente.
diff --git a/fastlane/metadata/es-ES/release_notes.txt b/fastlane/metadata/es-ES/release_notes.txt
index 5a2064b..ee2e2cb 100644
--- a/fastlane/metadata/es-ES/release_notes.txt
+++ b/fastlane/metadata/es-ES/release_notes.txt
@@ -1,8 +1,4 @@
-Novedades en 1.0.6:
+Novedades en 1.1.0:
-• Platos prioritarios — marca un plato con estrella para que el auto-completar lo elija primero.
-• Reglas de días — las restricciones de etiquetas ahora admiten "solo entre semana" o "solo fin de semana".
-• Botón auto-rellenar — ahora visible directamente cuando hay huecos vacíos.
-• Toca para reemplazar — toca un hueco relleno para cambiar el plato directamente.
-• Exportar en cualquier momento — comparte tu semana aunque no esté completa; los huecos vacíos muestran "Por definir".
-• Recordatorio dominical — nueva notificación para planificar la semana antes del lunes.
+• Premium ahora es una suscripción mensual — platos ilimitados, planificación y exportación por 2,99 €/mes.
+• Los usuarios con compra única previa conservan el acceso completo de forma automática.
diff --git a/fastlane/metadata/fr-FR/promotional_text.txt b/fastlane/metadata/fr-FR/promotional_text.txt
index aa9a934..78db6b4 100644
--- a/fastlane/metadata/fr-FR/promotional_text.txt
+++ b/fastlane/metadata/fr-FR/promotional_text.txt
@@ -1 +1 @@
-NOUVEAU v1.0.6 : Plats prioritaires, règles par jour, remplacer en un tap, et exportez même une semaine incomplète.
+NOUVEAU v1.1.0 : Premium est désormais un abonnement mensuel. Les acheteurs existants conservent automatiquement leur accès complet.
diff --git a/fastlane/metadata/fr-FR/release_notes.txt b/fastlane/metadata/fr-FR/release_notes.txt
index 49b9408..f7b4df4 100644
--- a/fastlane/metadata/fr-FR/release_notes.txt
+++ b/fastlane/metadata/fr-FR/release_notes.txt
@@ -1,8 +1,4 @@
-Nouveautés dans la version 1.0.6 :
+Nouveautés dans la version 1.1.0 :
-• Plats prioritaires — mettez une étoile à un plat pour que l'auto-complétion le choisisse en premier.
-• Règles de jours — les restrictions d'étiquettes prennent désormais en charge "jours de semaine" ou "week-end".
-• Bouton auto-remplir — désormais visible directement lorsqu'il y a des créneaux vides.
-• Appuyer pour remplacer — appuyez sur un créneau rempli pour changer le plat directement.
-• Exporter à tout moment — partagez votre semaine même incomplète ; les créneaux vides affichent "À définir".
-• Rappel du dimanche — nouvelle notification pour planifier la semaine avant le lundi.
+• Premium est désormais un abonnement mensuel — plats illimités, planification et export pour 2,99 €/mois.
+• Les utilisateurs ayant effectué un achat unique conservent automatiquement leur accès complet.
diff --git a/fastlane/metadata/it/promotional_text.txt b/fastlane/metadata/it/promotional_text.txt
index 645cedb..3fb26c5 100644
--- a/fastlane/metadata/it/promotional_text.txt
+++ b/fastlane/metadata/it/promotional_text.txt
@@ -1 +1 @@
-NOVITÀ v1.0.6: Piatti prioritari, regole per giorno, sostituisci con un tap ed esporta anche settimane incomplete.
+NOVITÀ v1.1.0: Premium è ora un abbonamento mensile. Gli acquirenti precedenti mantengono automaticamente l'accesso completo.
diff --git a/fastlane/metadata/it/release_notes.txt b/fastlane/metadata/it/release_notes.txt
index c08a1b7..e35313a 100644
--- a/fastlane/metadata/it/release_notes.txt
+++ b/fastlane/metadata/it/release_notes.txt
@@ -1,8 +1,4 @@
-Novità nella versione 1.0.6:
+Novità nella versione 1.1.0:
-• Piatti prioritari — metti una stella a un piatto per farlo scegliere per primo dall'auto-completamento.
-• Regole per giorni — le restrizioni dei tag ora supportano "solo giorni feriali" o "solo weekend".
-• Pulsante auto-assegna — ora visibile direttamente quando ci sono slot vuoti.
-• Tocca per sostituire — tocca uno slot pieno per cambiare il piatto direttamente.
-• Esporta in qualsiasi momento — condividi la settimana anche se incompleta; gli slot vuoti mostrano "Da definire".
-• Promemoria domenicale — nuova notifica per pianificare la settimana prima di lunedì.
+• Premium è ora un abbonamento mensile — piatti illimitati, pianificazione ed esportazione a 2,99 €/mese.
+• Gli utenti con acquisto singolo precedente mantengono automaticamente l'accesso completo.
diff --git a/fastlane/metadata/pt-BR/promotional_text.txt b/fastlane/metadata/pt-BR/promotional_text.txt
index d818d35..d8ad78f 100644
--- a/fastlane/metadata/pt-BR/promotional_text.txt
+++ b/fastlane/metadata/pt-BR/promotional_text.txt
@@ -1 +1 @@
-NOVIDADE 1.0.6: Pratos prioritários, regras por dia, troca com toque e exporte a semana mesmo incompleta.
+NOVIDADE 1.1.0: Premium agora é uma assinatura mensal. Compradores anteriores mantêm o acesso completo automaticamente.
diff --git a/fastlane/metadata/pt-BR/release_notes.txt b/fastlane/metadata/pt-BR/release_notes.txt
index 1d7c095..91ff0db 100644
--- a/fastlane/metadata/pt-BR/release_notes.txt
+++ b/fastlane/metadata/pt-BR/release_notes.txt
@@ -1,8 +1,4 @@
-Novidades na versão 1.0.6:
+Novidades na versão 1.1.0:
-• Pratos prioritários — marque um prato com estrela para que a atribuição automática o escolha primeiro.
-• Regras de dias — restrições de etiquetas agora suportam "apenas dias úteis" ou "apenas fim de semana".
-• Botão auto-atribuir — agora visível diretamente quando há slots vazios.
-• Toque para substituir — toque em um slot preenchido para trocar o prato diretamente.
-• Exportar a qualquer hora — compartilhe sua semana mesmo incompleta; slots vazios mostram "A definir".
-• Lembrete de domingo — nova notificação para planejar a semana antes da segunda-feira.
+• Premium agora é uma assinatura mensal — pratos ilimitados, planejamento e exportação por R$ 2,99/mês.
+• Usuários com compra única anterior mantêm o acesso completo automaticamente.