Просмотр исходного кода

fix(iap): stop showing hardcoded paywall prices before StoreKit loads

Use localized Product.displayPrice only; show an em dash until products are available.
Document ASC-safe product ID rules and keeping IDs in sync across code and StoreKit config.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 недель назад: 3
Родитель
Сommit
3c71d5b75e

+ 9 - 7
App for Indeed/Controllers/PremiumPlansWindowController.swift

@@ -356,11 +356,13 @@ private final class PremiumPlansViewController: NSViewController {
356
         }
356
         }
357
     }
357
     }
358
 
358
 
359
+    /// Shown until StoreKit returns localized `Product.displayPrice` (never use hardcoded currency amounts).
360
+    private static let unloadedPricePlaceholder = "—"
361
+
359
     private struct Plan {
362
     private struct Plan {
360
         let id: String
363
         let id: String
361
         let title: String
364
         let title: String
362
         let subtitle: String
365
         let subtitle: String
363
-        let price: String
364
         let period: String
366
         let period: String
365
         let billedPill: String
367
         let billedPill: String
366
         let billedLine: String
368
         let billedLine: String
@@ -405,7 +407,6 @@ private final class PremiumPlansViewController: NSViewController {
405
             id: "weekly",
407
             id: "weekly",
406
             title: "Weekly",
408
             title: "Weekly",
407
             subtitle: "Flexible and commitment-free",
409
             subtitle: "Flexible and commitment-free",
408
-            price: "$9.99",
409
             period: "/ week",
410
             period: "/ week",
410
             billedPill: "",
411
             billedPill: "",
411
             billedLine: "",
412
             billedLine: "",
@@ -424,7 +425,6 @@ private final class PremiumPlansViewController: NSViewController {
424
             id: "monthly",
425
             id: "monthly",
425
             title: "Monthly",
426
             title: "Monthly",
426
             subtitle: "Balanced for regular productivity",
427
             subtitle: "Balanced for regular productivity",
427
-            price: "$19.99",
428
             period: "/ month",
428
             period: "/ month",
429
             billedPill: "",
429
             billedPill: "",
430
             billedLine: "",
430
             billedLine: "",
@@ -443,7 +443,6 @@ private final class PremiumPlansViewController: NSViewController {
443
             id: "yearly",
443
             id: "yearly",
444
             title: "Yearly",
444
             title: "Yearly",
445
             subtitle: "Best value for long-term users",
445
             subtitle: "Best value for long-term users",
446
-            price: "$39.99",
447
             period: "/ year",
446
             period: "/ year",
448
             billedPill: "3 days free trial",
447
             billedPill: "3 days free trial",
449
             billedLine: "",
448
             billedLine: "",
@@ -603,7 +602,7 @@ private final class PremiumPlansViewController: NSViewController {
603
         topRightTag.isHidden = plan.billedPill.isEmpty
602
         topRightTag.isHidden = plan.billedPill.isEmpty
604
         topRightTag.font = .systemFont(ofSize: 10, weight: .bold)
603
         topRightTag.font = .systemFont(ofSize: 10, weight: .bold)
605
 
604
 
606
-        let priceLabel = NSTextField(labelWithString: plan.price)
605
+        let priceLabel = NSTextField(labelWithString: Self.unloadedPricePlaceholder)
607
         priceLabel.font = .systemFont(ofSize: 18, weight: .semibold)
606
         priceLabel.font = .systemFont(ofSize: 18, weight: .semibold)
608
         priceLabel.textColor = Theme.primaryText
607
         priceLabel.textColor = Theme.primaryText
609
 
608
 
@@ -931,8 +930,11 @@ private final class PremiumPlansViewController: NSViewController {
931
 
930
 
932
     private func applyStorePricing() {
931
     private func applyStorePricing() {
933
         for plan in plans {
932
         for plan in plans {
934
-            guard let fields = planPriceFields[plan.id],
935
-                  let product = subscriptionStore.product(forPlanKey: plan.id) else { continue }
933
+            guard let fields = planPriceFields[plan.id] else { continue }
934
+            guard let product = subscriptionStore.product(forPlanKey: plan.id) else {
935
+                fields.price.stringValue = Self.unloadedPricePlaceholder
936
+                continue
937
+            }
936
             fields.price.stringValue = product.displayPrice
938
             fields.price.stringValue = product.displayPrice
937
             if let period = product.subscription?.subscriptionPeriod {
939
             if let period = product.subscription?.subscriptionPeriod {
938
                 fields.period.stringValue = periodSuffix(for: period)
940
                 fields.period.stringValue = periodSuffix(for: period)

+ 3 - 2
App for Indeed/Subscription/SubscriptionProductIDs.swift

@@ -6,8 +6,9 @@
6
 import Foundation
6
 import Foundation
7
 
7
 
8
 /// Identifiers for auto-renewable subscriptions in App Store Connect.
8
 /// Identifiers for auto-renewable subscriptions in App Store Connect.
9
-/// Local Xcode runs use `App for Indeed/ProSubscriptions.storekit` (selected in the Run scheme → Options → StoreKit Configuration).
10
-/// Create three subscriptions with these exact IDs and attach them to the same subscription group.
9
+/// IDs may only contain letters, numbers, underscores, and periods (no hyphens).
10
+/// Keep these strings identical in code, `ProSubscriptions.storekit`, and App Store Connect.
11
+/// Local Xcode runs use `App for Indeed/ProSubscriptions.storekit` (Run scheme → Options → StoreKit Configuration).
11
 enum SubscriptionProductIDs {
12
 enum SubscriptionProductIDs {
12
     static let weekly = "com.mqldev.appforindeed.pro.weekly"
13
     static let weekly = "com.mqldev.appforindeed.pro.weekly"
13
     static let monthly = "com.mqldev.appforindeed.pro.monthly"
14
     static let monthly = "com.mqldev.appforindeed.pro.monthly"