Fix AdMob history banner and refine Felica history mapping

This commit is contained in:
2026-04-24 21:32:56 +07:00
parent 8f0b001501
commit 7882f77a27
6 changed files with 289 additions and 38 deletions

View File

@ -34,6 +34,10 @@ final class SettingsViewController: UIViewController {
// Ad banner
private let adContainer = UIView()
private var bannerView = GADBannerView()
private var bannerRetryWorkItem: DispatchWorkItem?
private var isBannerAdLoaded = false
private var isBannerLoadInFlight = false
private let bannerRetryDelay: TimeInterval = 20
// Dynamic constraints toggled on ad load/fail
private var generalTopNoAd: NSLayoutConstraint!
@ -59,6 +63,15 @@ final class SettingsViewController: UIViewController {
setupFooter()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadBannerAdIfNeeded(reason: "viewDidAppear")
}
deinit {
bannerRetryWorkItem?.cancel()
}
// MARK: - ScrollView
private func setupScrollView() {
@ -132,10 +145,55 @@ final class SettingsViewController: UIViewController {
bannerView.heightAnchor.constraint(equalToConstant: CGFloat(adSize.size.height))
])
bannerView.load(GADRequest())
lastBottomAnchor = adContainer.bottomAnchor
}
private func loadBannerAdIfNeeded(reason: String) {
guard !isBannerAdLoaded, !isBannerLoadInFlight else { return }
guard isViewLoaded, view.window != nil else {
logAdMob("skip load: view is not visible", reason: reason)
return
}
bannerRetryWorkItem?.cancel()
bannerView.rootViewController = self
isBannerLoadInFlight = true
logAdMob("loading banner", reason: reason)
bannerView.load(GADRequest())
}
private func scheduleBannerRetry(after delay: TimeInterval = 20) {
bannerRetryWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
self?.loadBannerAdIfNeeded(reason: "retry")
}
bannerRetryWorkItem = workItem
logAdMob("scheduling retry in \(Int(delay))s")
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
}
private func logAdMob(_ message: String, reason: String? = nil, error: Error? = nil) {
var parts = ["[AdMob][Settings]", message]
if let reason {
parts.append("reason=\(reason)")
}
parts.append("adUnitID=\(bannerView.adUnitID ?? "-")")
parts.append("visible=\(viewIfLoaded?.window != nil)")
parts.append("loaded=\(isBannerAdLoaded)")
parts.append("inFlight=\(isBannerLoadInFlight)")
parts.append("rootVC=\(String(describing: type(of: bannerView.rootViewController)))")
if let nsError = error as NSError? {
parts.append("errorDomain=\(nsError.domain)")
parts.append("errorCode=\(nsError.code)")
parts.append("error=\(nsError.localizedDescription)")
}
debugLog(parts.joined(separator: " | "))
}
// MARK: - General Section
private func setupGeneralSection() {
@ -291,6 +349,10 @@ final class SettingsViewController: UIViewController {
extension SettingsViewController: GADBannerViewDelegate {
func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
bannerRetryWorkItem?.cancel()
isBannerLoadInFlight = false
isBannerAdLoaded = true
logAdMob("banner loaded")
generalTopNoAd.isActive = false
generalTopWithAd.isActive = true
UIView.animate(withDuration: 0.3) {
@ -300,12 +362,16 @@ extension SettingsViewController: GADBannerViewDelegate {
}
func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
isBannerLoadInFlight = false
isBannerAdLoaded = false
logAdMob("banner failed", error: error)
generalTopWithAd.isActive = false
generalTopNoAd.isActive = true
UIView.animate(withDuration: 0.3) {
self.adContainer.isHidden = true
self.view.layoutIfNeeded()
}
scheduleBannerRetry(after: bannerRetryDelay)
}
}