From f63a9b44fc866eb2195c087a9453432df08e4e04 Mon Sep 17 00:00:00 2001 From: Andrea Busi Date: Thu, 30 Jul 2020 07:24:48 +0200 Subject: [PATCH] refactor: Recreate controllers for subscriptions --- .../project.pbxproj | 28 + .../InApp/EQNBaseTableViewCell.swift | 25 + .../SubscriptionDetailViewController.swift | 120 +++ .../SubscriptionProductTableViewCell.swift | 50 ++ .../SubscriptionsActiveTableViewCell.swift | 41 ++ .../SubscriptionsHeaderTableViewCell.swift | 19 + .../InApp/SubscriptionsViewController.swift | 226 ++++++ .../Earthquake Network-Bridging-Header.h | 1 + .../Models/EQNPurchaseAvailability.swift | 32 + .../Models/EQNPurchaseUtility.swift | 20 + .../Earthquake Network/Models/IAPHelper.swift | 2 + .../Models/VersioneProProducts.swift | 25 + .../Storyboards/Base.lproj/Main.storyboard | 682 ++++++++---------- .../Storyboards/es.lproj/Main.strings | 10 + .../Storyboards/it.lproj/Main.strings | 10 + .../en.lproj/Localizable.strings | 12 +- .../es.lproj/Localizable.strings | 10 +- .../it.lproj/Localizable.strings | 10 +- Sources/Earthquake Network/model/EQNManager.h | 16 +- .../model/EQNReteSmartphone.h | 1 + .../model/EQNReteSmartphone.m | 7 +- 21 files changed, 955 insertions(+), 392 deletions(-) create mode 100644 Sources/Earthquake Network/Controllers/InApp/EQNBaseTableViewCell.swift create mode 100644 Sources/Earthquake Network/Controllers/InApp/SubscriptionDetailViewController.swift create mode 100644 Sources/Earthquake Network/Controllers/InApp/SubscriptionProductTableViewCell.swift create mode 100644 Sources/Earthquake Network/Controllers/InApp/SubscriptionsActiveTableViewCell.swift create mode 100644 Sources/Earthquake Network/Controllers/InApp/SubscriptionsHeaderTableViewCell.swift create mode 100644 Sources/Earthquake Network/Controllers/InApp/SubscriptionsViewController.swift create mode 100644 Sources/Earthquake Network/Models/EQNPurchaseAvailability.swift diff --git a/Sources/Earthquake Network.xcodeproj/project.pbxproj b/Sources/Earthquake Network.xcodeproj/project.pbxproj index 78d30b0..d7a4678 100644 --- a/Sources/Earthquake Network.xcodeproj/project.pbxproj +++ b/Sources/Earthquake Network.xcodeproj/project.pbxproj @@ -126,13 +126,20 @@ 8CFA6326219A41590099EB0E /* DettagliTsunamiViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CFA6325219A41590099EB0E /* DettagliTsunamiViewController.m */; }; C89115902FEA7A0A31514912 /* Pods_Earthquake_Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25A8BFFE29D46740E8A8A7A3 /* Pods_Earthquake_Network.framework */; }; DC3ADD3924CB2F3D00737919 /* alert_star_trek.wav in Resources */ = {isa = PBXBuildFile; fileRef = 8CF12CC721DE43A400613AC5 /* alert_star_trek.wav */; }; + DC3BA11124D1A9C90062EE7F /* SubscriptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3BA11024D1A9C90062EE7F /* SubscriptionsViewController.swift */; }; DCAB01E324CEBFE800E8B54C /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB01E224CEBFE800E8B54C /* MenuViewController.swift */; }; DCAB01E524CEC12E00E8B54C /* MenuHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB01E424CEC12E00E8B54C /* MenuHeaderTableViewCell.swift */; }; DCAB01E724CEC22100E8B54C /* MenuItemTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB01E624CEC22100E8B54C /* MenuItemTableViewCell.swift */; }; DCB6FBEC24D0B40600ED23B8 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DCB6FBEB24D0B40600ED23B8 /* Colors.xcassets */; }; + DCBB267A24D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */; }; + DCBB267C24D1E98300F04559 /* EQNBaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267B24D1E98300F04559 /* EQNBaseTableViewCell.swift */; }; + DCBB267E24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */; }; + DCBB268024D1ECE200F04559 /* SubscriptionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */; }; + DCC23DEC24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */; }; DCC23DEF24D28F58003A2404 /* EQNEdgeInsetLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC23DEE24D28F58003A2404 /* EQNEdgeInsetLabel.swift */; }; DCD3E3C024D15576007C78D4 /* PurchaseProVersionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD3E3BF24D15576007C78D4 /* PurchaseProVersionViewController.swift */; }; DCF10DC624D2B8C7009F34C3 /* EQNPurchaseUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF10DC524D2B8C7009F34C3 /* EQNPurchaseUtility.swift */; }; + DCF10DCD24D2C935009F34C3 /* EQNPurchaseAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF10DCC24D2C935009F34C3 /* EQNPurchaseAvailability.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -353,14 +360,21 @@ 8CFA6324219A41590099EB0E /* DettagliTsunamiViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DettagliTsunamiViewController.h; sourceTree = ""; }; 8CFA6325219A41590099EB0E /* DettagliTsunamiViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DettagliTsunamiViewController.m; sourceTree = ""; }; C4FB0D7EEA34F8222369E1BB /* Pods-Earthquake Network.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Earthquake Network.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Earthquake Network/Pods-Earthquake Network.debug.xcconfig"; sourceTree = ""; }; + DC3BA11024D1A9C90062EE7F /* SubscriptionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsViewController.swift; sourceTree = ""; }; DC414C0024CDA09A008D9AE4 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; DCAB01E224CEBFE800E8B54C /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; DCAB01E424CEC12E00E8B54C /* MenuHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuHeaderTableViewCell.swift; sourceTree = ""; }; DCAB01E624CEC22100E8B54C /* MenuItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemTableViewCell.swift; sourceTree = ""; }; DCB6FBEB24D0B40600ED23B8 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsHeaderTableViewCell.swift; sourceTree = ""; }; + DCBB267B24D1E98300F04559 /* EQNBaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseTableViewCell.swift; sourceTree = ""; }; + DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionProductTableViewCell.swift; sourceTree = ""; }; + DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDetailViewController.swift; sourceTree = ""; }; + DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsActiveTableViewCell.swift; sourceTree = ""; }; DCC23DEE24D28F58003A2404 /* EQNEdgeInsetLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNEdgeInsetLabel.swift; sourceTree = ""; }; DCD3E3BF24D15576007C78D4 /* PurchaseProVersionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseProVersionViewController.swift; sourceTree = ""; }; DCF10DC524D2B8C7009F34C3 /* EQNPurchaseUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNPurchaseUtility.swift; sourceTree = ""; }; + DCF10DCC24D2C935009F34C3 /* EQNPurchaseAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNPurchaseAvailability.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -770,6 +784,7 @@ 8C483CBB21FDACE500259FD2 /* VersioneProProducts.swift */, 8C483CB721FDACD300259FD2 /* IAPHelper.swift */, DCF10DC524D2B8C7009F34C3 /* EQNPurchaseUtility.swift */, + DCF10DCC24D2C935009F34C3 /* EQNPurchaseAvailability.swift */, ); path = Models; sourceTree = ""; @@ -795,6 +810,12 @@ isa = PBXGroup; children = ( DCD3E3BF24D15576007C78D4 /* PurchaseProVersionViewController.swift */, + DC3BA11024D1A9C90062EE7F /* SubscriptionsViewController.swift */, + DCBB267B24D1E98300F04559 /* EQNBaseTableViewCell.swift */, + DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */, + DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */, + DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */, + DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */, ); path = InApp; sourceTree = ""; @@ -1063,6 +1084,8 @@ 8CCE165821EB1E0000173CD9 /* RetiSismicheTableViewController.m in Sources */, 8CCE165521EA378800173CD9 /* SegnalazioniUtentiTableViewController.m in Sources */, 8C483CC021FDACEE00259FD2 /* DetailViewController.swift in Sources */, + DCBB268024D1ECE200F04559 /* SubscriptionDetailViewController.swift in Sources */, + DCBB267E24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift in Sources */, 8CFA6323219A2C610099EB0E /* Tsunami.m in Sources */, 8CCE164E21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m in Sources */, 8C4E343F215012FA008B0D2A /* EQNManager.m in Sources */, @@ -1089,11 +1112,14 @@ 8CF66059214C566B009F4314 /* Reachability.m in Sources */, 8C593E8A217BA2470008B260 /* EQNSegnalazione.m in Sources */, 8C14112E21ED2FA300A59729 /* AreaInteresseTableViewController.m in Sources */, + DCBB267C24D1E98300F04559 /* EQNBaseTableViewCell.swift in Sources */, 8C29EAFB2258A4DD00FD90A9 /* DettagliAbbonamentoViewController.swift in Sources */, 8CBD3DCA2149B9AD0070C963 /* ReteSmartphone.m in Sources */, + DCF10DCD24D2C935009F34C3 /* EQNPurchaseAvailability.swift in Sources */, 8C13E84F220B89360009CFE4 /* ElencoFiltroEntiTableViewController.m in Sources */, 8CFA6326219A41590099EB0E /* DettagliTsunamiViewController.m in Sources */, 8CAFD7C521825E4A00F8BD29 /* EQNSisma.m in Sources */, + DCC23DEC24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift in Sources */, 8CF6604F214C0E58009F4314 /* EQNCalibrazione.m in Sources */, DCD3E3C024D15576007C78D4 /* PurchaseProVersionViewController.swift in Sources */, 8C2B25142193927E00E0E25E /* ReteSismiDettagliMappa.m in Sources */, @@ -1107,6 +1133,7 @@ 8C5EA23A2177B3ED002DC156 /* MasterViewController.m in Sources */, 8C14113121ED3E5B00A59729 /* AllertaSismiTableViewController.m in Sources */, 8CCE164B21E7BAB200173CD9 /* EQNNotificheTempoReale.m in Sources */, + DCBB267A24D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift in Sources */, 8CEAE4D92163E23E001A42B9 /* SWRevealViewController.m in Sources */, 8CF4F4D2216D2C780057110B /* EQNReteSmartphone.m in Sources */, 8CBD3DD82149B9AD0070C963 /* main.m in Sources */, @@ -1130,6 +1157,7 @@ 8C8EBBA721540039002784BA /* EQNUser.m in Sources */, 8CADAA9421B2627D0044E256 /* LogViewController.m in Sources */, 8CCE166421EBEFBD00173CD9 /* MessaggioInformativoTableViewController.m in Sources */, + DC3BA11124D1A9C90062EE7F /* SubscriptionsViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/Earthquake Network/Controllers/InApp/EQNBaseTableViewCell.swift b/Sources/Earthquake Network/Controllers/InApp/EQNBaseTableViewCell.swift new file mode 100644 index 0000000..39b3be9 --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/EQNBaseTableViewCell.swift @@ -0,0 +1,25 @@ +// +// EQNBaseTableViewCell.swift +// Earthquake Network +// +// Created by Busi Andrea on 29/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import Foundation + + +class EQNBaseTableViewCell: UITableViewCell { + override var frame: CGRect { + get { + return super.frame + } + set (newFrame) { + let inset: CGFloat = 15 + var frame = newFrame + frame.origin.x += inset + frame.size.width -= 2 * inset + super.frame = frame + } + } +} diff --git a/Sources/Earthquake Network/Controllers/InApp/SubscriptionDetailViewController.swift b/Sources/Earthquake Network/Controllers/InApp/SubscriptionDetailViewController.swift new file mode 100644 index 0000000..d402221 --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/SubscriptionDetailViewController.swift @@ -0,0 +1,120 @@ +// +// SubscriptionDetailViewController.swift +// Earthquake Network +// +// Created by Busi Andrea on 29/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import UIKit +import SafariServices + + +class SubscriptionDetailViewController: UIViewController { + + var product: SKProduct? { + didSet { + updateUI() + } + } + + @IBOutlet private weak var productTitleLabel: UILabel! + @IBOutlet private weak var productImageView: UIImageView! + @IBOutlet private weak var productDescriptionLabel: UILabel! + @IBOutlet private weak var subscriptionDetailsLabel: UILabel! + @IBOutlet private weak var openPrivacyButton: UIButton! + @IBOutlet private weak var openTermsButton: UIButton! + @IBOutlet private weak var purchaseRecapLabel: UILabel! + @IBOutlet private weak var productPriceLabel: UILabel! + @IBOutlet private weak var purchaseButton: UIButton! + + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + + NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)), + name: .IAPHelperPurchaseNotification, + object: nil) + + updateUI() + } + + + // MARK: - Private + + private func updateUI() { + guard let product = product, isViewLoaded else { return } + + productTitleLabel.text = product.localizedTitle + productDescriptionLabel.text = product.localizedDescription + + var purchaseRecapString = "" + switch product.productIdentifier { + + case VersioneProProducts.Identifier.Subscription10kMonthly: + productImageView.image = UIImage.init(named: "top_10k") + purchaseRecapString = "pagerai al mese:" + + case VersioneProProducts.Identifier.Subscription100kMonthly: + productImageView.image = UIImage.init(named: "top_100k") + purchaseRecapString = "pagerai al mese:" + + + case VersioneProProducts.Identifier.Subscription100kYearly, VersioneProProducts.Identifier.Subscription100kYearlyDiscounted: + productImageView.image = UIImage.init(named: "top_100k") + purchaseRecapString = "pagerai all'anno:" + + case VersioneProProducts.Identifier.Subscription10kYearly, VersioneProProducts.Identifier.Subscription10kYearlyDiscounted: + productImageView.image = UIImage.init(named: "top_10k") + purchaseRecapString = "pagerai all'anno:" + + default: + break + } + + purchaseRecapLabel.text = "\(product.localizedDescription), \(NSLocalizedString(purchaseRecapString, comment: ""))" + + priceFormatter.locale = product.priceLocale + productPriceLabel.text = priceFormatter.string(from: product.price) + } + + // MARK: - Notifications + + @objc func handlePurchaseNotification(_ notification: Notification) { + navigationController?.popViewController(animated: true) + } + + // MARK: - Actions + + @IBAction func openExternalLinkTapped(_ sender: UIButton) { + var linkUrl: URL? + if sender == openPrivacyButton { + linkUrl = URL(string: "\(EQNWebsiteAddress)/privacy/") + } else if sender == openTermsButton { + linkUrl = URL(string: "\(EQNWebsiteAddress)/terms-conditions/") + + } + + if let url = linkUrl { + let controller = SFSafariViewController(url: url) + present(controller, animated: true, completion: nil) + } + } + + @IBAction func subscribeTapped(_ sender: UIButton) { + guard let product = product else { return } + + VersioneProProducts.store.buyProduct(product) + } + + // MARK: - Helper + + private var priceFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.formatterBehavior = .behavior10_4 + formatter.numberStyle = .currency + return formatter + }() +} diff --git a/Sources/Earthquake Network/Controllers/InApp/SubscriptionProductTableViewCell.swift b/Sources/Earthquake Network/Controllers/InApp/SubscriptionProductTableViewCell.swift new file mode 100644 index 0000000..62f3c32 --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/SubscriptionProductTableViewCell.swift @@ -0,0 +1,50 @@ +// +// SubscriptionProductTableViewCell.swift +// Earthquake Network +// +// Created by Busi Andrea on 29/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import UIKit + +class SubscriptionProductTableViewCell: EQNBaseTableViewCell { + + var product: SKProduct? { + didSet { + updateUI() + } + } + var availability: EQNPurchaseAvailability? { + didSet { + updateUI() + } + } + + @IBOutlet private weak var productImageView: UIImageView! + @IBOutlet private weak var productTitleLabel: UILabel! + @IBOutlet private weak var productDescriptionLabel: UILabel? + @IBOutlet private weak var productInfoLabel: UILabel! + + + // MARK: - Private + + private func updateUI() { + guard let product = product else { return } + + productImageView.image = imageForProductIdentifier(product.productIdentifier) + productTitleLabel.text = product.localizedTitle + productDescriptionLabel?.text = product.localizedDescription + + let infoKey = is100kSubscriptionForProductIdentifier(product.productIdentifier) ? "inapp_available_100k" : "inapp_available_10k" + let counter = availability(for: product.productIdentifier) + productInfoLabel.text = String(format: NSLocalizedString(infoKey, comment: ""), counter) + } + + private func availability(for productIdentifier: String) -> Int { + if is100kSubscriptionForProductIdentifier(productIdentifier) { + return availability?.top100kAvailable ?? 0 + } + return availability?.top10kAvailable ?? 0 + } +} diff --git a/Sources/Earthquake Network/Controllers/InApp/SubscriptionsActiveTableViewCell.swift b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsActiveTableViewCell.swift new file mode 100644 index 0000000..c63dd3d --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsActiveTableViewCell.swift @@ -0,0 +1,41 @@ +// +// SubscriptionsActiveTableViewCell.swift +// Earthquake Network +// +// Created by Busi Andrea on 30/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import UIKit + +class SubscriptionsActiveTableViewCell: EQNBaseTableViewCell { + + var product: SKProduct? { + didSet { + updateUI() + } + } + + @IBOutlet private weak var noSubscriptionsLabel: UILabel! + @IBOutlet private weak var activeSubscriptionImageView: UIImageView! + + // MARK: - View Lifecycle + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + // MARK: - Private + + private func updateUI() { + if let productIdentifier = product?.productIdentifier { + noSubscriptionsLabel.isHidden = true + activeSubscriptionImageView.isHidden = false + activeSubscriptionImageView.image = imageForProductIdentifier(productIdentifier) + } else { + noSubscriptionsLabel.isHidden = false + activeSubscriptionImageView.isHidden = true + } + } +} diff --git a/Sources/Earthquake Network/Controllers/InApp/SubscriptionsHeaderTableViewCell.swift b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsHeaderTableViewCell.swift new file mode 100644 index 0000000..b36c993 --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsHeaderTableViewCell.swift @@ -0,0 +1,19 @@ +// +// SubscriptionsHeaderTableViewCell.swift +// Earthquake Network +// +// Created by Busi Andrea on 29/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import UIKit + +class SubscriptionsHeaderTableViewCell: EQNBaseTableViewCell { + + @IBOutlet weak var headerTitleLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } +} diff --git a/Sources/Earthquake Network/Controllers/InApp/SubscriptionsViewController.swift b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsViewController.swift new file mode 100644 index 0000000..8081e97 --- /dev/null +++ b/Sources/Earthquake Network/Controllers/InApp/SubscriptionsViewController.swift @@ -0,0 +1,226 @@ +// +// SubscriptionsViewController.swift +// Earthquake Network +// +// Created by Busi Andrea on 29/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import UIKit + + +class SubscriptionsViewController: UITableViewController { + + private static let SegueIdentifierSubscriptionDetail = "ShowSubscriptionDetail" + + // sezioni + private enum TableSection: CaseIterable { + case active + case description + case monthly + case yearly + + var sectionTitle: String? { + switch self { + case .monthly: return NSLocalizedString("Abbonamenti mensili", comment: "") + case .yearly: return NSLocalizedString("Abbonamenti annuali", comment: "") + default: return nil + } + } + } + + private let sections = TableSection.allCases + + private var allProducts = [SKProduct]() + private var monthlyProducts = [SKProduct]() + private var yearlyProducts = [SKProduct]() + /// Product already bought by the user + private var subscribedProduct: SKProduct? + /// Availability for subscriptions + private var availability: EQNPurchaseAvailability? + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + + addObservers() + configureUI() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + loadData() + checkAvailabilities() + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == Self.SegueIdentifierSubscriptionDetail, + let controller = segue.destination as? SubscriptionDetailViewController, + let product = sender as? SKProduct { + controller.product = product + } + } + + // MARK: - Private + + private func addObservers() { + NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)), + name: .IAPHelperPurchaseNotification, + object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(fail(_:)), + name: .IAPHelperPurchaseNotificationFail, + object: nil) + } + + private func configureUI() { + let restoreButton = UIBarButtonItem(title: NSLocalizedString("purchase_pro_restore", comment: ""), + style: .plain, + target: self, + action: #selector(restoreTapped(_:))) + navigationItem.rightBarButtonItem = restoreButton + + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 240.0; + } + + private func updateUI() { + monthlyProducts.removeAll() + yearlyProducts.removeAll() + + // creates list to show + let isDiscountAvailable = checkDiscountPrice() + allProducts.forEach { (product) in + if isDiscountAvailable { + if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kMonthly || + product.productIdentifier == VersioneProProducts.Identifier.Subscription100kMonthly { + monthlyProducts.append(product) + } else if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kYearlyDiscounted || + product.productIdentifier == VersioneProProducts.Identifier.Subscription100kYearlyDiscounted { + yearlyProducts.append(product) + } + } else { + if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kMonthly || + product.productIdentifier == VersioneProProducts.Identifier.Subscription100kMonthly { + monthlyProducts.append(product) + } + else if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kYearly || + product.productIdentifier == VersioneProProducts.Identifier.Subscription100kYearly { + yearlyProducts.append(product) + } + } + } + + tableView.reloadData() + } + + private func loadData() { + VersioneProProducts.store.requestProducts{ [weak self] success, products in + guard let self = self, let products = products, success == true else { return } + + // todo: teniamo il più "alto" + let purchased = products.filter { (product) -> Bool in + VersioneProProducts.store.isProductPurchased(product.productIdentifier) + } + self.subscribedProduct = purchased.first + + self.allProducts = products.sorted(by: { $0.productIdentifier > $1.productIdentifier }) + + self.updateUI() + } + } + + private func checkDiscountPrice() -> Bool { + let downloaded = EQNManager.default().rete_smartphone?.subscriptionsDiscounted + return downloaded ?? false + } + + private func checkAvailabilities() { + EQNPurchaseUtility.availableSubscriptions { (availability) in + DispatchQueue.main.async { + self.availability = availability + self.updateUI() + } + } + } + + // MARK: - Actions + + @objc func restoreTapped(_ sender: AnyObject) { + VersioneProProducts.store.restorePurchases() + } + + // MARK: - Notifications + + @objc func fail(_ notification: Notification){ + // todo + } + + @objc func handlePurchaseNotification(_ notification: Notification) { + loadData() + } + + // 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] + if let cell = tableView.dequeueReusableCell(withIdentifier: "SectionHeaderCell") as? SubscriptionsHeaderTableViewCell { + cell.headerTitleLabel.text = tableSection.sectionTitle + return cell + } + return nil + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + let tableSection = sections[section] + if tableSection.sectionTitle != nil { + return 50 + } + return 10 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + let tableSection = sections[section] + switch tableSection { + case .active: return 1 + case .description: return 1 + case .monthly: return monthlyProducts.count + case .yearly: return yearlyProducts.count + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let tableSection = sections[indexPath.section] + if tableSection == .active { + let cell = tableView.dequeueReusableCell(withIdentifier: "ActiveSubscriptionsCell", for: indexPath) as! SubscriptionsActiveTableViewCell + cell.product = subscribedProduct + return cell + } + if tableSection == .description { + let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) + return cell + } + + let products = tableSection == .monthly ? monthlyProducts : yearlyProducts + let cell = tableView.dequeueReusableCell(withIdentifier: "SubscriptionCell", for: indexPath) as! SubscriptionProductTableViewCell + cell.product = products[indexPath.row] + cell.availability = availability + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + let tableSection = sections[indexPath.section] + if tableSection == .monthly || tableSection == .yearly { + let products = tableSection == .monthly ? monthlyProducts : yearlyProducts + performSegue(withIdentifier: Self.SegueIdentifierSubscriptionDetail, sender: products[indexPath.row]) + } + } +} diff --git a/Sources/Earthquake Network/Earthquake Network-Bridging-Header.h b/Sources/Earthquake Network/Earthquake Network-Bridging-Header.h index 4ccf92d..316df2f 100644 --- a/Sources/Earthquake Network/Earthquake Network-Bridging-Header.h +++ b/Sources/Earthquake Network/Earthquake Network-Bridging-Header.h @@ -6,3 +6,4 @@ #import "Costanti.h" #import "EQNUser.h" #import "SWRevealViewController.h" +#import "EQNManager.h" diff --git a/Sources/Earthquake Network/Models/EQNPurchaseAvailability.swift b/Sources/Earthquake Network/Models/EQNPurchaseAvailability.swift new file mode 100644 index 0000000..fa33766 --- /dev/null +++ b/Sources/Earthquake Network/Models/EQNPurchaseAvailability.swift @@ -0,0 +1,32 @@ +// +// EQNPurchaseAvailability.swift +// Earthquake Network +// +// Created by Busi Andrea on 30/07/2020. +// Copyright © 2020 Earthquake Network. All rights reserved. +// + +import Foundation + + +struct EQNPurchaseAvailability { + var top10kAvailable: Int = 0 + var top100kAvailable: Int = 0 + + // MARK: - Init + + init(data: Data) { + guard let availabilities = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: String]] else { + return + } + + availabilities.forEach { (availiability) in + if let string100k = availiability["top_100k_available"], let int100K = Int(string100k) { + self.top100kAvailable = int100K + } + if let string10k = availiability["top_10k_available"], let int10K = Int(string10k) { + self.top10kAvailable = int10K + } + } + } +} diff --git a/Sources/Earthquake Network/Models/EQNPurchaseUtility.swift b/Sources/Earthquake Network/Models/EQNPurchaseUtility.swift index 733feb8..425a6fe 100644 --- a/Sources/Earthquake Network/Models/EQNPurchaseUtility.swift +++ b/Sources/Earthquake Network/Models/EQNPurchaseUtility.swift @@ -40,4 +40,24 @@ class EQNPurchaseUtility { completion(timeInHours) } } + + /// Returns availabilities for active subscriptions + /// - Parameter completion: Completion + static func availableSubscriptions(completion: @escaping (_ availability: EQNPurchaseAvailability?) -> Void) { + guard let url = URL(string: URL_SERVER_UTENTI_DISPONIBILI) else { + completion(nil) + return + } + + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in + guard let data = data else { + completion(nil) + return + } + + let availability = EQNPurchaseAvailability(data: data) + completion(availability) + } + task.resume() + } } diff --git a/Sources/Earthquake Network/Models/IAPHelper.swift b/Sources/Earthquake Network/Models/IAPHelper.swift index 8c9b410..cc59c5f 100644 --- a/Sources/Earthquake Network/Models/IAPHelper.swift +++ b/Sources/Earthquake Network/Models/IAPHelper.swift @@ -154,6 +154,8 @@ extension IAPHelper: SKPaymentTransactionObserver { break case .purchasing: break + @unknown default: + break } } } diff --git a/Sources/Earthquake Network/Models/VersioneProProducts.swift b/Sources/Earthquake Network/Models/VersioneProProducts.swift index dbf6254..83a7e54 100644 --- a/Sources/Earthquake Network/Models/VersioneProProducts.swift +++ b/Sources/Earthquake Network/Models/VersioneProProducts.swift @@ -55,3 +55,28 @@ public struct VersioneProProducts { func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? { return productIdentifier.components(separatedBy: ".").last } + +func imageForProductIdentifier(_ productIdentifier: String) -> UIImage? { + let products100k = [VersioneProProducts.Identifier.Subscription100kMonthly, + VersioneProProducts.Identifier.Subscription100kYearly, + VersioneProProducts.Identifier.Subscription100kYearlyDiscounted] + if products100k.contains(productIdentifier) { + return UIImage(named: "top_100k") + } + + let products10k = [VersioneProProducts.Identifier.Subscription10kMonthly, + VersioneProProducts.Identifier.Subscription10kYearly, + VersioneProProducts.Identifier.Subscription10kYearlyDiscounted] + if products10k.contains(productIdentifier) { + return UIImage(named: "top_10k") + } + + return nil +} + +func is100kSubscriptionForProductIdentifier(_ productIdentifier: String) -> Bool { + let products100k = [VersioneProProducts.Identifier.Subscription100kMonthly, + VersioneProProducts.Identifier.Subscription100kYearly, + VersioneProProducts.Identifier.Subscription100kYearlyDiscounted] + return products100k.contains(productIdentifier) +} diff --git a/Sources/Earthquake Network/Storyboards/Base.lproj/Main.storyboard b/Sources/Earthquake Network/Storyboards/Base.lproj/Main.storyboard index 85cb3e9..535c6bf 100644 --- a/Sources/Earthquake Network/Storyboards/Base.lproj/Main.storyboard +++ b/Sources/Earthquake Network/Storyboards/Base.lproj/Main.storyboard @@ -282,7 +282,154 @@ In più sostieni il progetto di ricerca il quale non riceve finanziamenti estern - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2276,389 +2423,196 @@ In più sostieni il progetto di ricerca il quale non riceve finanziamenti estern - + - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -4830,6 +4784,8 @@ In più sostieni il progetto di ricerca il quale non riceve finanziamenti estern + + diff --git a/Sources/Earthquake Network/Storyboards/es.lproj/Main.strings b/Sources/Earthquake Network/Storyboards/es.lproj/Main.strings index 477d464..5dbeaac 100644 --- a/Sources/Earthquake Network/Storyboards/es.lproj/Main.strings +++ b/Sources/Earthquake Network/Storyboards/es.lproj/Main.strings @@ -531,3 +531,13 @@ "Xkb-Nt-Fiw.normalTitle" = "Informativa privacy"; // missing "dnA-Yy-Ado.normalTitle" = "Termini e condizioni"; // missing "6z8-bz-exh.text" = "Está comprando la versión pro, el anuncios serán eliminados"; + +// Subscriptions +"c9L-RP-VQh.text" = "Suscripción activa"; +"ZXY-1S-WBT.text" = "Servicio de prioridad"; +"axa-WN-rP3.text" = "Ninguna suscripción activa"; +"ZqJ-8B-jWz.normalTitle" = "Informativa privacy"; // missing +"Lur-Tu-wib.normalTitle" = "Termini e condizioni"; // missing +"BNz-EW-lXI.text" = "Se envía una alerta a los usuarios de la app cada vez que se detecta un sismo en tiempo real. Alertar a todos los usuarios puede tomar hasta 30 segundos ya que no es técnicamente factible alertar instantáneamente a todos. Ahora puedes unirte a las primeras 10'000 o 100'000 personas alertadas en tiempo real. El orden de alerta es el siguiente: primero todos los usuarios con el servicio TOP 10K, luego todos los usuarios con el servicio TOP 100K y finalmente todos los demás usuarios. Para el mismo servicio, el orden de alerta se basa en la distancia desde el epicentro."; +"s4u-Jd-ULw.text" = "• Tu pago se cargará a tu cuenta de iTunes después de la confirmación de la compra\n\n• La suscripción se renueva automáticamente a menos que la renovación automática se desactive al menos 24 horas antes del final del período actual\n\n• Se cobrará la suscripción para la renovación dentro de las 24 horas antes del final del período actual\n\n• Las suscripciones pueden ser administradas por el usuario y la renovación automática puede desactivarse accediendo a la configuración de la cuenta del usuario después de la compra."; +"iY8-yI-DH9.normalTitle" = "SUSCRIBIR AL SERVICIO"; diff --git a/Sources/Earthquake Network/Storyboards/it.lproj/Main.strings b/Sources/Earthquake Network/Storyboards/it.lproj/Main.strings index cae5a8c..67a75e4 100644 --- a/Sources/Earthquake Network/Storyboards/it.lproj/Main.strings +++ b/Sources/Earthquake Network/Storyboards/it.lproj/Main.strings @@ -531,3 +531,13 @@ "Xkb-Nt-Fiw.normalTitle" = "Informativa privacy"; // missing "dnA-Yy-Ado.normalTitle" = "Termini e condizioni"; // missing "6z8-bz-exh.text" = "Stai acquistando la versione pro, verrà rimossa la pubblicità"; + +// Subscriptions +"c9L-RP-VQh.text" = "Servizi attivi"; +"ZXY-1S-WBT.text" = "Servizio di priorità"; +"axa-WN-rP3.text" = "Nessun servizio attivo"; +"ZqJ-8B-jWz.normalTitle" = "Informativa privacy"; // missing +"Lur-Tu-wib.normalTitle" = "Termini e condizioni"; // missing +"BNz-EW-lXI.text" = "Un'allerta è inviata agli utenti dell'app ogni volta che un sisma è rilevato in tempo reale. Allertare tutti può richiedere fino a 30 secondi in quanto non è tecnicamente fattibile farlo istantaneamente. Ora puoi entrare a far parte delle liste di priorità delle prime 10'000 o 100'000 persone allertate. L'ordine di allerta è il seguente: prima tutti gli utenti con servizio TOP 10K, successivamente tutti gli utenti con servizio TOP 100K ed infine tutti gli altri utenti. A parità di servizio, l'ordine di allerta si basa sulla distanza dall'epicentro."; +"s4u-Jd-ULw.text" = "• Il pagamento verrà addebitato sull\'account iTunes alla conferma dell\'acquisto\n\n• L\'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L\'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall\'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell\'utente dopo l\'acquisto."; +"iY8-yI-DH9.normalTitle" = "ABBONATI AL SERVIZIO"; diff --git a/Sources/Earthquake Network/en.lproj/Localizable.strings b/Sources/Earthquake Network/en.lproj/Localizable.strings index b8d4202..fc3294f 100644 --- a/Sources/Earthquake Network/en.lproj/Localizable.strings +++ b/Sources/Earthquake Network/en.lproj/Localizable.strings @@ -721,12 +721,6 @@ /* titolo pulsante notifica rete smartphone */ "VOTA L'APP" = "Vote the app"; -/* No comment provided by engineer. */ -"• Il pagamento verrà addebitato sull'account iTunes alla conferma dell'acquisto\n\n• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto." = "• Your payment will be charged to your iTunes account upon confirmation of purchase\n\n• The subscription is automatically renewed unless the automatic renewal is deactivated at least 24 hours before the end of the current period\n\n• Subscription will be charged for renewal within 24 hours before the end of the current period and the renewal cost identified\n\n• Subscriptions can be managed by the user and automatic renewal can be deactivated by accessing the user's account settings after the purchase."; - -/* No comment provided by engineer. */ -"• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto.\n\n" = "• The subscription is automatically renewed unless the automatic renewal is deactivated at least 24 hours before the end of the current period\n\n• The subscription will be charged for renewal within 24 hours before the end of the current period and the renewal cost identified\n\n• Subscriptions can be managed by the user and automatic renewal can be deactivated by accessing the user's account settings after purchase."; - "Globale" = "Global"; "Italia" = "Italy"; "Spagna" = "Spain"; @@ -791,3 +785,9 @@ "purchase_pro_restore" = "Restore"; "purchase_pro_restore_alert_title" = "Restore completed"; "purchase_pro_restore_alert_message" = "You have restored the product you purchased"; + +"inapp_available_10k" = "Top 10K: %lu subscriptions still available to be alerted in less than 1 second since the detection of the quake"; +"inapp_available_100k" = "Top 100K: %lu subscriptions still available to be alerted in less than 1 second since the detection of the quake"; + + +"• Il pagamento verrà addebitato sull'account iTunes alla conferma dell'acquisto\n\n• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto." = ""; diff --git a/Sources/Earthquake Network/es.lproj/Localizable.strings b/Sources/Earthquake Network/es.lproj/Localizable.strings index 319e802..024ac94 100644 --- a/Sources/Earthquake Network/es.lproj/Localizable.strings +++ b/Sources/Earthquake Network/es.lproj/Localizable.strings @@ -716,13 +716,6 @@ /* titolo pulsante notifica rete smartphone */ "VOTA L'APP" = "Vota la app"; -/* No comment provided by engineer. */ -"• Il pagamento verrà addebitato sull'account iTunes alla conferma dell'acquisto\n\n• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto." = "• Tu pago se cargará a tu cuenta de iTunes después de la confirmación de la compra\n\n• La suscripción se renueva automáticamente a menos que la renovación automática se desactive al menos 24 horas antes del final del período actual\n\n• Se cobrará la suscripción para la renovación dentro de las 24 horas antes del final del período actual\n\n• Las suscripciones pueden ser administradas por el usuario y la renovación automática puede desactivarse accediendo a la configuración de la cuenta del usuario después de la compra."; - -/* No comment provided by engineer. */ -"• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto.\n\n" = "• La suscripción se renueva automáticamente a menos que la renovación automática se desactive al menos 24 horas antes del final del período actual\n\n• La suscripción se cobrará por la renovación dentro de las 24 horas anteriores al final del período actual\n\n• Las suscripciones pueden ser administradas por el usuario y la renovación automática puede desactivarse accediendo a la configuración de la cuenta del usuario después de la compra."; - - "Globale" = "Global"; "Italia" = "Italia"; "Spagna" = "España"; @@ -783,3 +776,6 @@ "purchase_pro_restore" = "Restore"; // missing "purchase_pro_restore_alert_title" = "Restore completed"; // missing "purchase_pro_restore_alert_message" = "Has restaurado el producto que compraste"; + +"inapp_available_10k" = "Top 10K: %d suscripciones aún disponibles para recibir la alerta en menos de 1 segundo a partir de la detección del sismo"; +"inapp_available_100k" = "Top 100K: %d suscripciones aún disponibles para recibir la alerta en menos de 1 segundo a partir de la detección del sismo"; diff --git a/Sources/Earthquake Network/it.lproj/Localizable.strings b/Sources/Earthquake Network/it.lproj/Localizable.strings index ea7fb81..932a49c 100644 --- a/Sources/Earthquake Network/it.lproj/Localizable.strings +++ b/Sources/Earthquake Network/it.lproj/Localizable.strings @@ -715,13 +715,6 @@ /* titolo pulsante notifica rete smartphone */ "VOTA L'APP" = "VOTA L\'APP"; -/* No comment provided by engineer. */ -"• Il pagamento verrà addebitato sull'account iTunes alla conferma dell'acquisto\n\n• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto." = "• Il pagamento verrà addebitato sull\'account iTunes alla conferma dell\'acquisto\n\n• L\'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L\'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall\'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell\'utente dopo l\'acquisto."; - -/* No comment provided by engineer. */ -"• L'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell'utente dopo l'acquisto.\n\n" = "• L\'abbonamento si rinnova automaticamente a meno che il rinnovo automatico non venga disattivato almeno 24 ore prima della fine del periodo corrente\n\n• L\'abbonamento verrà addebitato per il rinnovo entro 24 ore prima della fine del periodo corrente e identificato il costo del rinnovo\n\n• Le sottoscrizioni possono essere gestite dall\'utente e il rinnovo automatico può essere disattivato accedendo alle Impostazioni account dell\'utente dopo l\'acquisto."; - - "Globale" = "Globale"; "Italia" = "Italia"; "Spagna" = "Spagna"; @@ -781,3 +774,6 @@ "purchase_pro_restore" = "Ripristina"; "purchase_pro_restore_alert_title" = "Ripristino effettuato"; "purchase_pro_restore_alert_message" = "Hai ripristinato il prodotto da te acquistato"; + +"inapp_available_10k" = "Top 10K: %lu sottoscrizioni ancora disponibili per essere allertato in meno di 1 secondo dal rilevamento del sisma"; +"inapp_available_100k" = "Top 100K: %lu sottoscrizioni ancora disponibili per essere allertato in meno di 5 secondi dal rilevamento del sisma"; diff --git a/Sources/Earthquake Network/model/EQNManager.h b/Sources/Earthquake Network/model/EQNManager.h index 193d44e..5ec16f9 100644 --- a/Sources/Earthquake Network/model/EQNManager.h +++ b/Sources/Earthquake Network/model/EQNManager.h @@ -13,16 +13,16 @@ @interface EQNManager : NSObject @property (nonatomic, assign) BOOL isBackground; -@property (nonatomic, strong) EQNReteSmartphone *rete_smartphone; -@property (nonatomic, strong) EQNAreaCheck *area_check; -@property (nonatomic, strong) NSArray *datiGraficoUtente; -@property (nonatomic, strong) NSArray *datiPastQuakes; -@property (nonatomic, strong) NSArray *elencoSelagnazioniManuali; -@property (nonatomic, strong) NSArray *retiSismiche; -@property (nonatomic, strong) NSArray *listaTsunami; +@property (nonatomic, strong, nullable) EQNReteSmartphone *rete_smartphone; +@property (nonatomic, strong, nullable) EQNAreaCheck *area_check; +@property (nonatomic, strong, nullable) NSArray *datiGraficoUtente; +@property (nonatomic, strong, nullable) NSArray *datiPastQuakes; +@property (nonatomic, strong, nullable) NSArray *elencoSelagnazioniManuali; +@property (nonatomic, strong, nullable) NSArray *retiSismiche; +@property (nonatomic, strong, nullable) NSArray *listaTsunami; -+(EQNManager *) defaultManager; ++(nonnull EQNManager *) defaultManager; -(void)avviaManager; -(void)stopManager; -(void)controllaStatoApplicazione; diff --git a/Sources/Earthquake Network/model/EQNReteSmartphone.h b/Sources/Earthquake Network/model/EQNReteSmartphone.h index f5e7c9a..7dbe993 100644 --- a/Sources/Earthquake Network/model/EQNReteSmartphone.h +++ b/Sources/Earthquake Network/model/EQNReteSmartphone.h @@ -17,6 +17,7 @@ @property (nonatomic, strong) NSNumber *y_man; @property (nonatomic, strong) NSNumber *r_man; @property (nonatomic, strong) NSString *diff; +@property (nonatomic) BOOL subscriptionsDiscounted; -(id)initWithInfo:(NSArray *)list; diff --git a/Sources/Earthquake Network/model/EQNReteSmartphone.m b/Sources/Earthquake Network/model/EQNReteSmartphone.m index 82d617a..19c6fb8 100644 --- a/Sources/Earthquake Network/model/EQNReteSmartphone.m +++ b/Sources/Earthquake Network/model/EQNReteSmartphone.m @@ -34,9 +34,14 @@ case 5: _r_man = dict[@"r_man"]; break; - default: + case 6: _diff = dict[@"diff"]; break; + case 7: + self.subscriptionsDiscounted = [dict[@"st"] boolValue]; + break; + default: + break; } } }