Rewarded Ads
Rewarded ads let users opt-in to watch a video ad in exchange for an in-app reward (extra lives, premium content, virtual currency, etc.).
Each ad format uses a Zone ID to identify the ad placement. Zone IDs are configured in the Empower dashboard.
Note: All ad status listeners are optional. The SDK handles ad loading and display automatically. Use listeners only if you need to track ad states for analytics, UI updates, or custom logic.
Quick Start
import EmpowerMobileAdsclass StoreViewController: UIViewController, AdStatusDelegate {private var rewardedZoneManager: EmpowerZoneManager?override func viewDidLoad() {super.viewDidLoad()loadRewardedAd()}func loadRewardedAd() {rewardedZoneManager = EMAManager.shared.loadRewarded(zoneId: "YOUR_REWARDED_ZONE_ID",delegate: self)}@IBAction func watchAdButtonTapped(_ sender: UIButton) {guard let manager = rewardedZoneManager,manager.isRewardedReady() else {showAlert("Ad not ready", "Please try again in a moment.")return}manager.showRewarded()}// Called when user COMPLETES the adfunc empowerRewardedAdStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .rewarded {grantReward()loadRewardedAd() // Preload next}}func rewardedStatusChanged(_ manager: EmpowerRewardedManager) {switch manager.status {case .ready:enableWatchAdButton()case .failed:disableWatchAdButton()default:break}}private func grantReward() {// Grant 100 coins to userUserManager.shared.addCoins(100)showAlert("Reward Earned!", "You received 100 coins!")}}
Important: Reward Callback
There are two delegate methods for rewarded ads:
| Method | When Called | Use For |
|---|---|---|
rewardedStatusChanged(_:) | Status changes (loading, ready, etc.) | UI updates |
empowerRewardedAdStatusChanged(_:) | User completed watching | Granting rewards |
Always use empowerRewardedAdStatusChanged for granting rewards. The .rewarded status is only triggered when the user successfully completes watching the entire ad.
// CORRECT - Grant reward herefunc empowerRewardedAdStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .rewarded {grantReward() // User earned the reward}}// WRONG - Don't grant reward herefunc rewardedStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .used {// This is called when ad closes, NOT when reward is earned// User might have skipped the ad}}
Loading Rewarded Ads
Basic Loading
rewardedZoneManager = EMAManager.shared.loadRewarded(zoneId: "YOUR_ZONE_ID",delegate: self)
With Custom Parameters
let customParams: [String: [String]] = ["user_level": ["25"],"reward_type": ["coins"]]rewardedZoneManager = EMAManager.shared.loadRewarded(zoneId: "YOUR_ZONE_ID",delegate: self,customParameters: customParams)
Showing Rewarded Ads
Check Readiness
func showRewardedAd() {guard let manager = rewardedZoneManager else {print("Rewarded manager not initialized")return}if manager.isRewardedReady() {manager.showRewarded()} else {showAlert("Not Ready", "Ad is still loading. Please try again.")}}
With Confirmation Dialog
@IBAction func watchAdButtonTapped(_ sender: UIButton) {let alert = UIAlertController(title: "Watch Ad for Coins?",message: "Watch a short video to earn 100 coins!",preferredStyle: .alert)alert.addAction(UIAlertAction(title: "Watch", style: .default) { [weak self] _ inself?.showRewardedAd()})alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))present(alert, animated: true)}
Status Handling
Complete Implementation
extension StoreViewController: AdStatusDelegate {// Status updates (loading, ready, failed, etc.)func rewardedStatusChanged(_ manager: EmpowerRewardedManager) {switch manager.status {case .initializing:// Ad is loadingwatchAdButton.isEnabled = falsewatchAdButton.setTitle("Loading...", for: .normal)case .ready:// Ad is ready to showwatchAdButton.isEnabled = truewatchAdButton.setTitle("Watch Ad for 100 Coins", for: .normal)case .failed:// Ad failed to loadwatchAdButton.isEnabled = falsewatchAdButton.setTitle("Ad Unavailable", for: .normal)// Retry after delayretryLoadAfterDelay()case .present:// Ad is showingpauseBackgroundMusic()case .used:// Ad was closed (reward may or may not have been earned)resumeBackgroundMusic()loadRewardedAd() // Preload nextdefault:break}}// REWARD CALLBACK - User completed watchingfunc empowerRewardedAdStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .rewarded {// USER EARNED THE REWARDgrantReward()}}private func grantReward() {// Add coinslet coinsEarned = 100UserManager.shared.addCoins(coinsEarned)// Show success messageshowRewardAlert(coins: coinsEarned)// AnalyticsAnalytics.track("reward_earned", ["coins": coinsEarned])}private func retryLoadAfterDelay() {DispatchQueue.main.asyncAfter(deadline: .now() + 30) { [weak self] inself?.loadRewardedAd()}}}
Status Flow
loadRewarded() → .initializing → .ready → showRewarded() → .present → User watches → .rewarded → .used↓ ↓.failed User skips → .used (no reward)
Status Values
| Status | Description |
|---|---|
.initializing | Ad is loading |
.ready | Ad is ready to show |
.present | Ad is currently showing |
.rewarded | User completed ad and earned reward |
.failed | Ad failed to load |
.used | Ad was closed |
SwiftUI Integration
import SwiftUIimport EmpowerMobileAds// MARK: - Rewarded Ad Coordinatorclass RewardedAdCoordinator: NSObject, ObservableObject, AdStatusDelegate {@Published var isReady = false@Published var isLoading = true@Published var rewardEarned = falseprivate var zoneManager: EmpowerZoneManager?private let zoneId: Stringvar onRewardEarned: ((Int) -> Void)?init(zoneId: String) {self.zoneId = zoneIdsuper.init()load()}func load() {isLoading = truezoneManager = EMAManager.shared.loadRewarded(zoneId: zoneId,delegate: self)}func show() {guard isReady, let manager = zoneManager else { return }manager.showRewarded()}// Status updatesfunc rewardedStatusChanged(_ manager: EmpowerRewardedManager) {DispatchQueue.main.async {switch manager.status {case .ready:self.isReady = trueself.isLoading = falsecase .failed:self.isReady = falseself.isLoading = falsecase .used:self.isReady = falseself.load()default:break}}}// Reward callbackfunc empowerRewardedAdStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .rewarded {DispatchQueue.main.async {self.rewardEarned = trueself.onRewardEarned?(100)}}}}// MARK: - Usage in SwiftUI Viewstruct StoreView: View {@StateObject private var rewardedAd = RewardedAdCoordinator(zoneId: "YOUR_ZONE_ID")@State private var coins = 0@State private var showRewardAlert = falsevar body: some View {VStack(spacing: 20) {Text("Coins: \(coins)").font(.largeTitle)Button(action: {rewardedAd.show()}) {HStack {Image(systemName: "play.circle.fill")Text(buttonTitle)}.frame(maxWidth: .infinity).padding().background(rewardedAd.isReady ? Color.blue : Color.gray).foregroundColor(.white).cornerRadius(10)}.disabled(!rewardedAd.isReady)}.padding().onAppear {rewardedAd.onRewardEarned = { amount incoins += amountshowRewardAlert = true}}.alert("Reward Earned!", isPresented: $showRewardAlert) {Button("OK", role: .cancel) {}} message: {Text("You received 100 coins!")}}private var buttonTitle: String {if rewardedAd.isLoading {return "Loading..."} else if rewardedAd.isReady {return "Watch Ad for 100 Coins"} else {return "Ad Unavailable"}}}
Troubleshooting
Reward Not Granted
- Ensure you're using the correct callback:
// Use THIS for rewardsfunc empowerRewardedAdStatusChanged(_ manager: EmpowerRewardedManager) {if manager.status == .rewarded {grantReward()}}
Check delegate is set correctly
Verify ad completed (not skipped)
Ad Not Loading
// Debug stateprint("Manager exists: \(rewardedZoneManager != nil)")print("Is ready: \(rewardedZoneManager?.isRewardedReady() ?? false)")// Enable loggingEMASettings.shared.logLevel = .all