Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moloco Native ad support in adapter. #478

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions adapters/Moloco/MolocoAdapter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
ADFA84C22C17704300A501AB /* InterstitialAdLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFA84AA2C17704300A501AB /* InterstitialAdLoader.swift */; };
ADFA84C32C17704300A501AB /* NativeAdLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFA84AB2C17704300A501AB /* NativeAdLoader.swift */; };
ADFA84C42C17704300A501AB /* RewardedAdLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFA84AC2C17704300A501AB /* RewardedAdLoader.swift */; };
F04223732D494E59005D2513 /* MolocoNativeFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04223722D494E4A005D2513 /* MolocoNativeFactory.swift */; };
F04223742D494E59005D2513 /* MolocoNativeFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04223722D494E4A005D2513 /* MolocoNativeFactory.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -115,6 +117,7 @@
ADFA84B82C17704300A501AB /* Script_Validate.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = Script_Validate.sh; sourceTree = "<group>"; };
ADFA84BC2C17704300A501AB /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
ADFA84D42C17738E00A501AB /* MolocoAdapterFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MolocoAdapterFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F04223722D494E4A005D2513 /* MolocoNativeFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MolocoNativeFactory.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -179,6 +182,7 @@
70E380E92C472410004CCB92 /* MolocoSdkImpl.swift */,
85AD79612CA5D967007358CE /* MolocoBannerFactory.swift */,
8566027E2C63F6FF0068D786 /* MolocoRewardedFactory.swift */,
F04223722D494E4A005D2513 /* MolocoNativeFactory.swift */,
706083C62C47CDB100896E93 /* MolocoInterstitialFactory.swift */,
70A1FA362CC343D300BC54D0 /* MolocoBidTokenGetter.swift */,
703876C82CF52724000C09EE /* MolocoSdkVersionProviding.swift */,
Expand Down Expand Up @@ -365,6 +369,7 @@
ADA6BA4D2CF146FE009C8FDA /* BannerAdLoader.swift in Sources */,
ADA6BA4E2CF146FE009C8FDA /* MolocoUtils.swift in Sources */,
ADA6BA4F2CF146FE009C8FDA /* MolocoSdkImpl.swift in Sources */,
F04223742D494E59005D2513 /* MolocoNativeFactory.swift in Sources */,
ADA6BA502CF146FE009C8FDA /* RewardedAdLoader.swift in Sources */,
ADA6BA512CF146FE009C8FDA /* MolocoBannerFactory.swift in Sources */,
ADA6BA522CF146FE009C8FDA /* MolocoBidTokenGetter.swift in Sources */,
Expand All @@ -388,6 +393,7 @@
7072CDE82C409510002BF6AE /* MolocoUtils.swift in Sources */,
70E380EA2C472410004CCB92 /* MolocoSdkImpl.swift in Sources */,
ADFA84C42C17704300A501AB /* RewardedAdLoader.swift in Sources */,
F04223732D494E59005D2513 /* MolocoNativeFactory.swift in Sources */,
85AD79622CA5D967007358CE /* MolocoBannerFactory.swift in Sources */,
70A1FA372CC343D300BC54D0 /* MolocoBidTokenGetter.swift in Sources */,
ADFA84BD2C17704300A501AB /* MolocoMediationAdapter.swift in Sources */,
Expand Down
6 changes: 4 additions & 2 deletions adapters/Moloco/MolocoAdapter/MolocoMediationAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public final class MolocoMediationAdapter: NSObject, GADRTBAdapter {
/// Used to create Moloco rewarded ads.
private var molocoRewardedFactory: MolocoRewardedFactory = MolocoMediationAdapter.molocoSdkImpl

/// Used to create Moloco native ads.
private var molocoNativeFactory: MolocoNativeFactory = MolocoMediationAdapter.molocoSdkImpl

/// Used to create Moloco banner ads.
private var molocoBannerFactory: MolocoBannerFactory = MolocoMediationAdapter.molocoSdkImpl

Expand Down Expand Up @@ -260,13 +263,12 @@ public final class MolocoMediationAdapter: NSObject, GADRTBAdapter {
rewardedAdLoader?.loadAd()
}

// TODO: Remove if not needed. If removed, then remove the |NativeAdLoader| class as well.
@objc public func loadNativeAd(
for adConfiguration: GADMediationNativeAdConfiguration,
completionHandler: @escaping GADMediationNativeLoadCompletionHandler
) {
nativeAdLoader = NativeAdLoader(
adConfiguration: adConfiguration, loadCompletionHandler: completionHandler)
adConfiguration: adConfiguration, loadCompletionHandler: completionHandler, molocoNativeFactory: molocoNativeFactory)
nativeAdLoader?.loadAd()
}

Expand Down
22 changes: 22 additions & 0 deletions adapters/Moloco/MolocoAdapter/MolocoNativeFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// MolocoNativeFactory.swift
// MolocoAdapter
//
// Created by Vishal Dhiman on 1/28/25.
//

import Foundation
import MolocoSDK

/// Protocol for a factory of Moloco Native ads.
public protocol MolocoNativeFactory {

@MainActor
@available(iOS 13.0, *)
func createNativeAd(for adUnit: String, delegate: MolocoNativeAdDelegate)
-> MolocoNativeAd?

}



13 changes: 13 additions & 0 deletions adapters/Moloco/MolocoAdapter/MolocoSdkImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ extension MolocoSdkImpl: MolocoBannerFactory {

}

extension MolocoSdkImpl: MolocoNativeFactory {

@MainActor
@available(iOS 13.0, *)
func createNativeAd(for adUnit: String, delegate: MolocoNativeAdDelegate) -> MolocoNativeAd? {
guard let rootViewController = MolocoUtils.keyWindow()?.rootViewController else {
return nil
}

return Moloco.shared.createNativeAd(for: adUnit, delegate: delegate)
}

}
// MARK: - MolocoBidTokenGetter

extension MolocoSdkImpl: MolocoBidTokenGetter {
Expand Down
147 changes: 127 additions & 20 deletions adapters/Moloco/MolocoAdapter/NativeAdLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,159 @@

import Foundation
import GoogleMobileAds
import MolocoSDK

/// Loads native ads on Moloco ads SDK.
final class NativeAdLoader: NSObject {

/// The native ad configuration.
private let adConfiguration: GADMediationNativeAdConfiguration

/// The completion handler to call when native ad loading succeeds or fails.
private let loadCompletionHandler: GADMediationNativeLoadCompletionHandler

/// The ad event delegate which is used to report native related information to the Google Mobile Ads SDK.
private weak var eventDelegate: GADMediationNativeAdEventDelegate?

private let molocoNativeFactory: MolocoNativeFactory?

private var nativeAd: MolocoNativeAd?

init(
adConfiguration: GADMediationNativeAdConfiguration,
loadCompletionHandler: @escaping GADMediationNativeLoadCompletionHandler
loadCompletionHandler: @escaping GADMediationNativeLoadCompletionHandler,
molocoNativeFactory: MolocoNativeFactory
) {
self.adConfiguration = adConfiguration
self.loadCompletionHandler = loadCompletionHandler
self.molocoNativeFactory = molocoNativeFactory
super.init()
}

func loadAd() {
// TODO: implement and make sure to call |nativeAdLoadCompletionHandler| after loading an ad.
guard #available(iOS 13.0, *) else {
let error = MolocoUtils.error(
code: .adServingNotSupported,
description: "Moloco SDK does not support serving ads on iOS 12 and below")
_ = loadCompletionHandler(nil, error)
return
}

let molocoAdUnitId = MolocoUtils.getAdUnitId(from: adConfiguration)
guard let molocoAdUnitId = molocoAdUnitId else {
let error = MolocoUtils.error(
code: .invalidAdUnitId, description: "Missing required parameter")
_ = loadCompletionHandler(nil, error)
return
}

guard let bidResponse = adConfiguration.bidResponse else {
let error = MolocoUtils.error(code: .nilBidResponse, description: "Nil bid response.")
_ = loadCompletionHandler(nil, error)
return
}

DispatchQueue.main.async { [weak self] in
guard let self else { return }

self.nativeAd = self.molocoNativeFactory?.createNativeAd(
for: molocoAdUnitId, delegate: self)
guard self.nativeAd != nil else {
let error = MolocoUtils.error(code: .invalidAdUnitId, description: "Ad not loaded.")
_ = loadCompletionHandler(nil, error)
return
}
self.nativeAd?.load(bidResponse: bidResponse)
}

}

}

// MARK: - MolocoNativeAdDelegate

extension NativeAdLoader: MolocoNativeAdDelegate {
func didLoad(ad: any MolocoSDK.MolocoAd) {

guard ad.isReady else {
let error = MolocoUtils.error(code: .adNotReadyForShow, description: "Ad not ready to show.")
_ = loadCompletionHandler(nil, error)
return
}

eventDelegate = loadCompletionHandler(self, nil)
}

func failToLoad(ad: any MolocoSDK.MolocoAd, with error: (any Error)?) {
_ = loadCompletionHandler(nil, error)
}

func didShow(ad: any MolocoSDK.MolocoAd) {
eventDelegate?.reportImpression()
}

func failToShow(ad: any MolocoSDK.MolocoAd, with error: (any Error)?) {
let showError =
error
?? MolocoUtils.error(
code: .adFailedToShow, description: "Ad failed to show")
eventDelegate?.didFailToPresentWithError(showError)
}

func didHide(ad: any MolocoSDK.MolocoAd) {
eventDelegate?.didDismissFullScreenView()
}

func didClick(on ad: any MolocoSDK.MolocoAd) {
eventDelegate?.reportClick()
}

func didHandleClick(ad: any MolocoAd) {
eventDelegate?.reportClick()
}

func didHandleImpression(ad: any MolocoAd) {
eventDelegate?.reportImpression()
}

}

// MARK: - GADMediationNativeAd

extension NativeAdLoader: GADMediationNativeAd {

// TODO: implement computed properties and methods below. Implement more optional methods from |GADMediationNativeAd|, if needed.

var headline: String? {
return nil
return self.nativeAd?.assets?.title
}

var images: [GADNativeAdImage]? {
return nil
guard let mainImage = self.nativeAd?.assets?.mainImage else {
return nil
}
return [GADNativeAdImage(image: mainImage)]
}

var mediaView: UIView? {
guard let assets = self.nativeAd?.assets else {
return nil
}
return assets.videoView ?? UIImageView(image: assets.mainImage)
}

var body: String? {
return nil
return self.nativeAd?.assets?.description
}

var icon: GADNativeAdImage? {
return nil
return self.nativeAd?.assets?.appIcon.map { .init(image: $0) }
}

var callToAction: String? {
return nil
return self.nativeAd?.assets?.ctaTitle
}

var starRating: NSDecimalNumber? {
return nil
return self.nativeAd?.assets?.rating as? NSDecimalNumber
}

var store: String? {
Expand All @@ -77,29 +178,35 @@ extension NativeAdLoader: GADMediationNativeAd {
}

var advertiser: String? {
return nil
return self.nativeAd?.assets?.sponsorText
}

var extraAssets: [String: Any]? {
return nil
}

var hasVideoContent: Bool {
// TODO: implement
return true
return (self.nativeAd?.assets?.videoView != nil)
}

func handlesUserClicks() -> Bool {
// TODO: implement
return true
return false
}

func handlesUserImpressions() -> Bool {
// TODO: implement
return true
return false
}

}
func didRecordImpression() {
nativeAd?.handleImpression()
}

// MARK: - <OtherProtocol>
// TODO: extend and implement any other protocol, if any.
func didRecordClickOnAsset(
withName assetName: GADNativeAssetIdentifier,
view: UIView,
viewController: UIViewController
) {
nativeAd?.handleClick()
}

}