194 lines
7.1 KiB
Swift
Executable File
194 lines
7.1 KiB
Swift
Executable File
//
|
|
// SceneDelegate.swift
|
|
// Emoney Info
|
|
//
|
|
// Created by Wira Irawan on 22/07/24.
|
|
//
|
|
|
|
import UIKit
|
|
import AppTrackingTransparency
|
|
import GoogleMobileAds
|
|
|
|
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
|
|
var window: UIWindow?
|
|
private var hasRequestedTrackingAuthorization = false
|
|
private var isTrackingAuthorizationRequestInFlight = false
|
|
private var pendingTrackingCompletions: [() -> Void] = []
|
|
private var hasStartedAdMob = false
|
|
private weak var homeVC: HomeViewController?
|
|
|
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
|
guard let windowScene = (scene as? UIWindowScene) else { return }
|
|
|
|
let tabBar = MainTabBarController()
|
|
tabBar.viewControllers = [
|
|
makeHomeVC(tabBar: tabBar),
|
|
makeSettingsVC()
|
|
]
|
|
|
|
let window = UIWindow(windowScene: windowScene)
|
|
window.rootViewController = tabBar
|
|
window.makeKeyAndVisible()
|
|
self.window = window
|
|
}
|
|
|
|
func sceneDidDisconnect(_ scene: UIScene) {
|
|
// Called as the scene is being released by the system.
|
|
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
|
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
|
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
|
}
|
|
|
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
|
// Called when the scene has moved from an inactive state to an active state.
|
|
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
|
requestTrackingAuthorizationIfNeeded()
|
|
}
|
|
|
|
func sceneWillResignActive(_ scene: UIScene) {
|
|
// Called when the scene will move from an active state to an inactive state.
|
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
|
}
|
|
|
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
|
// Called as the scene transitions from the background to the foreground.
|
|
// Use this method to undo the changes made on entering the background.
|
|
}
|
|
|
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
|
// Called as the scene transitions from the foreground to the background.
|
|
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
|
// to restore the scene back to its current state.
|
|
}
|
|
|
|
// MARK: - VC Factories
|
|
|
|
private func makeHomeVC(tabBar: MainTabBarController) -> HomeViewController {
|
|
let vc = HomeViewController()
|
|
homeVC = vc
|
|
|
|
vc.onScanTapped = { [weak vc] in
|
|
guard let vc, let callback = vc as? ApduCallback else { return }
|
|
self.startNfcScan(callback: callback)
|
|
}
|
|
|
|
vc.onViewHistoryTapped = { [weak vc] in
|
|
let historyVC = HistoryHostingController()
|
|
historyVC.riwayatList = vc?.latestRiwayatList ?? []
|
|
historyVC.cardLabel = vc?.cardTypeText ?? ""
|
|
historyVC.balanceText = vc?.balanceText ?? ""
|
|
historyVC.cardNumber = vc?.latestCardNumber ?? ""
|
|
historyVC.modalPresentationStyle = .fullScreen
|
|
vc?.present(historyVC, animated: true)
|
|
}
|
|
|
|
vc.onSettingsTapped = { [weak tabBar] in
|
|
tabBar?.selectedIndex = 1
|
|
}
|
|
|
|
return vc
|
|
}
|
|
|
|
private func makeSettingsVC() -> UINavigationController {
|
|
let vc = SettingsViewController()
|
|
|
|
vc.onShowCardNumberChanged = { isOn in
|
|
UserDefaults.standard.set(isOn, forKey: "masked")
|
|
NotificationCenter.default.post(name: Notification.Name("refreshScreen"), object: nil)
|
|
}
|
|
|
|
vc.onHelpCenterTapped = { [weak vc] in
|
|
let faqVC = FAQViewController()
|
|
vc?.navigationController?.pushViewController(faqVC, animated: true)
|
|
}
|
|
|
|
vc.onAboutTapped = { [weak vc] in
|
|
let aboutVC = AboutViewController()
|
|
vc?.navigationController?.pushViewController(aboutVC, animated: true)
|
|
}
|
|
|
|
let nav = UINavigationController(rootViewController: vc)
|
|
nav.setNavigationBarHidden(true, animated: false)
|
|
return nav
|
|
}
|
|
|
|
private func requestTrackingAuthorizationIfNeeded(completion: (() -> Void)? = nil) {
|
|
if let completion {
|
|
pendingTrackingCompletions.append(completion)
|
|
}
|
|
|
|
guard #available(iOS 14, *) else {
|
|
startAdMobSDKIfNeeded()
|
|
flushPendingTrackingCompletions(after: 0)
|
|
return
|
|
}
|
|
|
|
guard ATTrackingManager.trackingAuthorizationStatus == .notDetermined else {
|
|
// User already responded to ATT in a previous session — start AdMob immediately.
|
|
startAdMobSDKIfNeeded()
|
|
flushPendingTrackingCompletions(after: 0)
|
|
return
|
|
}
|
|
|
|
guard !hasRequestedTrackingAuthorization else {
|
|
flushPendingTrackingCompletions(after: 0)
|
|
return
|
|
}
|
|
|
|
hasRequestedTrackingAuthorization = true
|
|
isTrackingAuthorizationRequestInFlight = true
|
|
// Short delay so the root view controller completes its appearance
|
|
// transition before iOS presents the ATT dialog.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
ATTrackingManager.requestTrackingAuthorization { _ in
|
|
DispatchQueue.main.async {
|
|
self.isTrackingAuthorizationRequestInFlight = false
|
|
// Start AdMob only after the user has responded to ATT,
|
|
// ensuring the SDK never collects data before consent is given.
|
|
self.startAdMobSDKIfNeeded()
|
|
self.flushPendingTrackingCompletions(after: 1.2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func startAdMobSDKIfNeeded() {
|
|
guard !hasStartedAdMob else { return }
|
|
hasStartedAdMob = true
|
|
// GADMobileAds.sharedInstance().requestConfiguration.testDeviceIdentifiers = ["ae454ddf2186e5ac7871bf705de41098"]
|
|
GADMobileAds.sharedInstance().start { [weak self] _ in
|
|
DispatchQueue.main.async {
|
|
self?.homeVC?.loadBannerAd()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func startNfcScan(callback: ApduCallback) {
|
|
let beginScan = {
|
|
let api = UnifiedNfcApi()
|
|
api.setCallback(apduCallback: callback)
|
|
api.searchCard()
|
|
}
|
|
|
|
if #available(iOS 14, *),
|
|
ATTrackingManager.trackingAuthorizationStatus == .notDetermined,
|
|
isTrackingAuthorizationRequestInFlight {
|
|
pendingTrackingCompletions.append(beginScan)
|
|
return
|
|
}
|
|
|
|
requestTrackingAuthorizationIfNeeded(completion: beginScan)
|
|
}
|
|
|
|
private func flushPendingTrackingCompletions(after delay: TimeInterval) {
|
|
let completions = pendingTrackingCompletions
|
|
pendingTrackingCompletions.removeAll()
|
|
guard !completions.isEmpty else { return }
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
|
|
completions.forEach { $0() }
|
|
}
|
|
}
|
|
}
|