From 5e1cb87475d4363b961c8a5e8932a99acb1acf98 Mon Sep 17 00:00:00 2001 From: leesam Date: Wed, 15 Jul 2026 11:11:27 +0900 Subject: [PATCH 1/3] feat: add reward ad show API --- GADManager/GADManager.swift | 46 +++++++++++++++++++++++++++++++++---- README.md | 8 +++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/GADManager/GADManager.swift b/GADManager/GADManager.swift index f2d6d9a..49c0d87 100644 --- a/GADManager/GADManager.swift +++ b/GADManager/GADManager.swift @@ -297,12 +297,14 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc //reprepare } - public func prepare(rewardUnit unit: E, isTesting: Bool = false){ + public func prepare(rewardUnit unit: E, isTesting: Bool = false, hideTestLabel: Bool? = nil){ self.isTesting[unit] = isTesting; self.isRewardUnit[unit] = true; + self.hideTestLabels[unit] = hideTestLabel; guard let _ = self.adObjects[unit] else{ if let unitId = self.adId(forUnit: unit, andForAdType: .reward, isTesting: isTesting) { let req = GoogleMobileAds.Request(); + if hideTestLabel ?? false { req.hideTestLabel() } self.isLoading[unit] = true; @@ -311,14 +313,25 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc return; } - newAd?.fullScreenContentDelegate = self; - self.adObjects[unit] = newAd; if let error = error{ print("GAD Reward is error. unit[\(unit)] id[\(unitId)] error[\(error)]"); self.isLoading[unit] = false; + let completion = self.completions[unit]; + self.completions[unit] = nil; + completion?(unit, newAd, false); + return; + } + + guard let newAd = newAd else{ + self.isLoading[unit] = false; + let completion = self.completions[unit]; + self.completions[unit] = nil; + completion?(unit, nil, false); return; } + newAd.fullScreenContentDelegate = self; + self.adObjects[unit] = newAd; debugPrint("Reward is ready. unit[\(unit)]"); self.isLoading[unit] = false; guard let completion = self.completions[unit] else{ @@ -329,6 +342,9 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc } }else{ assertionFailure("create dictionary 'GADUnitIdentifiers' and insert new unit id into it."); + let completion = self.completions[unit]; + self.completions[unit] = nil; + completion?(unit, nil, false); } return; } @@ -359,7 +375,7 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc func reprepare(rewardUnit unit: E, isTesting: Bool = false){ self.adObjects[unit] = nil; - self.prepare(rewardUnit: unit, isTesting: isTesting); + self.prepare(rewardUnit: unit, isTesting: isTesting, hideTestLabel: self.hideTestLabels[unit]); } func reprepare(adObject: NSObject, isTesting: Bool = false){ @@ -408,6 +424,28 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc return value; } + public func show(rewardUnit unit: E, force: Bool = false, needToWait wait: Bool = true, isTesting: Bool = false, viewController: UIViewController? = nil, completion: ((E, GoogleMobileAds.AdReward?, Bool) -> Void)? = nil){ + self.isTesting[unit] = isTesting; + self.isRewardUnit[unit] = true; + + let rewardCompletion: ((E, NSObject?, Bool) -> Void)? = { unit, object, rewarded in + completion?(unit, object as? GoogleMobileAds.AdReward, rewarded); + } + + if self.adObjects[unit] == nil && !(self.isLoading[unit] ?? false){ + if wait{ + self.completions[unit] = rewardCompletion; + } + self.prepare(rewardUnit: unit, isTesting: isTesting, hideTestLabel: self.hideTestLabels[unit]); + if !wait{ + completion?(unit, nil, false); + } + return; + } + + self.show(unit: unit, force: force, needToWait: wait, isTesting: isTesting, viewController: viewController, completion: rewardCompletion); + } + public func show(unit: E, force : Bool = false, needToWait wait: Bool = false, isTesting: Bool = false, viewController: UIViewController? = nil, completion: ((E, NSObject?, Bool) -> Void)? = nil){ self.isTesting[unit] = isTesting; diff --git a/README.md b/README.md index 0071a64..8989849 100644 --- a/README.md +++ b/README.md @@ -88,18 +88,22 @@ adManager.show(unit: .opening, needToWait: true) { unit, _, shown in } ### Rewarded +Use `prepare(rewardUnit:)` to preload a rewarded ad through this library. Then call `show(rewardUnit:)`; `rewarded` is `true` only after the user earns the reward. + ```swift import GoogleMobileAds adManager.prepare(rewardUnit: .rewarded) -adManager.show(unit: .rewarded) { unit, obj, rewarded in - if rewarded, let reward = obj as? GoogleMobileAds.AdReward { +adManager.show(rewardUnit: .rewarded) { unit, reward, rewarded in + if rewarded, let reward { print("Earned \(reward.amount) \(reward.type)") } } ``` +If you need the legacy full-screen API, `show(unit:)` still works and returns the `AdReward` as the callback object when earned. + ### Banner ```swift From a1b34f3d42ce19942293e696edeca4da6d13e2e5 Mon Sep 17 00:00:00 2001 From: leesam Date: Wed, 15 Jul 2026 15:34:06 +0900 Subject: [PATCH 2/3] fix: return rewarded ad from callback --- GADManager/GADManager.swift | 7 ++++--- README.md | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/GADManager/GADManager.swift b/GADManager/GADManager.swift index 49c0d87..1deff69 100644 --- a/GADManager/GADManager.swift +++ b/GADManager/GADManager.swift @@ -424,12 +424,13 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc return value; } - public func show(rewardUnit unit: E, force: Bool = false, needToWait wait: Bool = true, isTesting: Bool = false, viewController: UIViewController? = nil, completion: ((E, GoogleMobileAds.AdReward?, Bool) -> Void)? = nil){ + public func show(rewardUnit unit: E, force: Bool = false, needToWait wait: Bool = true, isTesting: Bool = false, viewController: UIViewController? = nil, completion: ((E, GoogleMobileAds.RewardedAd?, Bool) -> Void)? = nil){ self.isTesting[unit] = isTesting; self.isRewardUnit[unit] = true; - let rewardCompletion: ((E, NSObject?, Bool) -> Void)? = { unit, object, rewarded in - completion?(unit, object as? GoogleMobileAds.AdReward, rewarded); + let rewardCompletion: ((E, NSObject?, Bool) -> Void)? = { [weak self] unit, object, rewarded in + let rewardAd = object as? GoogleMobileAds.RewardedAd ?? self?.adObjects[unit] as? GoogleMobileAds.RewardedAd; + completion?(unit, rewardAd, rewarded); } if self.adObjects[unit] == nil && !(self.isLoading[unit] ?? false){ diff --git a/README.md b/README.md index 8989849..3d3649a 100644 --- a/README.md +++ b/README.md @@ -88,15 +88,15 @@ adManager.show(unit: .opening, needToWait: true) { unit, _, shown in } ### Rewarded -Use `prepare(rewardUnit:)` to preload a rewarded ad through this library. Then call `show(rewardUnit:)`; `rewarded` is `true` only after the user earns the reward. +Use `prepare(rewardUnit:)` to preload a rewarded ad through this library. Then call `show(rewardUnit:)`; its completion returns the `RewardedAd`, and `rewarded` is `true` only after the user earns the reward. Read the amount and type from `rewardedAd.adReward`. ```swift import GoogleMobileAds adManager.prepare(rewardUnit: .rewarded) -adManager.show(rewardUnit: .rewarded) { unit, reward, rewarded in - if rewarded, let reward { +adManager.show(rewardUnit: .rewarded) { unit, rewardedAd, rewarded in + if rewarded, let reward = rewardedAd?.adReward { print("Earned \(reward.amount) \(reward.type)") } } From 295a4ae090d507591fb7e64d91a8226ae3f028dd Mon Sep 17 00:00:00 2001 From: leesam Date: Wed, 15 Jul 2026 16:32:13 +0900 Subject: [PATCH 3/3] fix: preserve deferred reward show options --- GADManager/GADManager.swift | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/GADManager/GADManager.swift b/GADManager/GADManager.swift index 1deff69..4debaaa 100644 --- a/GADManager/GADManager.swift +++ b/GADManager/GADManager.swift @@ -54,6 +54,8 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc var lastBeginLoading : [E : Date] = [:]; var hideTestLabels : [E: Bool] = [:]; var completions : [E : (E, NSObject?, Bool) -> Void] = [:]; + var pendingRewardViewControllers : [E : UIViewController] = [:]; + var pendingRewardForces : [E : Bool] = [:]; public var canShowFirstTime = true; public weak var delegate : GADManagerDelegate?; @@ -318,6 +320,8 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc self.isLoading[unit] = false; let completion = self.completions[unit]; self.completions[unit] = nil; + self.pendingRewardViewControllers[unit] = nil; + self.pendingRewardForces[unit] = nil; completion?(unit, newAd, false); return; } @@ -326,6 +330,8 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc self.isLoading[unit] = false; let completion = self.completions[unit]; self.completions[unit] = nil; + self.pendingRewardViewControllers[unit] = nil; + self.pendingRewardForces[unit] = nil; completion?(unit, nil, false); return; } @@ -334,16 +340,20 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc self.adObjects[unit] = newAd; debugPrint("Reward is ready. unit[\(unit)]"); self.isLoading[unit] = false; + let viewController = self.pendingRewardViewControllers.removeValue(forKey: unit); + let force = self.pendingRewardForces.removeValue(forKey: unit) ?? false; guard let completion = self.completions[unit] else{ return; } - self.show(unit: unit, completion: completion); + self.show(unit: unit, force: force, isTesting: self.isTesting[unit] ?? isTesting, viewController: viewController, completion: completion); } }else{ assertionFailure("create dictionary 'GADUnitIdentifiers' and insert new unit id into it."); let completion = self.completions[unit]; self.completions[unit] = nil; + self.pendingRewardViewControllers[unit] = nil; + self.pendingRewardForces[unit] = nil; completion?(unit, nil, false); } return; @@ -427,23 +437,29 @@ public class GADManager : NSObject, GoogleMobileAds.FullSc public func show(rewardUnit unit: E, force: Bool = false, needToWait wait: Bool = true, isTesting: Bool = false, viewController: UIViewController? = nil, completion: ((E, GoogleMobileAds.RewardedAd?, Bool) -> Void)? = nil){ self.isTesting[unit] = isTesting; self.isRewardUnit[unit] = true; - + let rewardCompletion: ((E, NSObject?, Bool) -> Void)? = { [weak self] unit, object, rewarded in let rewardAd = object as? GoogleMobileAds.RewardedAd ?? self?.adObjects[unit] as? GoogleMobileAds.RewardedAd; completion?(unit, rewardAd, rewarded); } - - if self.adObjects[unit] == nil && !(self.isLoading[unit] ?? false){ + + guard self.adObjects[unit] != nil else{ if wait{ self.completions[unit] = rewardCompletion; + self.pendingRewardViewControllers[unit] = viewController; + self.pendingRewardForces[unit] = force; } - self.prepare(rewardUnit: unit, isTesting: isTesting, hideTestLabel: self.hideTestLabels[unit]); + + if !(self.isLoading[unit] ?? false){ + self.prepare(rewardUnit: unit, isTesting: isTesting, hideTestLabel: self.hideTestLabels[unit]); + } + if !wait{ completion?(unit, nil, false); } return; } - + self.show(unit: unit, force: force, needToWait: wait, isTesting: isTesting, viewController: viewController, completion: rewardCompletion); }