The Metica Ads SDK for iOS provides a simple, efficient way to integrate Metica's advertising functionality into your iOS applications. The SDK supports interstitial, rewarded, banner, and MREC ad formats with built-in A/B testing capabilities through Smart Floors.
Requirements
iOS 15.0 or higher
Swift 5.0+
Xcode 14+
AppLovinSDK dependency (version 13.0.0 or higher)
Valid Metica API credentials (API key, App ID)
CRITICAL REQUIREMENT
You must initialize the Metica SDK before invoking any ad methods. The Metica SDK handles AppLovin SDK initialization internally, so you should NOT initialize AppLovin SDK separately.
// Set user consent for personalized ads
MeticaSdk.privacySettings.setHasUserConsent(isConsent: true)
// Set "Do Not Sell" flag for CCPA compliance
MeticaSdk.privacySettings.setDoNotSell(isDoNotSell: false)
import MeticaSDK
class AppViewModel: ObservableObject, MeticaInitCallback {
@Published var isInitialized = false
@Published var userGroup = ""
@MainActor
func initializeSDK() {
// Enable logging for development
MeticaLogger.enableLogs = true
// Set privacy settings BEFORE initialization
MeticaSdk.privacySettings.setHasUserConsent(isConsent: true)
MeticaSdk.privacySettings.setDoNotSell(isDoNotSell: false)
// Create configuration
let initConfig = MeticaInitConfig(
apiKey: "YOUR_API_KEY",
appId: "YOUR_APP_ID",
userId: "" // Leave empty for auto-generated stable ID
)
let mediationInfo = MeticaMediationInfo(
mediationType: .max,
key: "YOUR_APPLOVIN_SDK_KEY"
)
// Initialize the SDK
MeticaSdk.initialize(
initConfig: initConfig,
mediationInfo: mediationInfo,
callback: self
)
}
// MARK: - MeticaInitCallback
func onInit(initResponse: MeticaInitResponse) {
DispatchQueue.main.async {
self.isInitialized = true
self.userGroup = initResponse.smartFloors.userGroup == .trial ? "trial" : "holdout"
if initResponse.smartFloors.isSuccess {
print("Metica SDK initialized successfully")
} else {
print("Metica SDK initialized in fallback mode")
}
}
}
}
class InterstitialAdManager: MeticaAdsLoadCallback, MeticaAdsShowCallback {
let adUnitId = "YOUR_INTERSTITIAL_AD_UNIT_ID"
private var retryAttempt: Int = 0
private let maxRetryDelay: Double = 64.0
func loadAd() {
MeticaSdk.Ads.loadInterstitial(adUnitId: adUnitId, callback: self)
}
func showAd() {
if MeticaSdk.Ads.isInterstitialReady(adUnitId: adUnitId) {
MeticaSdk.Ads.showInterstitial(adUnitId: adUnitId, callback: self)
} else {
print("Interstitial not ready")
}
}
// MARK: - MeticaAdsLoadCallback
func onAdLoadSuccess(meticaAd: MeticaAd) {
// Reset retry state on success
retryAttempt = 0
print("Interstitial loaded: \(meticaAd.adUnitId)")
}
func onAdLoadFailed(meticaAdError: MeticaAdError) {
print("Interstitial load failed: \(meticaAdError.message)")
// Exponential backoff retry (2s, 4s, 8s, ... capped at 64s)
retryAttempt += 1
let delaySeconds = min(pow(2.0, Double(retryAttempt)), maxRetryDelay)
DispatchQueue.main.asyncAfter(deadline: .now() + delaySeconds) { [weak self] in
guard let self = self else { return }
print("Retrying interstitial load in \(delaySeconds)s")
self.loadAd()
}
}
// MARK: - MeticaAdsShowCallback
func onAdShowSuccess(meticaAd: MeticaAd) {
print("Interstitial shown")
}
func onAdShowFailed(meticaAd: MeticaAd, meticaAdError: MeticaAdError) {
print("Interstitial show failed: \(meticaAdError.message)")
}
func onAdHidden(meticaAd: MeticaAd) {
print("Interstitial hidden")
self.loadAd()
}
func onAdClicked(meticaAd: MeticaAd) {
print("Interstitial clicked")
}
func onAdRevenuePaid(meticaAd: MeticaAd) {
print("Interstitial revenue: \(meticaAd.revenue)")
}
func onAdRewarded(meticaAd: MeticaAd) {
// Not called for interstitial ads
}
}
class RewardedAdManager: MeticaAdsLoadCallback, MeticaAdsShowCallback {
let adUnitId = "YOUR_REWARDED_AD_UNIT_ID"
private var retryAttempt: Int = 0
private let maxRetryDelay: Double = 64.0
func loadAd() {
MeticaSdk.Ads.loadRewarded(adUnitId: adUnitId, callback: self)
}
func showAd() {
if MeticaSdk.Ads.isRewardedReady(adUnitId: adUnitId) {
MeticaSdk.Ads.showRewarded(adUnitId: adUnitId, callback: self)
} else {
print("Rewarded ad not ready")
}
}
// MARK: - MeticaAdsLoadCallback
func onAdLoadSuccess(meticaAd: MeticaAd) {
retryAttempt = 0
print("Rewarded ad loaded")
}
func onAdLoadFailed(meticaAdError: MeticaAdError) {
print("Rewarded ad load failed: \(meticaAdError.message)")
// Exponential backoff retry (2s, 4s, 8s, ... capped at 64s)
retryAttempt += 1
let delaySeconds = min(pow(2.0, Double(retryAttempt)), maxRetryDelay)
DispatchQueue.main.asyncAfter(deadline: .now() + delaySeconds) { [weak self] in
guard let self = self else { return }
print("Retrying rewarded load in \(delaySeconds)s")
self.loadAd()
}
}
// MARK: - MeticaAdsShowCallback
func onAdShowSuccess(meticaAd: MeticaAd) {
print("Rewarded ad shown")
}
func onAdShowFailed(meticaAd: MeticaAd, meticaAdError: MeticaAdError) {
print("Rewarded ad show failed: \(meticaAdError.message)")
}
func onAdHidden(meticaAd: MeticaAd) {
print("Rewarded ad hidden")
self.loadAd()
}
func onAdClicked(meticaAd: MeticaAd) {
print("Rewarded ad clicked")
}
func onAdRevenuePaid(meticaAd: MeticaAd) {
print("Rewarded ad revenue: \(meticaAd.revenue)")
}
func onAdRewarded(meticaAd: MeticaAd) {
print("User earned reward!")
// Grant reward to user here
}
}