177 lines
5.0 KiB
Swift
177 lines
5.0 KiB
Swift
import Foundation
|
|
import SwiftUI
|
|
import Combine
|
|
import GoogleMobileAds
|
|
import AppTrackingTransparency
|
|
import AdSupport
|
|
|
|
@MainActor
|
|
class AdMobService: ObservableObject {
|
|
// MARK: - Published Properties
|
|
|
|
@Published var isConsentObtained = false
|
|
@Published var canShowAds = false
|
|
@Published var isLoading = false
|
|
|
|
// MARK: - Ad Unit IDs
|
|
|
|
// Test Ad Unit IDs - Replace with production IDs before release
|
|
#if DEBUG
|
|
static let bannerAdUnitID = "ca-app-pub-3940256099942544/2934735716"
|
|
#else
|
|
static let bannerAdUnitID = "ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY" // Replace with your Ad Unit ID
|
|
#endif
|
|
|
|
// MARK: - Initialization
|
|
|
|
init() {
|
|
checkConsentStatus()
|
|
}
|
|
|
|
// MARK: - Consent Management
|
|
|
|
func checkConsentStatus() {
|
|
// Check if we already have consent
|
|
let consentStatus = UserDefaults.standard.bool(forKey: "adConsentObtained")
|
|
isConsentObtained = consentStatus
|
|
canShowAds = consentStatus
|
|
}
|
|
|
|
func requestConsent() async {
|
|
if #available(iOS 14.5, *) {
|
|
let status = await ATTrackingManager.requestTrackingAuthorization()
|
|
|
|
switch status {
|
|
case .authorized:
|
|
isConsentObtained = true
|
|
canShowAds = true
|
|
case .denied, .restricted:
|
|
// Can still show non-personalized ads
|
|
isConsentObtained = true
|
|
canShowAds = true
|
|
case .notDetermined:
|
|
// Will be asked again later
|
|
break
|
|
@unknown default:
|
|
break
|
|
}
|
|
} else {
|
|
// iOS 14.4 and earlier - consent assumed
|
|
isConsentObtained = true
|
|
canShowAds = true
|
|
}
|
|
|
|
UserDefaults.standard.set(isConsentObtained, forKey: "adConsentObtained")
|
|
}
|
|
|
|
// MARK: - GDPR Consent (UMP SDK)
|
|
|
|
func requestGDPRConsent() async {
|
|
// Implement UMP SDK consent flow if targeting EU users
|
|
// This is a simplified version - full implementation requires UMP SDK
|
|
|
|
let isEUUser = isUserInEU()
|
|
|
|
if isEUUser {
|
|
// Show GDPR consent dialog
|
|
// For now, assume consent if user continues
|
|
isConsentObtained = true
|
|
canShowAds = true
|
|
} else {
|
|
isConsentObtained = true
|
|
canShowAds = true
|
|
}
|
|
|
|
UserDefaults.standard.set(isConsentObtained, forKey: "adConsentObtained")
|
|
}
|
|
|
|
private func isUserInEU() -> Bool {
|
|
let euCountries = [
|
|
"AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
|
|
"DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
|
|
"PL", "PT", "RO", "SK", "SI", "ES", "SE", "GB", "IS", "LI",
|
|
"NO", "CH"
|
|
]
|
|
|
|
let countryCode = Locale.current.region?.identifier ?? ""
|
|
return euCountries.contains(countryCode)
|
|
}
|
|
|
|
// MARK: - Analytics
|
|
|
|
func logAdImpression() {
|
|
FirebaseService.shared.logAdImpression(adType: "banner")
|
|
}
|
|
|
|
func logAdClick() {
|
|
FirebaseService.shared.logAdClick(adType: "banner")
|
|
}
|
|
}
|
|
|
|
// MARK: - Banner Ad Coordinator
|
|
|
|
class BannerAdCoordinator: NSObject, BannerViewDelegate {
|
|
weak var adMobService: AdMobService?
|
|
|
|
func bannerViewDidReceiveAd(_ bannerView: BannerView) {
|
|
print("Banner ad received")
|
|
adMobService?.logAdImpression()
|
|
}
|
|
|
|
func bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {
|
|
print("Banner ad failed to load: \(error.localizedDescription)")
|
|
}
|
|
|
|
func bannerViewDidRecordImpression(_ bannerView: BannerView) {
|
|
// Impression recorded
|
|
}
|
|
|
|
func bannerViewDidRecordClick(_ bannerView: BannerView) {
|
|
adMobService?.logAdClick()
|
|
}
|
|
|
|
func bannerViewWillPresentScreen(_ bannerView: BannerView) {
|
|
// Ad will present full screen
|
|
}
|
|
|
|
func bannerViewWillDismissScreen(_ bannerView: BannerView) {
|
|
// Ad will dismiss
|
|
}
|
|
|
|
func bannerViewDidDismissScreen(_ bannerView: BannerView) {
|
|
// Ad dismissed
|
|
}
|
|
}
|
|
|
|
// MARK: - UIKit Banner View Wrapper
|
|
|
|
struct BannerAdView: UIViewRepresentable {
|
|
@EnvironmentObject var adMobService: AdMobService
|
|
|
|
func makeUIView(context: Context) -> BannerView {
|
|
let bannerView = BannerView(adSize: AdSizeBanner)
|
|
bannerView.adUnitID = AdMobService.bannerAdUnitID
|
|
|
|
// Get root view controller
|
|
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
let rootViewController = windowScene.windows.first?.rootViewController {
|
|
bannerView.rootViewController = rootViewController
|
|
}
|
|
|
|
bannerView.delegate = context.coordinator
|
|
bannerView.load(Request())
|
|
|
|
return bannerView
|
|
}
|
|
|
|
func updateUIView(_ uiView: BannerView, context: Context) {
|
|
// Banner updates automatically
|
|
}
|
|
|
|
func makeCoordinator() -> BannerAdCoordinator {
|
|
let coordinator = BannerAdCoordinator()
|
|
coordinator.adMobService = adMobService
|
|
return coordinator
|
|
}
|
|
}
|