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
- Swift
- Objective-C
import EmpowerMobileAdsclass StoreViewController: UIViewController, AdStatusDelegate {private let rewardedZoneId = "YOUR_REWARDED_ZONE_ID"override func viewDidLoad() {super.viewDidLoad()loadRewardedAd()}func loadRewardedAd() {EMAManager.shared.loadRewardedAd(zoneId: rewardedZoneId, delegate: self)}@IBAction func watchAdButtonTapped(_ sender: UIButton) {if EMAManager.shared.isRewardedReady(zoneId: rewardedZoneId) {EMAManager.shared.showRewarded(zoneId: rewardedZoneId, from: self)} else {showAlert("Ad not ready", "Please try again in a moment.")}}func rewardedStatusChanged(adStatus: AdStatus) {switch adStatus {case .ready:enableWatchAdButton()case .rewarded(let type, let amount):// USER COMPLETED THE AD — grant the rewardgrantReward(amount)case .closed:loadRewardedAd() // Preload nextcase .failed:disableWatchAdButton()default:break}}private func grantReward(_ amount: Int) {UserManager.shared.addCoins(amount)showAlert("Reward Earned!", "You received \(amount) coins!")}}
Important: Reward Callback
Rewarded status updates are delivered through the single status callback
(rewardedStatusChanged in Swift, rewardedStatusChanged:rewardType:rewardAmount: in
Objective-C).
Grant the reward only when the status is rewarded. This status fires exactly once, when
the user successfully finishes watching the ad. If the user closes or skips the ad early, you
receive closed without a preceding rewarded, so no reward is granted.
- Swift
- Objective-C
func rewardedStatusChanged(adStatus: AdStatus) {switch adStatus {case .rewarded(let type, let amount):// CORRECT — user earned the rewardgrantReward(amount)case .closed:// Ad dismissed (reward may or may not have been earned). Do NOT grant here.loadRewardedAd()default:break}}
The rewardType / rewardAmount parameters (Objective-C) and the .rewarded(type:amount:)
associated values (Swift) carry the reward payload configured for the zone.
Loading Rewarded Ads
- Swift
- Objective-C
EMAManager.shared.loadRewardedAd(zoneId: "YOUR_ZONE_ID", delegate: self)
Auto-Preload
Enable auto-preload so the next ad is loaded automatically when the current one is shown, keeping an ad ready at all times:
- Swift
- Objective-C
EMAManager.shared.setRewardedAutoPreload(enabled: true, zoneId: "YOUR_ZONE_ID")
Showing Rewarded Ads
Always verify the ad is ready before showing:
- Swift
- Objective-C
func showRewardedAd() {let zoneId = "YOUR_ZONE_ID"if EMAManager.shared.isRewardedReady(zoneId: zoneId) {EMAManager.shared.showRewarded(zoneId: zoneId, from: self)} else {showAlert("Not Ready", "Ad is still loading. Please try again.")}}
Status Handling
- Swift
- Objective-C
extension StoreViewController: AdStatusDelegate {func rewardedStatusChanged(adStatus: AdStatus) {switch adStatus {case .initializing:watchAdButton.isEnabled = falsewatchAdButton.setTitle("Loading...", for: .normal)case .ready:watchAdButton.isEnabled = truewatchAdButton.setTitle("Watch Ad for 100 Coins", for: .normal)case .shown:pauseBackgroundMusic()case .rewarded(_, let amount):// User completed the ad — grant the rewardgrantReward(amount)case .closed:resumeBackgroundMusic()loadRewardedAd() // Preload nextcase .failed:watchAdButton.isEnabled = falsewatchAdButton.setTitle("Ad Unavailable", for: .normal)retryLoadAfterDelay()default:break}}private func retryLoadAfterDelay() {DispatchQueue.main.asyncAfter(deadline: .now() + 30) { [weak self] inself?.loadRewardedAd()}}}
Status Flow
loadRewardedAd() → .initializing → .ready → showRewarded() → .shown → user completes → .rewarded → .closed↓ ↓.failed user skips → .closed (no reward)
Status Values
Swift (AdStatus) | Objective-C (EMAAdStatusType) | Description |
|---|---|---|
.initializing | EMAAdStatusTypeInitializing | Ad is loading |
.ready | EMAAdStatusTypeReady | Ad is ready to show |
.shown | EMAAdStatusTypeShown | Ad is currently showing |
.rewarded(type:amount:) | EMAAdStatusTypeRewarded | User completed the ad and earned the reward |
.failed(Error?) | EMAAdStatusTypeFailed | Ad failed to load |
.closed | EMAAdStatusTypeClosed | Ad was closed/dismissed |
Releasing a Zone
- Swift
- Objective-C
EMAManager.shared.destroyRewardedZone(zoneId: "YOUR_ZONE_ID")
SwiftUI Integration
import SwiftUIimport EmpowerMobileAds// MARK: - Rewarded Ad Coordinatorclass RewardedAdCoordinator: NSObject, ObservableObject, AdStatusDelegate {@Published var isReady = false@Published var isLoading = trueprivate let zoneId: Stringvar onRewardEarned: ((Int) -> Void)?init(zoneId: String) {self.zoneId = zoneIdsuper.init()load()}func load() {isLoading = trueEMAManager.shared.loadRewardedAd(zoneId: zoneId, delegate: self)}func show() {guard isReady,let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,let rootVC = windowScene.windows.first?.rootViewController else { return }EMAManager.shared.showRewarded(zoneId: zoneId, from: rootVC)}func rewardedStatusChanged(adStatus: AdStatus) {DispatchQueue.main.async {switch adStatus {case .ready:self.isReady = trueself.isLoading = falsecase .failed:self.isReady = falseself.isLoading = falsecase .rewarded(_, let amount):self.onRewardEarned?(amount)case .closed:self.isReady = falseself.load() // Reloaddefault:break}}}}// 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() }) {Text(rewardedAd.isReady ? "Watch Ad for 100 Coins" : "Ad Unavailable").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 your reward!")}}}
Troubleshooting
Reward Not Granted
- Grant the reward in the
rewardedstatus only (not onclosed). - Check the delegate/observer is set when loading.
- Verify the ad completed (a skipped ad never reports
rewarded).
Ad Not Loading
- Swift
- Objective-C
print("Is ready: \(EMAManager.shared.isRewardedReady(zoneId: "YOUR_ZONE_ID"))")EMASettings.shared.logLevel = .all
API Reference
func loadRewardedAd(zoneId: String, delegate: AdStatusDelegate? = nil)func showRewarded(zoneId: String, from viewController: UIViewController)func isRewardedReady(zoneId: String) -> Boolfunc setRewardedAutoPreload(enabled: Bool, zoneId: String)func destroyRewardedZone(zoneId: String)
Objective-C selectors
| Swift | Objective-C selector |
|---|---|
loadRewardedAd(zoneId:delegate:) | loadRewardedAdWithZoneId:observer: |
showRewarded(zoneId:from:) | showRewardedWithZoneId:fromViewController: |
isRewardedReady(zoneId:) | isRewardedReadyForZoneId: |
setRewardedAutoPreload(enabled:zoneId:) | setRewardedAutoPreloadEnabled:forZoneId: |
destroyRewardedZone(zoneId:) | destroyRewardedZone: |
In Objective-C, adopt
EMAAdObserverand implementrewardedStatusChanged:rewardType:rewardAmount:. Grant the reward whenstatusisEMAAdStatusTypeRewarded.