Commit 4f15e58a by Dmitry Stepanets

Merge branch 'feature/IOS-288-subscription-popup' into develop

parents 26b4dba9 40069501
...@@ -192,6 +192,7 @@ ...@@ -192,6 +192,7 @@
CDD0F1E52572425200CF5017 /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; }; CDD0F1E52572425200CF5017 /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; };
CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1E72572429E00CF5017 /* AppFont.swift */; }; CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1E72572429E00CF5017 /* AppFont.swift */; };
CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */; }; CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */; };
CDD4C43A271971AC0025481A /* SubscriptionPopUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD4C439271971AC0025481A /* SubscriptionPopUpViewController.swift */; };
CDD75F0D25DE68B10099ACDB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CDD75F0F25DE68B10099ACDB /* Localizable.strings */; }; CDD75F0D25DE68B10099ACDB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CDD75F0F25DE68B10099ACDB /* Localizable.strings */; };
CDDCD50726809F6D00E089AD /* ShortsSwipeHelperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDCD50626809F6D00E089AD /* ShortsSwipeHelperView.swift */; }; CDDCD50726809F6D00E089AD /* ShortsSwipeHelperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDCD50626809F6D00E089AD /* ShortsSwipeHelperView.swift */; };
CDDCD5092680C18B00E089AD /* ShortsUnreadNudgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDCD5082680C18B00E089AD /* ShortsUnreadNudgeView.swift */; }; CDDCD5092680C18B00E089AD /* ShortsUnreadNudgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDCD5082680C18B00E089AD /* ShortsUnreadNudgeView.swift */; };
...@@ -553,6 +554,7 @@ ...@@ -553,6 +554,7 @@
CDD0F1E42572425200CF5017 /* SF-Pro.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro.ttf"; sourceTree = "<group>"; }; CDD0F1E42572425200CF5017 /* SF-Pro.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro.ttf"; sourceTree = "<group>"; };
CDD0F1E72572429E00CF5017 /* AppFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFont.swift; sourceTree = "<group>"; }; CDD0F1E72572429E00CF5017 /* AppFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFont.swift; sourceTree = "<group>"; };
CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; }; CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
CDD4C439271971AC0025481A /* SubscriptionPopUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPopUpViewController.swift; sourceTree = "<group>"; };
CDD75F0E25DE68B10099ACDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; CDD75F0E25DE68B10099ACDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
CDDCD50626809F6D00E089AD /* ShortsSwipeHelperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortsSwipeHelperView.swift; sourceTree = "<group>"; }; CDDCD50626809F6D00E089AD /* ShortsSwipeHelperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortsSwipeHelperView.swift; sourceTree = "<group>"; };
CDDCD5082680C18B00E089AD /* ShortsUnreadNudgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortsUnreadNudgeView.swift; sourceTree = "<group>"; }; CDDCD5082680C18B00E089AD /* ShortsUnreadNudgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortsUnreadNudgeView.swift; sourceTree = "<group>"; };
...@@ -1564,6 +1566,7 @@ ...@@ -1564,6 +1566,7 @@
CE6E410426EBA3B4009829AE /* Views */, CE6E410426EBA3B4009829AE /* Views */,
CE6E410226EBA3B0009829AE /* SubscriptionStoreViewController.swift */, CE6E410226EBA3B0009829AE /* SubscriptionStoreViewController.swift */,
CE6E410526EBA3EB009829AE /* SubscriptionOverviewViewController.swift */, CE6E410526EBA3EB009829AE /* SubscriptionOverviewViewController.swift */,
CDD4C439271971AC0025481A /* SubscriptionPopUpViewController.swift */,
); );
path = Subscriptions; path = Subscriptions;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -2274,6 +2277,7 @@ ...@@ -2274,6 +2277,7 @@
CDC6125725E7AB1A00188DA7 /* TodayAirQualityCell.swift in Sources */, CDC6125725E7AB1A00188DA7 /* TodayAirQualityCell.swift in Sources */,
CD8B60AE263819400055CB3F /* NWSAlertViewController.swift in Sources */, CD8B60AE263819400055CB3F /* NWSAlertViewController.swift in Sources */,
CE6E410326EBA3B0009829AE /* SubscriptionStoreViewController.swift in Sources */, CE6E410326EBA3B0009829AE /* SubscriptionStoreViewController.swift in Sources */,
CDD4C43A271971AC0025481A /* SubscriptionPopUpViewController.swift in Sources */,
CD593BCF2608A50900C93428 /* ForecastHourlyCell.swift in Sources */, CD593BCF2608A50900C93428 /* ForecastHourlyCell.swift in Sources */,
CDB0D4CC2670D12F0081C773 /* TodayShortsCell.swift in Sources */, CDB0D4CC2670D12F0081C773 /* TodayShortsCell.swift in Sources */,
CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */, CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */,
......
...@@ -8,26 +8,38 @@ ...@@ -8,26 +8,38 @@
import UIKit import UIKit
import OneWeatherCore import OneWeatherCore
class SubscriptionCoordinator: Coordinator { class SubscriptionCoordinator: NSObject, Coordinator {
//Private
private let parentViewController: UIViewController private let parentViewController: UIViewController
private let storeManager: StoreManager
private let viewModel: SubscriptionViewModel
private var activeViewController: UIViewController?
//Public
public var childCoordinators = [Coordinator]() public var childCoordinators = [Coordinator]()
public var parentCoordinator: Coordinator? public var parentCoordinator: Coordinator?
private let storeManager: StoreManager
public init(parentViewController: UIViewController, storeManager: StoreManager = StoreManager.shared) { public init(parentViewController: UIViewController, storeManager: StoreManager = StoreManager.shared) {
self.parentViewController = parentViewController self.parentViewController = parentViewController
self.storeManager = storeManager self.storeManager = storeManager
self.viewModel = SubscriptionViewModel(storeManager: storeManager)
super.init()
self.viewModel.delegate = self
} }
public func start() { public func start() {
let viewModel = SubscriptionViewModel(storeManager: storeManager) activeViewController = SubscriptionStoreViewController(viewModel: self.viewModel)
let vc = SubscriptionStoreViewController(coordinator: self, viewModel: viewModel) self.parentViewController.present(activeViewController!, animated: true)
}
self.parentViewController.present(vc, animated: true) public func startPopUp() {
activeViewController = SubscriptionPopUpViewController(viewModel: self.viewModel)
self.parentViewController.present(activeViewController!, animated: true)
} }
public func openOverview() { private func openOverview() {
let overviewCoordinator = SubscriptionOverviewCoordinator(parentViewController: self.parentViewController) let overviewCoordinator = SubscriptionOverviewCoordinator(parentViewController: self.parentViewController)
overviewCoordinator.parentCoordinator = self overviewCoordinator.parentCoordinator = self
childCoordinators.append(overviewCoordinator) childCoordinators.append(overviewCoordinator)
...@@ -38,3 +50,14 @@ class SubscriptionCoordinator: Coordinator { ...@@ -38,3 +50,14 @@ class SubscriptionCoordinator: Coordinator {
parentCoordinator?.childDidFinish(child: self) parentCoordinator?.childDidFinish(child: self)
} }
} }
//MARK: Subscription View Model
extension SubscriptionCoordinator: SubscriptionViewModelDelegate {
func viewModel(_ vm: SubscriptionViewModel, finishedSubscriptionPurchaseWithResult result: Bool) {
if result {
self.activeViewController?.dismiss(animated: true, completion: {
self.openOverview()
})
}
}
}
...@@ -15,7 +15,7 @@ protocol TodayCoordinatorDelegate: AnyObject { ...@@ -15,7 +15,7 @@ protocol TodayCoordinatorDelegate: AnyObject {
class TodayCoordinator: Coordinator { class TodayCoordinator: Coordinator {
// MARK: - Private // MARK: - Private
private let navigationController = ColoredNavigationController(nibName: nil, bundle: nil) private let navigationController = ColoredNavigationController(nibName: nil, bundle: nil)
private var tabBarController:UITabBarController? private var tabBarController: UITabBarController?
var todayViewController: TodayViewController? var todayViewController: TodayViewController?
// MARK: - Public // MARK: - Public
...@@ -23,7 +23,7 @@ class TodayCoordinator: Coordinator { ...@@ -23,7 +23,7 @@ class TodayCoordinator: Coordinator {
public var childCoordinators = [Coordinator]() public var childCoordinators = [Coordinator]()
public var parentCoordinator: Coordinator? public var parentCoordinator: Coordinator?
public init(tabBarController:UITabBarController) { public init(tabBarController: UITabBarController) {
self.tabBarController = tabBarController self.tabBarController = tabBarController
} }
...@@ -65,6 +65,17 @@ class TodayCoordinator: Coordinator { ...@@ -65,6 +65,17 @@ class TodayCoordinator: Coordinator {
onboardingCoordinator.start() onboardingCoordinator.start()
} }
public func openSubscriptionStorePopUp() {
guard let parentController = self.tabBarController else {
return
}
let subscriptionCoordinator = SubscriptionCoordinator(parentViewController: parentController)
subscriptionCoordinator.parentCoordinator = self
childCoordinators.append(subscriptionCoordinator)
subscriptionCoordinator.startPopUp()
}
public func viewControllerDidEnd(controller: UIViewController) { public func viewControllerDidEnd(controller: UIViewController) {
parentCoordinator?.childDidFinish(child: self) parentCoordinator?.childDidFinish(child: self)
} }
......
...@@ -36,7 +36,7 @@ public class PushNotificationsManager: NSObject, PushNotificationsManagerProtoco ...@@ -36,7 +36,7 @@ public class PushNotificationsManager: NSObject, PushNotificationsManagerProtoco
// TODO: forced re-register on timeout // TODO: forced re-register on timeout
public func registerForRemoteNotifications() { public func registerForRemoteNotifications(completion: (() -> Void)? = nil) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { [weak self]
(granted, error) in (granted, error) in
guard let self = self else { return } guard let self = self else { return }
...@@ -49,6 +49,7 @@ public class PushNotificationsManager: NSObject, PushNotificationsManagerProtoco ...@@ -49,6 +49,7 @@ public class PushNotificationsManager: NSObject, PushNotificationsManagerProtoco
onMain { onMain {
MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self) MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self)
completion?()
} }
} }
} }
......
//
// SubscriptionPopUpViewController.swift
// 1Weather
//
// Created by Dmitry Stepanets on 15.10.2021.
//
import UIKit
import OneWeatherCore
class SubscriptionPopUpViewController: UIViewController {
private let storeViewController: SubscriptionStoreViewController
init(viewModel: SubscriptionViewModel) {
self.storeViewController = SubscriptionStoreViewController(viewModel: viewModel)
super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .overCurrentContext
modalTransitionStyle = .crossDissolve
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
prepareControler()
prepareStoreController()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
storeViewController.view.snp.updateConstraints { update in
let diffHeight = view.bounds.height - storeViewController.view.bounds.height
let offset = storeViewController.view.bounds.height + diffHeight / 2
update.top.equalTo(view.snp.bottom).offset(-offset)
}
UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
}
Settings.shared.initialSubscriptionShowed = true
}
}
private extension SubscriptionPopUpViewController {
func prepareControler() {
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}
func prepareStoreController() {
storeViewController.view.clipsToBounds = true
storeViewController.view.layer.cornerRadius = 12
view.addSubview(storeViewController.view)
addChild(storeViewController)
storeViewController.didMove(toParent: self)
//Constraints
storeViewController.view.snp.makeConstraints { make in
make.width.equalToSuperview().multipliedBy(0.87)
make.height.equalToSuperview().multipliedBy(0.9)
make.centerX.equalToSuperview()
make.top.equalTo(view.snp.bottom)
}
}
}
...@@ -15,7 +15,6 @@ import OneWeatherAnalytics ...@@ -15,7 +15,6 @@ import OneWeatherAnalytics
/// For users who previously purchased an in-app to disable ads: https://zpl.io/anzPMKr /// For users who previously purchased an in-app to disable ads: https://zpl.io/anzPMKr
/// For usual users: https://zpl.io/bLJ8rOd /// For usual users: https://zpl.io/bLJ8rOd
public class SubscriptionStoreViewController: UIViewController { public class SubscriptionStoreViewController: UIViewController {
private let coordinator: SubscriptionCoordinator
private let viewModel: SubscriptionViewModel private let viewModel: SubscriptionViewModel
private var localizationObserver: LocalizationChangeObserver! private var localizationObserver: LocalizationChangeObserver!
...@@ -25,28 +24,30 @@ public class SubscriptionStoreViewController: UIViewController { ...@@ -25,28 +24,30 @@ public class SubscriptionStoreViewController: UIViewController {
private let dynamicContentView = UIView() private let dynamicContentView = UIView()
init(coordinator: SubscriptionCoordinator, viewModel: SubscriptionViewModel) { init(viewModel: SubscriptionViewModel) {
self.coordinator = coordinator
self.viewModel = viewModel self.viewModel = viewModel
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.viewModel.delegate = self
} }
@available(*, unavailable) @available(*, unavailable)
required init?(coder: NSCoder) { return nil } required init?(coder: NSCoder) { return nil }
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.scrollView.contentSize = self.scrollViewContent.bounds.size
}
public override func viewDidLoad() { public override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
prepareViewController() prepareViewController()
prepareScrollView() prepareScrollView()
rebuildUI() rebuildUI()
} }
} }
//MARK: - UI Setup //MARK: - UI Setup
extension SubscriptionStoreViewController { private extension SubscriptionStoreViewController {
private func prepareViewController() { func prepareViewController() {
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
topHeaderView.delegate = self topHeaderView.delegate = self
localizationObserver = LocalizationChangeObserver { [weak self] in localizationObserver = LocalizationChangeObserver { [weak self] in
...@@ -54,7 +55,7 @@ extension SubscriptionStoreViewController { ...@@ -54,7 +55,7 @@ extension SubscriptionStoreViewController {
} }
} }
private func prepareScrollView() { func prepareScrollView() {
view.addSubview(scrollView) view.addSubview(scrollView)
scrollView.snp.makeConstraints { make in scrollView.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
...@@ -79,7 +80,7 @@ extension SubscriptionStoreViewController { ...@@ -79,7 +80,7 @@ extension SubscriptionStoreViewController {
} }
} }
private func rebuildUI() { func rebuildUI() {
for subview in dynamicContentView.subviews { for subview in dynamicContentView.subviews {
subview.removeFromSuperview() subview.removeFromSuperview()
} }
...@@ -159,12 +160,3 @@ extension SubscriptionStoreViewController: SubscriptionPurchaseButtonDelegate { ...@@ -159,12 +160,3 @@ extension SubscriptionStoreViewController: SubscriptionPurchaseButtonDelegate {
viewModel.purchase(subscription: product) viewModel.purchase(subscription: product)
} }
} }
extension SubscriptionStoreViewController: SubscriptionViewModelDelegate {
func viewModel(_ vm: SubscriptionViewModel, finishedSubscriptionPurchaseWithResult result: Bool) {
if result {
self.dismiss(animated: true) {
self.coordinator.openOverview()
}
}
}
}
...@@ -175,6 +175,10 @@ extension TodayViewController: TodayViewModelDelegate { ...@@ -175,6 +175,10 @@ extension TodayViewController: TodayViewModelDelegate {
coordinator.openOnboarding() coordinator.openOnboarding()
} }
func showSubscriptionPopup(viewModel: TodayViewModel) {
coordinator.openSubscriptionStorePopUp()
}
func viewModelDidChange<P>(model: P) where P : ViewModelProtocol { func viewModelDidChange<P>(model: P) where P : ViewModelProtocol {
cityButton.configure(with: viewModel.location) cityButton.configure(with: viewModel.location)
cityButton.isHidden = false cityButton.isHidden = false
......
...@@ -14,6 +14,7 @@ import AppTrackingTransparency ...@@ -14,6 +14,7 @@ import AppTrackingTransparency
protocol TodayViewModelDelegate: ViewModelDelegate { protocol TodayViewModelDelegate: ViewModelDelegate {
func showOnboarding(viewModel: TodayViewModel) func showOnboarding(viewModel: TodayViewModel)
func showPrivacyNotice(viewModel: TodayViewModel) func showPrivacyNotice(viewModel: TodayViewModel)
func showSubscriptionPopup(viewModel: TodayViewModel)
} }
class TodayViewModel: ViewModelProtocol { class TodayViewModel: ViewModelProtocol {
...@@ -100,7 +101,12 @@ class TodayViewModel: ViewModelProtocol { ...@@ -100,7 +101,12 @@ class TodayViewModel: ViewModelProtocol {
private func onboardingFlowCompleted() { private func onboardingFlowCompleted() {
self.initializeAllAdsIfNeeded() self.initializeAllAdsIfNeeded()
PushNotificationsManager.shared.registerForRemoteNotifications() PushNotificationsManager.shared.registerForRemoteNotifications(completion: { [weak self] in
guard let self = self else { return }
if !Settings.shared.initialSubscriptionShowed && CCPAHelper.shared.isNewUser {
self.delegate?.showSubscriptionPopup(viewModel: self)
}
})
analytics(log: .ANALYTICS_VIEW_TODAY) analytics(log: .ANALYTICS_VIEW_TODAY)
} }
......
...@@ -101,6 +101,9 @@ public class Settings { ...@@ -101,6 +101,9 @@ public class Settings {
@UserDefaultsBasicValue(key: "shorts_showed_swipeUp_count", userDefaults: UserDefaults.appDefaults) @UserDefaultsBasicValue(key: "shorts_showed_swipeUp_count", userDefaults: UserDefaults.appDefaults)
public var shortsSwipeUpNudgeShowedCount = 0 public var shortsSwipeUpNudgeShowedCount = 0
@UserDefaultsBasicValue(key: "initial_subscription_showed", userDefaults: UserDefaults.appDefaults)
public var initialSubscriptionShowed = false
#warning("Not implemented!") #warning("Not implemented!")
//TODO: implement store in UserDefaults and configure via UI in debug builds. //TODO: implement store in UserDefaults and configure via UI in debug builds.
public var adLogging: Bool = true public var adLogging: Bool = true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment