// // SubscriptionsViewController.swift // Earthquake Network // // Created by Busi Andrea on 29/07/2020. // Copyright © 2020 Earthquake Network. All rights reserved. // import UIKit import StoreKit import Shogun @objc class SubscriptionsViewController: UITableViewController { // sezioni private enum TableSection: CaseIterable { case active case description case products var sectionTitle: String? { switch self { case .products: NSLocalizedString("subscriptions_available", comment: "") default: nil } } } private let sections = TableSection.allCases /// All products retrieved from AppStore private var products = [EQNInAppProducts]() /// Product already bought by the user private var productSubscribed: EQNInAppProducts? /// Availability for subscriptions private var availability: EQNPurchaseAvailability? /// Tells if products are loading private var isLoading = false /// Tells if a restore is in progress private var isRestorePurchase = false // MARK: - Init @objc convenience init() { self.init(style: .plain) } // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() addObservers() configureUI() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) loadData() checkAvailabilities() } // MARK: - Private private func addObservers() { NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)), name: .EQNInAppPurchaseDidComplete, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(fail(_:)), name: .EQNInAppPurchaseDidFail, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleNoTransactionsNotification(_:)), name: .EQNInAppPurchaseNoTransactions, object: nil) } private func configureUI() { // if is presented in Simulator, add done button if navigationController?.viewControllers.first == self { let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeTapped(_:))) navigationItem.leftBarButtonItem = doneButton } navigationItem.largeTitleDisplayMode = .never tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 600.0 tableView.separatorStyle = .none tableView.backgroundColor = .systemGroupedBackground tableView.contentInset = EQNBaseContainerTableViewCell.EdgeInsets // remove extra padding on top of each section header tableView.sectionHeaderTopPadding = 0.0 tableView.registerCell(for: SubscriptionsActiveTableViewCell.self) tableView.registerCell(for: SubscriptionsDescriptionTableViewCell.self) tableView.registerCell(for: SubscriptionProductTableViewCell.self) tableView.registerHeaderFooterView(for: SubscriptionsHeaderTableViewCell.self) } private func loadData() { isLoading = true EQNInAppProducts.store.requestProducts { [weak self] success, storeProducts in self?.isLoading = false guard let self = self, let storeProducts, success == true else { return } let products = storeProducts.compactMap { EQNInAppProducts.from(product: $0) } let purchased = products .filter { (product) -> Bool in // filter for subscriptions let isPurchased = EQNInAppProducts.store.isProductPurchased(product.productIdentifier) let isSubscription = product.isSubscription return isPurchased && isSubscription }.sorted { lProduct, rProduct in // If user has more than one subscriptions, // show first the Top10k. let lIs10k = lProduct.isTop10k let rIs10k = rProduct.isTop10k // If left item is Top10k, order first if lIs10k && !rIs10k { return true } else if !lProduct.isTop10k && rProduct.isTop10k { // right product is Top10k, left no return false } else { // both products Top10k or Top100k, keep existing order return false } } self.productSubscribed = purchased.first self.products = products.sorted(by: { $0.productIdentifier > $1.productIdentifier }) self.tableView.reloadData() } } private func checkDiscountPrice() -> Bool { let downloaded = EQNManager.manager().rete_smartphone?.subscriptionsDiscounted return downloaded ?? false } private func checkAvailabilities() { EQNPurchaseUtility.availableSubscriptions { (availability) in DispatchQueue.main.async { self.availability = availability self.tableView.reloadData() } } } // MARK: - Actions @objc func closeTapped(_ sender: AnyObject) { dismiss(animated: true, completion: nil) } // MARK: - Notifications @objc func fail(_ notification: Notification){ EQNInAppProducts.store.loadPurchase() } @objc func handlePurchaseNotification(_ notification: Notification) { if isRestorePurchase { isRestorePurchase = false var product: String = "unknown" if let productIdentifier = notification.object as? String, let productName = resourceNameForProductIdentifier(productIdentifier) { product = productName } let message = "\(NSLocalizedString("purchase_pro_restore_alert_message", comment: ""))\n\n(\(product))" let alert = UIAlertController(title: NSLocalizedString("purchase_pro_restore_alert_title", comment: ""), message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "ok", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } EQNInAppProducts.store.loadPurchase() loadData() } @objc func handleNoTransactionsNotification(_ notification: Notification) { let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: NSLocalizedString("purchase_pro_no_subscriptions_alert_message", comment: ""), preferredStyle: .alert) alert.addAction(UIAlertAction(title: "ok", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { sections.count } override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let tableSection = sections[section] switch tableSection.sectionTitle { case .some(let title): let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SubscriptionsHeaderTableViewCell.self) view.update(isLoading: isLoading, title: title) return view case .none: return nil } } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { let tableSection = sections[section] return switch tableSection.sectionTitle { case .some: 50.0 case .none: 0.0 } } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let tableSection = sections[section] switch tableSection { case .active: return 1 case .description: return 1 case .products: return products.isEmpty ? 0 : 2 } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let tableSection = sections[indexPath.section] switch tableSection { case .active: let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionsActiveTableViewCell.self, for: indexPath) cell.selectionStyle = .none cell.update(with: productSubscribed) cell.onTapRestore = { [weak self] in guard let self else { return } self.isRestorePurchase = true EQNInAppProducts.store.restorePurchases() } return cell case .description: let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionsDescriptionTableViewCell.self, for: indexPath) cell.selectionStyle = .none return cell case .products: let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionProductTableViewCell.self, for: indexPath) let category: EQNInAppProducts.Category = switch indexPath.row { case 0: .top10k case 1: .top100k default: .top100k } cell.update(category: category, availability: availability) return cell } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) let products = availableProducts(for: indexPath) if !products.isEmpty { let controller = SubscriptionDetailsViewController(products: products) navigationController?.pushViewController(controller, animated: true) } } // MARK: - Helpers private func availableProducts(for indexPath: IndexPath) -> [EQNInAppProducts] { let section = sections[indexPath.section] switch (section, indexPath.row) { case (.products, 0): return products.filter { $0.isTop10k } case (.products, 1): return products.filter { $0.isTop100k } default: return [] } } }