282 lines
11 KiB
Swift
282 lines
11 KiB
Swift
//
|
|
// 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 []
|
|
}
|
|
}
|
|
}
|