Initial commit
This commit is contained in:
193
Emoney Info/SceneDelegate.swift
Executable file
193
Emoney Info/SceneDelegate.swift
Executable file
@ -0,0 +1,193 @@
|
||||
//
|
||||
// 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 = ["7b42e513861d6b0c9f07529b748930c0"]
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user