fix(content): 콘텐츠 상세 광고를 제거한다
This commit is contained in:
@@ -11,7 +11,6 @@ import YandexMobileAds
|
|||||||
enum YandexBannerPlacement {
|
enum YandexBannerPlacement {
|
||||||
case liveTab
|
case liveTab
|
||||||
case liveDetail
|
case liveDetail
|
||||||
case contentDetail
|
|
||||||
case creatorCommunityAll
|
case creatorCommunityAll
|
||||||
case seriesMainHome
|
case seriesMainHome
|
||||||
case seriesMainDayOfWeek
|
case seriesMainDayOfWeek
|
||||||
@@ -23,10 +22,6 @@ enum YandexBannerPlacement {
|
|||||||
case chatTalkTabTop
|
case chatTalkTabTop
|
||||||
}
|
}
|
||||||
|
|
||||||
enum YandexInterstitialPlacement {
|
|
||||||
case contentDetail
|
|
||||||
}
|
|
||||||
|
|
||||||
enum YandexRewardedPlacement {
|
enum YandexRewardedPlacement {
|
||||||
case chatRoomQuota
|
case chatRoomQuota
|
||||||
}
|
}
|
||||||
@@ -39,8 +34,6 @@ enum YandexAdUnitIdProvider {
|
|||||||
YANDEX_LIVE_TAB_BANNER_AD_UNIT_ID
|
YANDEX_LIVE_TAB_BANNER_AD_UNIT_ID
|
||||||
case .liveDetail:
|
case .liveDetail:
|
||||||
YANDEX_LIVE_DETAIL_BANNER_AD_UNIT_ID
|
YANDEX_LIVE_DETAIL_BANNER_AD_UNIT_ID
|
||||||
case .contentDetail:
|
|
||||||
YANDEX_CONTENT_DETAIL_BANNER_AD_UNIT_ID
|
|
||||||
case .creatorCommunityAll:
|
case .creatorCommunityAll:
|
||||||
YANDEX_CREATOR_COMMUNITY_ALL_BANNER_AD_UNIT_ID
|
YANDEX_CREATOR_COMMUNITY_ALL_BANNER_AD_UNIT_ID
|
||||||
case .seriesMainHome:
|
case .seriesMainHome:
|
||||||
@@ -62,13 +55,6 @@ enum YandexAdUnitIdProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func interstitial(for placement: YandexInterstitialPlacement) -> String {
|
|
||||||
switch placement {
|
|
||||||
case .contentDetail:
|
|
||||||
YANDEX_CONTENT_DETAIL_INTERSTITIAL_AD_UNIT_ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func rewarded(for placement: YandexRewardedPlacement) -> String {
|
static func rewarded(for placement: YandexRewardedPlacement) -> String {
|
||||||
switch placement {
|
switch placement {
|
||||||
case .chatRoomQuota:
|
case .chatRoomQuota:
|
||||||
@@ -204,117 +190,6 @@ private struct YandexInlineBannerContainer: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
|
||||||
final class YandexInterstitialAdManager: NSObject {
|
|
||||||
|
|
||||||
static let shared = YandexInterstitialAdManager()
|
|
||||||
|
|
||||||
private var interstitialAd: InterstitialAd?
|
|
||||||
private var interstitialAdLoader: InterstitialAdLoader?
|
|
||||||
private var currentPlacement: YandexInterstitialPlacement?
|
|
||||||
private var pendingAction: (@MainActor () -> Void)?
|
|
||||||
private var isLoading = false
|
|
||||||
|
|
||||||
func preloadAd(for placement: YandexInterstitialPlacement) {
|
|
||||||
guard !isLoading else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentPlacement == placement, interstitialAd != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let loader = InterstitialAdLoader()
|
|
||||||
interstitialAdLoader = loader
|
|
||||||
interstitialAd = nil
|
|
||||||
currentPlacement = placement
|
|
||||||
isLoading = true
|
|
||||||
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
let loadedAd = try await loader.loadAd(with: AdRequest(adUnitID: YandexAdUnitIdProvider.interstitial(for: placement)))
|
|
||||||
guard currentPlacement == placement else {
|
|
||||||
isLoading = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedAd.delegate = self
|
|
||||||
interstitialAd = loadedAd
|
|
||||||
} catch {
|
|
||||||
if currentPlacement == placement {
|
|
||||||
interstitialAd = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentPlacement == placement {
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func showAdIfAvailable(for placement: YandexInterstitialPlacement, then action: @escaping @MainActor () -> Void) {
|
|
||||||
guard let presenter = presentingViewController(), let interstitialAd, currentPlacement == placement else {
|
|
||||||
action()
|
|
||||||
preloadAd(for: placement)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingAction = action
|
|
||||||
interstitialAd.show(from: presenter)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func completePendingAction() {
|
|
||||||
let action = pendingAction
|
|
||||||
pendingAction = nil
|
|
||||||
interstitialAd = nil
|
|
||||||
action?()
|
|
||||||
|
|
||||||
if let currentPlacement {
|
|
||||||
preloadAd(for: currentPlacement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 YandexInterstitialAdManager: InterstitialAdDelegate {
|
|
||||||
|
|
||||||
func interstitialAdDidShow(_ interstitialAd: InterstitialAd) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func interstitialAdDidDismiss(_ interstitialAd: InterstitialAd) {
|
|
||||||
completePendingAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func interstitialAdDidClick(_ interstitialAd: InterstitialAd) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func interstitialAd(_ interstitialAd: InterstitialAd, didTrackImpression impressionData: ImpressionData?) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func interstitialAd(_ interstitialAd: InterstitialAd, didFailToShow error: Error) {
|
|
||||||
completePendingAction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class YandexRewardedAdManager: NSObject {
|
final class YandexRewardedAdManager: NSObject {
|
||||||
|
|
||||||
|
|||||||
@@ -199,10 +199,6 @@ struct ContentDetailPlayView: View {
|
|||||||
.frame(width: screenSize().width - 40)
|
.frame(width: screenSize().width - 40)
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task {
|
|
||||||
await YandexInterstitialAdManager.shared.preloadAd(for: .contentDetail)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isPlaying() {
|
if !isPlaying() {
|
||||||
stopTimer()
|
stopTimer()
|
||||||
}
|
}
|
||||||
@@ -245,22 +241,7 @@ struct ContentDetailPlayView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard shouldShowInterstitialBeforePlayback(showPreviewAlert: showPreviewAlert) else {
|
|
||||||
playAction()
|
playAction()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
await YandexInterstitialAdManager.shared.showAdIfAvailable(for: .contentDetail, then: playAction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func shouldShowInterstitialBeforePlayback(showPreviewAlert: Bool) -> Bool {
|
|
||||||
if contentPlayManager.contentId == audioContent.contentId {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return audioContent.price <= 0 || showPreviewAlert
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sliderRange() -> ClosedRange<Double> {
|
private func sliderRange() -> ClosedRange<Double> {
|
||||||
|
|||||||
@@ -91,10 +91,6 @@ struct ContentDetailView: View {
|
|||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
}
|
}
|
||||||
|
|
||||||
YandexInlineBannerView(placement: .contentDetail)
|
|
||||||
.padding(.top, 13.3)
|
|
||||||
|
|
||||||
ContentDetailInfoView(
|
ContentDetailInfoView(
|
||||||
isExpandDescription: $viewModel.isExpandDescription,
|
isExpandDescription: $viewModel.isExpandDescription,
|
||||||
isShowPreviewAlert: $viewModel.isShowPreviewAlert,
|
isShowPreviewAlert: $viewModel.isShowPreviewAlert,
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
- [x] `ContentDetailView` 오픈예정/theme 표시와 다음화/이전화 사이 배너 삽입
|
- [x] `ContentDetailView` 오픈예정/theme 표시와 다음화/이전화 사이 배너 삽입
|
||||||
- [x] `ContentDetailPlayView` 무료 재생/미리듣기 시작 전 전면광고 삽입
|
- [x] `ContentDetailPlayView` 무료 재생/미리듣기 시작 전 전면광고 삽입
|
||||||
- [x] 빌드 및 정적 검증 기록 추가
|
- [x] 빌드 및 정적 검증 기록 추가
|
||||||
|
- [x] 2026-07-13 `ContentDetailView` 콘텐츠 상세 Yandex 인라인 배너 제거
|
||||||
|
- [x] 2026-07-13 `ContentDetailPlayView` 콘텐츠 상세 재생 전 Yandex 전면광고 제거
|
||||||
|
- [x] 2026-07-13 콘텐츠 상세 전용 Yandex placement/ad unit/전면광고 매니저 미사용 코드 제거
|
||||||
|
|
||||||
## 작업 기준
|
## 작업 기준
|
||||||
|
|
||||||
@@ -103,3 +106,18 @@
|
|||||||
- 결과:
|
- 결과:
|
||||||
- 페이지별 광고 ID를 독립적으로 설정할 수 있는 구조로 전환됨
|
- 페이지별 광고 ID를 독립적으로 설정할 수 있는 구조로 전환됨
|
||||||
- completion closure 기반 `self` 캡처 경고 제거 대상 구조를 async/await로 대체함
|
- completion closure 기반 `self` 캡처 경고 제거 대상 구조를 async/await로 대체함
|
||||||
|
|
||||||
|
- 2026-07-13 / 콘텐츠 상세 Yandex 광고 제거
|
||||||
|
- 무엇: 콘텐츠 상세 화면의 Yandex 인라인 배너와 재생 전 Yandex 전면광고 흐름을 제거했다.
|
||||||
|
- 왜: 콘텐츠 상세 페이지에서 광고 영역과 재생 전 전면광고를 더 이상 사용하지 않기 위해서다.
|
||||||
|
- 어떻게:
|
||||||
|
- `SodaLive/Sources/Content/Detail/ContentDetailView.swift`에서 `YandexInlineBannerView(placement: .contentDetail)` 호출을 제거했다.
|
||||||
|
- `SodaLive/Sources/Content/Detail/ContentDetailPlayView.swift`에서 `YandexInterstitialAdManager` preload/show 호출과 `shouldShowInterstitialBeforePlayback` 가드를 제거했다.
|
||||||
|
- `SodaLive/Sources/Common/YandexAdSupport.swift`에서 콘텐츠 상세 전용 배너 placement, interstitial placement, `YandexInterstitialAdManager`를 제거했다.
|
||||||
|
- `SodaLive/Sources/Utils/Constants.swift`, `SodaLive/Sources/Debug/Utils/Constants.swift`에서 콘텐츠 상세 배너/전면광고 ad unit 상수를 제거했다.
|
||||||
|
- `rg -n "YandexInlineBannerView\\(placement: \\.contentDetail\\)|YandexInterstitialPlacement|YandexInterstitialAdManager|YANDEX_CONTENT_DETAIL_(BANNER|INTERSTITIAL)_AD_UNIT_ID|shouldShowInterstitialBeforePlayback|showAdIfAvailable\\(for: \\.contentDetail|preloadAd\\(for: \\.contentDetail" SodaLive/Sources`
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
|
||||||
|
- 결과:
|
||||||
|
- 콘텐츠 상세 전용 Yandex 배너/전면광고 참조가 `SodaLive/Sources`에 남지 않음
|
||||||
|
- `SodaLive-dev` Debug 빌드 성공
|
||||||
|
- 다른 화면의 Yandex 인라인 배너와 채팅 rewarded 광고 지원 코드는 유지
|
||||||
|
|||||||
@@ -38,3 +38,15 @@
|
|||||||
- `SodaLive` Debug 빌드 성공
|
- `SodaLive` Debug 빌드 성공
|
||||||
- `lsp_diagnostics`는 SourceKit 단독 해석에서 `YandexMobileAds` 모듈 미해결을 보고했으나, 실제 Xcode 빌드는 두 스킴 모두 성공했다.
|
- `lsp_diagnostics`는 SourceKit 단독 해석에서 `YandexMobileAds` 모듈 미해결을 보고했으나, 실제 Xcode 빌드는 두 스킴 모두 성공했다.
|
||||||
- 실제 기기에서 광고 시청 후 오디오가 시작되는 수동 QA는 이 환경에서 앱 로그인/콘텐츠/광고 표시 조작이 불가해 수행하지 못했다.
|
- 실제 기기에서 광고 시청 후 오디오가 시작되는 수동 QA는 이 환경에서 앱 로그인/콘텐츠/광고 표시 조작이 불가해 수행하지 못했다.
|
||||||
|
|
||||||
|
- 2026-07-13 / 후속 변경: 콘텐츠 상세 재생 전 광고 제거
|
||||||
|
- 무엇: 콘텐츠 상세 재생 전 Yandex 전면광고 preload/show 흐름과 관련 공용 interstitial 매니저를 제거했다.
|
||||||
|
- 왜: 콘텐츠 상세에서 재생 전 광고를 더 이상 사용하지 않도록 정책이 변경되었기 때문이다.
|
||||||
|
- 어떻게:
|
||||||
|
- `SodaLive/Sources/Content/Detail/ContentDetailPlayView.swift`에서 `YandexInterstitialAdManager` 호출과 광고 표시 조건 함수를 제거했다.
|
||||||
|
- `SodaLive/Sources/Common/YandexAdSupport.swift`에서 `YandexInterstitialAdManager`, `YandexInterstitialPlacement`, interstitial ad unit 조회를 제거했다.
|
||||||
|
- `rg`로 콘텐츠 상세 전용 interstitial 참조가 남지 않는지 확인했다.
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
|
||||||
|
- 결과:
|
||||||
|
- 콘텐츠 상세 재생 탭은 광고 분기 없이 기존 재생 액션을 바로 실행한다.
|
||||||
|
- `SodaLive-dev` Debug 빌드 성공
|
||||||
|
|||||||
Reference in New Issue
Block a user