diff --git a/Passepartout-iOS/Base.lproj/Purchase.storyboard b/Passepartout-iOS/Base.lproj/Purchase.storyboard index ac1989bc..f969ca83 100644 --- a/Passepartout-iOS/Base.lproj/Purchase.storyboard +++ b/Passepartout-iOS/Base.lproj/Purchase.storyboard @@ -1,9 +1,9 @@ - + - + @@ -27,29 +27,36 @@ - + + - + - + - + + @@ -58,10 +65,12 @@ + - + + @@ -74,6 +83,7 @@ + diff --git a/Passepartout-iOS/Global/Product.swift b/Passepartout-iOS/Global/Product.swift index f3ff41cb..ebd7beff 100644 --- a/Passepartout-iOS/Global/Product.swift +++ b/Passepartout-iOS/Global/Product.swift @@ -97,6 +97,18 @@ enum Product: String { // MARK: All static let all: [Product] = allDonations + allFeatures + allProviders + + var isDonation: Bool { + return Product.allDonations.contains(self) + } + + var isFeature: Bool { + return Product.allFeatures.contains(self) + } + + var isProvider: Bool { + return Product.allProviders.contains(self) + } } extension Infrastructure.Name { diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift index 9ff50308..34679ec6 100644 --- a/Passepartout-iOS/Global/ProductManager.swift +++ b/Passepartout-iOS/Global/ProductManager.swift @@ -80,6 +80,21 @@ class ProductManager: NSObject { return inApp.product(withIdentifier: identifier) } + func featureProducts(includingFullVersion: Bool) -> [SKProduct] { + return inApp.products.filter { + guard let p = Product(rawValue: $0.productIdentifier) else { + return false + } + guard includingFullVersion || p != .fullVersion else { + return false + } + guard p.isFeature else { + return false + } + return true + } + } + func purchase(_ product: SKProduct, completionHandler: @escaping (InAppPurchaseResult, Error?) -> Void) { inApp.purchase(product: product) { if $0 == .success { diff --git a/Passepartout-iOS/Global/SwiftGen+Strings.swift b/Passepartout-iOS/Global/SwiftGen+Strings.swift index 1ec505c6..c3b275b4 100644 --- a/Passepartout-iOS/Global/SwiftGen+Strings.swift +++ b/Passepartout-iOS/Global/SwiftGen+Strings.swift @@ -82,6 +82,12 @@ internal enum L10n { /// Purchase internal static let title = L10n.tr("App", "purchase.title") internal enum Cells { + internal enum FullVersion { + /// \n- All providers (including those being added in the future)\n%@ + internal static func extraDescription(_ p1: String) -> String { + return L10n.tr("App", "purchase.cells.full_version.extra_description", p1) + } + } internal enum Restore { /// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again. internal static let description = L10n.tr("App", "purchase.cells.restore.description") @@ -89,6 +95,12 @@ internal enum L10n { internal static let title = L10n.tr("App", "purchase.cells.restore.title") } } + internal enum Sections { + internal enum Products { + /// Every product is a one-time purchase. + internal static let footer = L10n.tr("App", "purchase.sections.products.footer") + } + } } internal enum Service { internal enum Alerts { diff --git a/Passepartout-iOS/Global/en.lproj/App.strings b/Passepartout-iOS/Global/en.lproj/App.strings index ec407710..2c187314 100644 --- a/Passepartout-iOS/Global/en.lproj/App.strings +++ b/Passepartout-iOS/Global/en.lproj/App.strings @@ -61,5 +61,7 @@ "shortcuts.edit.cells.add_shortcut.caption" = "Add shortcut"; "purchase.title" = "Purchase"; +"purchase.sections.products.footer" = "Every product is a one-time purchase."; +"purchase.cells.full_version.extra_description" = "- All providers (including those being added in the future)\n%@"; "purchase.cells.restore.title" = "Restore purchases"; "purchase.cells.restore.description" = "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again."; diff --git a/Passepartout-iOS/Scenes/Purchase/PurchaseTableViewCell.swift b/Passepartout-iOS/Scenes/Purchase/PurchaseTableViewCell.swift index 8efbab02..a32dc6e3 100644 --- a/Passepartout-iOS/Scenes/Purchase/PurchaseTableViewCell.swift +++ b/Passepartout-iOS/Scenes/Purchase/PurchaseTableViewCell.swift @@ -29,27 +29,28 @@ import StoreKit class PurchaseTableViewCell: UITableViewCell { @IBOutlet private weak var labelTitle: UILabel? + @IBOutlet private weak var labelPrice: UILabel? + @IBOutlet private weak var labelDescription: UILabel? override func awakeFromNib() { super.awakeFromNib() labelTitle?.applyAccent(.current) + labelPrice?.applyAccent(.current) } - func fill(product: SKProduct) { - var title = product.localizedTitle - if let price = product.localizedPrice { - title += " @ \(price)" - } + func fill(product: SKProduct, customDescription: String? = nil) { fill( - title: title, - description: "\(product.localizedDescription)." + title: product.localizedTitle, + description: customDescription ?? "\(product.localizedDescription)." ) + labelPrice?.text = product.localizedPrice } func fill(title: String, description: String) { labelTitle?.text = title labelDescription?.text = description + labelPrice?.text = nil } } diff --git a/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift b/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift index d8acb4eb..43ce7e22 100644 --- a/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift +++ b/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift @@ -38,6 +38,8 @@ class PurchaseViewController: UITableViewController, StrongTableHost { private var skFeature: SKProduct? private var skFullVersion: SKProduct? + + private var fullVersionExtra: String? // MARK: StrongTableHost @@ -46,19 +48,26 @@ class PurchaseViewController: UITableViewController, StrongTableHost { func reloadModel() { model.clear() model.add(.products) - + model.setFooter(L10n.App.Purchase.Sections.Products.footer, forSection: .products) + var rows: [RowType] = [] let pm = ProductManager.shared - if let skFeature = pm.product(withIdentifier: feature) { - self.skFeature = skFeature - rows.append(.feature) - } if let skFullVersion = pm.product(withIdentifier: .fullVersion) { self.skFullVersion = skFullVersion rows.append(.fullVersion) } + if let skFeature = pm.product(withIdentifier: feature) { + self.skFeature = skFeature + rows.append(.feature) + } rows.append(.restore) model.set(rows, forSection: .products) + + let featureBulletsList: [String] = ProductManager.shared.featureProducts(includingFullVersion: false).map { + return "- \($0.localizedTitle)" + }.sortedCaseInsensitive() + let featureBullets = featureBulletsList.joined(separator: "\n") + fullVersionExtra = L10n.App.Purchase.Cells.FullVersion.extraDescription(featureBullets) } // MARK: UIViewController @@ -156,6 +165,14 @@ extension PurchaseViewController { case restore } + override func numberOfSections(in tableView: UITableView) -> Int { + return model.numberOfSections + } + + override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + return model.footer(forSection: section) + } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard !isLoading else { return 0 @@ -176,7 +193,7 @@ extension PurchaseViewController { guard let product = skFullVersion else { fatalError("Loaded full version cell, yet no corresponding product?") } - cell.fill(product: product) + cell.fill(product: product, customDescription: fullVersionExtra) case .restore: cell.fill(