refactor(ads): Yandex 광고 코드와 SDK 의존성을 제거한다
This commit is contained in:
@@ -1,316 +0,0 @@
|
||||
//
|
||||
// YandexAdSupport.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by OpenCode on 2026/04/28.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import YandexMobileAds
|
||||
|
||||
enum YandexBannerPlacement {
|
||||
case liveTab
|
||||
case liveDetail
|
||||
case creatorCommunityAll
|
||||
case seriesMainHome
|
||||
case seriesMainDayOfWeek
|
||||
case seriesMainByGenre
|
||||
case pushNotificationList
|
||||
case notificationReceiveSettings
|
||||
case chatCharacterList
|
||||
case chatOriginalTabTop
|
||||
case chatTalkTabTop
|
||||
}
|
||||
|
||||
enum YandexRewardedPlacement {
|
||||
case chatRoomQuota
|
||||
}
|
||||
|
||||
enum YandexAdUnitIdProvider {
|
||||
|
||||
static func banner(for placement: YandexBannerPlacement) -> String {
|
||||
switch placement {
|
||||
case .liveTab:
|
||||
YANDEX_LIVE_TAB_BANNER_AD_UNIT_ID
|
||||
case .liveDetail:
|
||||
YANDEX_LIVE_DETAIL_BANNER_AD_UNIT_ID
|
||||
case .creatorCommunityAll:
|
||||
YANDEX_CREATOR_COMMUNITY_ALL_BANNER_AD_UNIT_ID
|
||||
case .seriesMainHome:
|
||||
YANDEX_SERIES_MAIN_HOME_BANNER_AD_UNIT_ID
|
||||
case .seriesMainDayOfWeek:
|
||||
YANDEX_SERIES_MAIN_DAY_OF_WEEK_BANNER_AD_UNIT_ID
|
||||
case .seriesMainByGenre:
|
||||
YANDEX_SERIES_MAIN_BY_GENRE_BANNER_AD_UNIT_ID
|
||||
case .pushNotificationList:
|
||||
YANDEX_PUSH_NOTIFICATION_LIST_BANNER_AD_UNIT_ID
|
||||
case .notificationReceiveSettings:
|
||||
YANDEX_NOTIFICATION_RECEIVE_SETTINGS_BANNER_AD_UNIT_ID
|
||||
case .chatCharacterList:
|
||||
YANDEX_CHAT_CHARACTER_LIST_BANNER_AD_UNIT_ID
|
||||
case .chatOriginalTabTop:
|
||||
YANDEX_CHAT_ORIGINAL_TAB_TOP_BANNER_AD_UNIT_ID
|
||||
case .chatTalkTabTop:
|
||||
YANDEX_CHAT_TALK_TAB_TOP_BANNER_AD_UNIT_ID
|
||||
}
|
||||
}
|
||||
|
||||
static func rewarded(for placement: YandexRewardedPlacement) -> String {
|
||||
switch placement {
|
||||
case .chatRoomQuota:
|
||||
YANDEX_CHAT_ROOM_QUOTA_REWARDED_AD_UNIT_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct YandexInlineBannerView: View {
|
||||
|
||||
let placement: YandexBannerPlacement
|
||||
|
||||
var maxHeight: CGFloat = 90
|
||||
var horizontalPadding: CGFloat = 13.3
|
||||
|
||||
@State private var bannerHeight: CGFloat = 0
|
||||
@State private var isLoadFailed = true
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
let width = max(proxy.size.width - (horizontalPadding * 2), 1)
|
||||
let resolvedHeight = isLoadFailed ? 0 : (bannerHeight > 0 ? bannerHeight : maxHeight)
|
||||
|
||||
YandexInlineBannerContainer(
|
||||
placement: placement,
|
||||
width: width,
|
||||
maxHeight: maxHeight,
|
||||
bannerHeight: $bannerHeight,
|
||||
isLoadFailed: $isLoadFailed
|
||||
)
|
||||
.frame(width: width, height: resolvedHeight)
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
}
|
||||
.frame(height: isLoadFailed ? 0 : (bannerHeight > 0 ? bannerHeight : maxHeight))
|
||||
}
|
||||
}
|
||||
|
||||
private struct YandexInlineBannerContainer: UIViewRepresentable {
|
||||
|
||||
let placement: YandexBannerPlacement
|
||||
|
||||
let width: CGFloat
|
||||
let maxHeight: CGFloat
|
||||
|
||||
@Binding var bannerHeight: CGFloat
|
||||
@Binding var isLoadFailed: Bool
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
context.coordinator.parent = self
|
||||
context.coordinator.configureIfNeeded(containerView: uiView)
|
||||
}
|
||||
|
||||
final class Coordinator: NSObject, BannerAdViewDelegate {
|
||||
|
||||
var parent: YandexInlineBannerContainer
|
||||
private weak var containerView: UIView?
|
||||
private var bannerAdView: BannerAdView?
|
||||
private var currentWidth: CGFloat = 0
|
||||
|
||||
init(parent: YandexInlineBannerContainer) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func configureIfNeeded(containerView: UIView) {
|
||||
self.containerView = containerView
|
||||
|
||||
let roundedWidth = parent.width.rounded(.down)
|
||||
|
||||
guard bannerAdView == nil || abs(currentWidth - roundedWidth) > 0.5 else {
|
||||
return
|
||||
}
|
||||
|
||||
currentWidth = roundedWidth
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.parent.bannerHeight = 0
|
||||
self.parent.isLoadFailed = false
|
||||
}
|
||||
|
||||
bannerAdView?.removeFromSuperview()
|
||||
|
||||
let adSize = BannerAdSize.inline(width: roundedWidth, maxHeight: parent.maxHeight)
|
||||
let bannerAdView = BannerAdView(adSize: adSize)
|
||||
bannerAdView.translatesAutoresizingMaskIntoConstraints = false
|
||||
bannerAdView.delegate = self
|
||||
|
||||
containerView.addSubview(bannerAdView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
bannerAdView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
bannerAdView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
bannerAdView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
bannerAdView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
])
|
||||
|
||||
self.bannerAdView = bannerAdView
|
||||
bannerAdView.loadAd(with: AdRequest(adUnitID: YandexAdUnitIdProvider.banner(for: parent.placement)))
|
||||
}
|
||||
|
||||
func bannerAdViewDidLoad(_ adView: BannerAdView) {
|
||||
adView.layoutIfNeeded()
|
||||
|
||||
let measuredHeight = adView.bounds.height > 0 ? adView.bounds.height : parent.maxHeight
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.parent.bannerHeight = measuredHeight
|
||||
self.parent.isLoadFailed = false
|
||||
}
|
||||
}
|
||||
|
||||
func bannerAdViewDidFailLoading(_ adView: BannerAdView, error: Error) {
|
||||
DispatchQueue.main.async {
|
||||
self.parent.bannerHeight = 0
|
||||
self.parent.isLoadFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
func bannerAdViewDidClick(_ adView: BannerAdView) {
|
||||
}
|
||||
|
||||
func bannerAdView(_ adView: BannerAdView, didTrackImpression impressionData: ImpressionData?) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class YandexRewardedAdManager: NSObject {
|
||||
|
||||
static let shared = YandexRewardedAdManager()
|
||||
|
||||
private var rewardedAd: RewardedAd?
|
||||
private var rewardedAdLoader: RewardedAdLoader?
|
||||
private var currentPlacement: YandexRewardedPlacement?
|
||||
private var pendingRewardAction: (@MainActor () -> Void)?
|
||||
private var rewardedPlacement: YandexRewardedPlacement?
|
||||
private var isLoading = false
|
||||
|
||||
func preloadAd(for placement: YandexRewardedPlacement) {
|
||||
guard !isLoading else {
|
||||
return
|
||||
}
|
||||
|
||||
if currentPlacement == placement, rewardedAd != nil {
|
||||
return
|
||||
}
|
||||
|
||||
let loader = RewardedAdLoader()
|
||||
rewardedAdLoader = loader
|
||||
rewardedAd = nil
|
||||
currentPlacement = placement
|
||||
isLoading = true
|
||||
|
||||
Task {
|
||||
do {
|
||||
let loadedAd = try await loader.loadAd(with: AdRequest(adUnitID: YandexAdUnitIdProvider.rewarded(for: placement)))
|
||||
guard currentPlacement == placement else {
|
||||
isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
loadedAd.delegate = self
|
||||
rewardedAd = loadedAd
|
||||
} catch {
|
||||
if currentPlacement == placement {
|
||||
rewardedAd = nil
|
||||
}
|
||||
}
|
||||
|
||||
if currentPlacement == placement {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showAdIfAvailable(for placement: YandexRewardedPlacement, onReward: @escaping @MainActor () -> Void) -> Bool {
|
||||
guard let presenter = presentingViewController(), let rewardedAd, currentPlacement == placement else {
|
||||
preloadAd(for: placement)
|
||||
return false
|
||||
}
|
||||
|
||||
pendingRewardAction = onReward
|
||||
rewardedPlacement = placement
|
||||
rewardedAd.show(from: presenter)
|
||||
return true
|
||||
}
|
||||
|
||||
private func completeRewardIfNeeded() {
|
||||
let action = pendingRewardAction
|
||||
pendingRewardAction = nil
|
||||
action?()
|
||||
}
|
||||
|
||||
private func resetAndPreload() {
|
||||
pendingRewardAction = nil
|
||||
rewardedAd = nil
|
||||
|
||||
if let rewardedPlacement {
|
||||
self.rewardedPlacement = nil
|
||||
preloadAd(for: rewardedPlacement)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentingViewController() -> UIViewController? {
|
||||
guard
|
||||
let rootViewController = UIApplication.shared.connectedScenes
|
||||
.compactMap({ $0 as? UIWindowScene })
|
||||
.flatMap({ $0.windows })
|
||||
.first(where: { $0.isKeyWindow })?
|
||||
.rootViewController
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var topViewController = rootViewController
|
||||
|
||||
while let presentedViewController = topViewController.presentedViewController {
|
||||
topViewController = presentedViewController
|
||||
}
|
||||
|
||||
return topViewController
|
||||
}
|
||||
}
|
||||
|
||||
extension YandexRewardedAdManager: RewardedAdDelegate {
|
||||
|
||||
func rewardedAd(_ rewardedAd: RewardedAd, didReward reward: Reward) {
|
||||
DEBUG_LOG("리워드 광고 보상 받기 성공")
|
||||
completeRewardIfNeeded()
|
||||
}
|
||||
|
||||
func rewardedAd(_ rewardedAd: RewardedAd, didFailToShow error: Error) {
|
||||
DEBUG_LOG("리워드 광고 에러")
|
||||
resetAndPreload()
|
||||
}
|
||||
|
||||
func rewardedAdDidShow(_ rewardedAd: RewardedAd) {
|
||||
}
|
||||
|
||||
func rewardedAdDidDismiss(_ rewardedAd: RewardedAd) {
|
||||
DEBUG_LOG("리워드 광고 닫기")
|
||||
resetAndPreload()
|
||||
}
|
||||
|
||||
func rewardedAdDidClick(_ rewardedAd: RewardedAd) {
|
||||
}
|
||||
|
||||
func rewardedAd(_ rewardedAd: RewardedAd, didTrackImpression impressionData: ImpressionData?) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user