Admob rewarded ad won't load - swift

I have build a SwiftUI .sheet() that appears so the user can see their statistics and can click on a button that then will show a rewarded video to give the user some extra points. The code is as follows:
import SwiftUI
import GoogleMobileAds
struct Scoresheet: View {
#State private var rewardedadd = ViewController()
var body: some View {
VStack {
// all sorts of content.
// if a user clicks on a specific button it will call func loadadd()
}
.onAppear {
rewardedadd.loadRewardedAd()
}
}
func loadadd() {
rewardedadd.show()
}
}
In another file I have the following:
import UIKit
import GoogleMobileAds
class ViewController: UIViewController, GADFullScreenContentDelegate {
var rewardedAd: GADRewardedAd?
func loadRewardedAd() {
let request = GADRequest()
GADRewardedAd.load(withAdUnitID:"ca-app-pub-3940256099942544/1712485313",
request: request,
completionHandler: { [self] ad, error in
if let error = error {
print("Failed to load rewarded ad with error: \(error.localizedDescription)")
return
}
rewardedAd = ad
print("Rewarded ad loaded.")
rewardedAd?.fullScreenContentDelegate = self
})
}
func show() {
if let ad = rewardedAd {
ad.present(fromRootViewController: self) {
let reward = ad.adReward
print("Reward received with currency \(reward.amount), amount \(reward.amount.doubleValue)")
// TODO: Reward the user.
}
} else {
print("Ad wasn't ready")
}
}
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print("Ad did fail to present full screen content. Error: \(error.localizedDescription)")
}
func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
print("Ad will present full screen content.")
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
print("Ad did dismiss full screen content.")
}
}
I keep getting the following error:
Ad did fail to present full screen content. Error: The provided view controller is not being presented.
That is the error from func ad in class ViewController. Does anyone know what I can do to fix this issue?
---- edit ----
I have rebuild my viewcontroller class to the following:
import SwiftUI
import GoogleMobileAds
struct RewardedAd: UIViewRepresentable {
#State private var rewardedAd: GADRewardedAd
let adUnitId = "ca-app-pub-3940256099942544/1712485313"
func makeCoordinator() -> CoordinatorVideoAd {
return CoordinatorVideoAd()
}
func makeUIView(context: Context) -> GADRewardedAd {
let request = GADRequest()
rewardedAd.load(withAdUnitID: adUnitId,
request: request, completionHandler: { [self] ad, error in
if let error = error {
print("Failed to load rewarded ad with error: \(error.localizedDescription)")
return
}
rewardedAd = ad
print("Rewarded ad loaded.")
rewardedAd?.fullScreenContentDelegate = self
})
return rewardedAd
}
func updateUIView(_ uiView: GADRewardedAd, context: Context) { }
class CoordinatorVideoAd: NSObject, GADFullScreenContentDelegate {
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print("Ad did fail to present full screen content.")
}
func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
print("Ad will present full screen content.")
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
print("Ad did dismiss full screen content.")
}
}
}
I new have two error's:
Type 'RewardedAd' does not conform to protocol 'UIViewRepresentable'
Static member 'load' cannot be used on instance of type 'GADRewardedAd'
How can I fix this?

I believe that's now how you present a UIKit component to a swiftUI. You should read it in the apple docs about how to do it.
Here's an example how to do it :
First, create a struct that inherit the UIViewControllerRepresentable protocol
it'll look like this
struct SomethingViewController: UIViewControllerRepresentable {
typealias UIViewControllerType = [enter your UIViewController type]
}
add the type, for example, here i;m creating a FakeViewController
typealias UIViewControllerType = FakeViewController
it'll generate the nescessary method
Last, just customize it whahtever you want
struct SomethingViewController: UIViewControllerRepresentable {
#Binding var colorIDX: Int
let colors: [UIColor] = [.red,.blue,.green]
func makeUIViewController(context: Context) -> FakeViewController {
//Construct your ViewController
let viewController = FakeViewController()
viewController.bgColor = .systemPink
return viewController
}
func updateUIViewController(_ uiViewController: FakeViewController, context: Context) {
// Update your ViewController
uiViewController.view.backgroundColor = colors[colorIDX]
}
}
and setup in the SwiftUIView to look like this (depending on how do you want it, navigate to UIViewController or present it)
struct ContentView: View {
#State var isShow: Bool = false
#State var colorIDX: Int = 0
var body: some View {
VStack {
Button("show") {
if colorIDX >= 2 {
colorIDX = 0
} else {
colorIDX += 1
}
isShow.toggle()
}
}.sheet(isPresented: $isShow) {
} content: {
SomethingViewController(colorIDX: $colorIDX)
}
}
}
you see, every time i update something in the swiftUI View, it updates my ViewController and everytime I push the button, it'll present the ViewController
if you want to use a navigationController, just use it like any other swiftUI component/views
And that;s how you use a UIViewController in SwiftUI

Related

Interstitial ads pops up after every X seconds

can anyone please help me with this. How to show interstitial ads pop out after every x seconds and the ad will run only for few seconds and go back to the Main Screen.
Currently I am using this:
This is the Banner Ad:
import GoogleMobileAds
import SwiftUI
import UIKit
struct GADBannerViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let adSize = GADAdSizeFromCGSize(CGSize(width: 320, height: 100))
let view = GADBannerView(adSize: adSize)
let viewController = UIViewController()
view.adUnitID = "ca-app-pub-3940256099942544/2934735716"
view.rootViewController = viewController
viewController.view.addSubview(view)
viewController.view.frame = CGRect(origin: .zero, size: adSize.size)
view.load(GADRequest())
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
This is the Interstitial Ad:
import GoogleMobileAds
import SwiftUI
import UIKit
final class Interstitial:NSObject, GADFullScreenContentDelegate {
var interstitial:GADInterstitialAd?
override init() {
super.init()
self.loadInterstitial()
}
func loadInterstitial(){
let request = GADRequest()
GADInterstitialAd.load(withAdUnitID:"ca-app-pub-3940256099942544/4411468910",
request: request,
completionHandler: { [self] ad, error in
if let error = error {
print("Failed to load interstitial ad with error: \(error.localizedDescription)")
return
}
interstitial = ad
interstitial?.fullScreenContentDelegate = self
})
}
func showAd(){
if self.interstitial != nil {
let root = UIApplication.shared.windows.first?.rootViewController
self.interstitial?.present(fromRootViewController: root!)
}
else{
print("Not Ready")
}
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
print("Ad did dismiss full screen content.")
self.loadInterstitial()
}
}
And this is how I am currently implementing in my View.
import SwiftUI
import GoogleMobileAds
struct AdvertView: View {
private var fullScreenAd: Interstitial?
init() {
fullScreenAd = Interstitial()
}
var body: some View {
let adSize = GADAdSizeFromCGSize(CGSize(width: 320, height: 100))
NavigationView {
Form {
Button("test", action: {
self.fullScreenAd?.showAd()
})
//Banner
HStack {
Spacer()
GADBannerViewController()
.frame(width: adSize.size.width, height: adSize.size.height)
Spacer()
}
}
.navigationBarTitle("Ad Test")
}
}
}
So, when I press the Test Button, the ad changes to Interstitial ad.
I want to appear the ads both Banner and Interstitial on different screens after certain timings without using the buttons. Can anyone please help me with this?

Google Admob Interstitial shows the following error "The provided view controller is not being presented" in SwiftUI

I followed one of the tutorial and tried to use a button to show interstitial ad in my SwiftUI app but it keeps showing this error in logs.
What exactly is the fix since Admob tutorial for SwiftUI is extremely limited at the moment?
I have hooked it up to a button in content view and it refuses to show the ad...
final class InterstitialAd: NSObject {
var interstitialAd: GADInterstitialAd?
static let shared = InterstitialAd()
func loadAd(withAdUnitId id: String) {
let req = GADRequest()
GADInterstitialAd.load(withAdUnitID: id, request: req) { interstitialAd, err in
if let err = err {
print("Failed to load ad with error: \(err)")
return
}
self.interstitialAd = interstitialAd
}
}
}
final class InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {
//Here's the Ad Object we just created
let interstitialAd = InterstitialAd.shared.interstitialAd
#Binding var isPresented: Bool
var adUnitId: String
init(isPresented: Binding<Bool>, adUnitId: String) {
self._isPresented = isPresented
self.adUnitId = adUnitId
super.init()
interstitialAd?.fullScreenContentDelegate = self //Set this view as the delegate for the ad
}
//Make's a SwiftUI View from a UIViewController
func makeUIViewController(context: Context) -> UIViewController {
let view = UIViewController()
//Show the ad after a slight delay to ensure the ad is loaded and ready to present
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
self.showAd(from: view)
}
return view
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
//Presents the ad if it can, otherwise dismisses so the user's experience is not interrupted
func showAd(from root: UIViewController) {
if let ad = interstitialAd {
ad.present(fromRootViewController: root)
} else {
print("Ad not ready")
self.isPresented.toggle()
}
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
//Prepares another ad for the next time view presented
InterstitialAd.shared.loadAd(withAdUnitId: "ca-app-pub-3940256099942544/4411468910")
//Dismisses the view once ad dismissed
isPresented.toggle()
}
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print(error.localizedDescription)
}
}
struct FullScreenModifier<Parent: View>: View {
#Binding var isPresented: Bool
#State var adType: AdType
//Select adType
enum AdType {
case interstitial
case rewarded
}
var rewardFunc: () -> Void
var adUnitId: String
//The parent is the view that you are presenting over
//Think of this as your presenting view controller
var parent: Parent
var body: some View {
ZStack {
parent
if isPresented {
EmptyView()
.edgesIgnoringSafeArea(.all)
if adType == .rewarded {
} else if adType == .interstitial {
InterstitialAdView(isPresented: $isPresented, adUnitId: adUnitId)
}
}
}
.onAppear {
//Initialize the ads as soon as the view appears
if adType == .rewarded {
} else if adType == .interstitial {
InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
}
}
}
}
extension View {
public func presentRewardedAd(isPresented: Binding<Bool>, adUnitId: String, rewardFunc: #escaping (() -> Void)) -> some View {
FullScreenModifier(isPresented: isPresented, adType: .rewarded, rewardFunc: rewardFunc, adUnitId: adUnitId, parent: self)
}
public func presentInterstitialAd(isPresented: Binding<Bool>, adUnitId: String) -> some View {
FullScreenModifier(isPresented: isPresented, adType: .interstitial, rewardFunc: {}, adUnitId: adUnitId, parent: self)
}
}

How to authenticate with the Box SDK using SwiftUI?

I have been having a hard time trying to figure out how to authenticate with the Box API using SwiftUI.
As far as I understand, SwiftUI does not currently have the ability to satisfy the ASWebAuthenticationPresentationContextProviding protocol required to show the Safari OAuth2 login sheet. I know that I can make a UIViewControllerRepresentable to use UIKit within SwiftUI, but I can't get this to work.
I have figured out how to get the OAuth2 login sheet for Dropbox to appear and authenticate the client using SwiftUI.
The trick is to use a Coordinator to make the UIViewControllerRepresentable satisfy a protocol.
import SwiftUI
import BoxSDK
import AuthenticationServices
var boxSDK = BoxSDK(clientId: "<Client ID>", clientSecret: "<Client Secret>")
var boxClient: BoxClient
struct BoxLoginView: View {
#State var showLogin = false
var body: some View {
VStack {
Button {
showLogin = true
} label: {
Text("Login")
}
BoxView(isShown: $showLogin)
// Arbitrary frame size so that this view does not take up the whole screen
.frame(width: 40, height: 40)
}
}
}
/// A UIViewController that will present the OAuth2 Safari login screen when the isShown is true.
struct BoxView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController
let letsView = UIViewController()
#Binding var isShown : Bool
// Show the login Safari window when isShown
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if(isShown) {
getOAuthClient()
}
}
func makeUIViewController(context _: Self.Context) -> UIViewController {
return self.letsView
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func getOAuthClient() {
boxSDK.getOAuth2Client(tokenStore: KeychainTokenStore(), context:self.makeCoordinator()) { result in
switch result {
case let .success(client):
boxClient = client
case let .failure(error):
print("error in getOAuth2Client: \(error)")
}
}
}
class Coordinator: NSObject, ASWebAuthenticationPresentationContextProviding {
var parent: BoxView
init(parent: BoxView) {
self.parent = parent
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return parent.letsView.view.window ?? ASPresentationAnchor()
}
}
}

View won't show when calling by function in SwiftUI

I have this problem. I'm trying to show Ads in my app using AdMob. It all works fine (i did some debug) but the view containing the video is not showing... I can't figure out why it's not showing on the top of my content.
Here is the the class that calls and opens the view with ads:
import SwiftUI
import GoogleMobileAds
import UIKit
class Rewarded: NSObject, GADRewardedAdDelegate, ObservableObject{
#Published var adLoaded: Bool = false
var rewardedAd:GADRewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
var rewardFunction: (() -> Void)? = nil
override init() {
super.init()
LoadRewarded()
}
func LoadRewarded(){
let req = GADRequest()
self.rewardedAd.load(req) { error in
if error != nil {
self.adLoaded = false
print("Ad not loaded")
} else {
self.adLoaded = true
print("Ad loaded")
}
}
}
func showAd(rewardFunction: #escaping () -> Void){
if self.rewardedAd.isReady {
print("Ad is ready to show")
self.rewardFunction = rewardFunction
if let root = UIApplication.shared.windows.first?.rootViewController {
self.rewardedAd.present(fromRootViewController: root, delegate: self)
print("Presenting")
} else {
print("Failed to open ad")
}
} else {
print("Ad not ready to show")
}
}
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
if let rf = rewardFunction {
rf()
}
}
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {
self.rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
LoadRewarded()
}
}
And here is the SwiftUI portion of the code that calls the function of the class:
Button {
self.rewardAd.showAd(rewardFunction: {
// GIVE REWARD HERE
})
} label: {
Text("Watch Ad")
.foregroundColor(Color.white)
.font(.custom(customFont, size: 17))
.fontWeight(.bold)
}
Also the console doesn't print any error...
Does anybody know why it is not showing?
Thank you all
This works for me. I had a heck of a time getting this to work and tried following all of the examples I could find, finally landed on this.
func showAd(rewardFunction: #escaping () -> Void){
if let ad = rewardedAd {
self.rewardFunction = rewardFunction
if let root = UIApplication.shared.windows.first?.rootViewController {
ad.present(fromRootViewController: root, userDidEarnRewardHandler: {
let reward = ad.adReward
// reward the user
})
}
else {
print("Error presenting ad")
}
}
else{
print("Ad Not Ready")
}
}

How do I use the Google Mobile Ads SDK in SwiftUI, or use the UIKit UIViewController within a SwiftUI view?

I have a SwiftUI view that I want to open a rewarded ad from the Google Mobile Ads SDK when I press a button. The instructions for loading the ads (https://developers.google.com/admob/ios/rewarded-ads#create_rewarded_ad) are in UIKit, and I'm struggling to use them in my SwiftUI app. Is there a way to load the ads using SwiftUI, or if I use UIKit, how do I integrate it into SwiftUI?
This is the SwiftUI parent view:
struct AdMenu: View {
var body: some View {
NavigationView {
NavigationLink(destination: Ads())
{
Text("Watch Ad")
}
}
}
}
I don't know UIKit, but I think this is the code I want to use in SwiftUI:
class ViewController: UIViewController, GADRewardedAdDelegate {
var rewardedAd: GADRewardedAd?
var adRequestInProgress = false
#IBAction func doSomething(sender: UIButton) {
if rewardedAd?.isReady == true {
rewardedAd?.present(fromRootViewController: self, delegate:self)
}else {
let alert = UIAlertController(
title: "Rewarded video not ready",
message: "The rewarded video didn't finish loading or failed to load",
preferredStyle: .alert)
let alertAction = UIAlertAction(
title: "OK",
style: .cancel,
handler: { [weak self] action in
// redirect to AdMenu SwiftUI view somehow?
})
alert.addAction(alertAction)
self.present(alert, animated: true, completion: nil)
}
}
func createAndLoadRewardedAd() {
rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
adRequestInProgress = true
rewardedAd?.load(GADRequest()) { error in
self.adRequestInProgress = false
if let error = error {
print("Loading failed: \(error)")
} else {
print("Loading Succeeded")
}
}
return rewardedAd
}
// Tells the delegate that the user earned a reward
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
print("Reward received with currency: \(reward.type), amount \(reward.amount).")
}
// Tells the delegate that the rewarded ad was presented
func rewardedAdDidPresent(_ rewardedAd: GADRewardedAd) {
print("Rewarded ad presented.")
}
// Tells the delegate that the rewarded ad was dismissed
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {
print("Rewarded ad dismissed.")
}
// Tells the delegate that the rewarded ad failed to present
func rewardedAd(_ rewardedAd: GADRewardedAd, didFailToPresentWithError error: Error) {
rewardedAd = createAndLoadRewardedAd()
print("Rewarded ad failed to present.")
}
override func viewDidLoad() {
super.viewDidLoad()
if !adRequestInProgress && !(rewardedAd?.isReady ?? false) {
rewardedAd = createAndLoadRewardedAd()
}
I have used a very simple approach. I have a delegate class which loads the rewarded Ad and informs the view that its loaded, hence the view presents it. When user watches the full Ad, same delegate gets the success callback and it informs the view about it.
The code for delegate looks like:-
class RewardedAdDelegate: NSObject, GADRewardedAdDelegate, ObservableObject {
#Published var adLoaded: Bool = false
#Published var adFullyWatched: Bool = false
var rewardedAd: GADRewardedAd? = nil
func loadAd() {
rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardedAd!.load(GADRequest()) { error in
if error != nil {
self.adLoaded = false
} else {
self.adLoaded = true
}
}
}
/// Tells the delegate that the user earned a reward.
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
adFullyWatched = true
}
/// Tells the delegate that the rewarded ad was presented.
func rewardedAdDidPresent(_ rewardedAd: GADRewardedAd) {
self.adLoaded = false
}
/// Tells the delegate that the rewarded ad was dismissed.
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {}
/// Tells the delegate that the rewarded ad failed to present.
func rewardedAd(_ rewardedAd: GADRewardedAd, didFailToPresentWithError error: Error) {}
}
Now you need a view to initiate and present the RewardedAd:-
struct RewardedAd: View {
#ObservedObject var adDelegate = RewardedAdDelegate()
var body: some View {
if adDelegate.adLoaded && !adDelegate.adFullyWatched {
let root = UIApplication.shared.windows.first?.rootViewController
self.adDelegate.rewardedAd!.present(fromRootViewController: root!, delegate: adDelegate)
}
return Text("Load ad").onTapGesture {
self.adDelegate.loadAd()
}
}
}
Explanation:-
In the above view when user taps on Load Ad, we initiate the loading and then the delegate updates the Published Boolean. This informs our view that ad is Loaded and we call:-
let root = UIApplication.shared.windows.first?.rootViewController
self.adDelegate.rewardedAd!.present(fromRootViewController: root!, delegate: adDelegate)
The Ad is now playing on screen and if user watches is completely, the delegate will get a success callback and it'll update another Published Boolean. This will inform us that we need to reward the user now (you can add your way to handle/reward the user).
I figured out a way how to nicely translate the objective-c code from the AdMob page to something you can easily use in SwiftUI.
final class RewardedAd {
private let rewardId = "ca-app-pub-3940256099942544/1712485313" // TODO: replace this with your own Ad ID
var rewardedAd: GADRewardedAd?
init() {
load()
}
func load(){
let request = GADRequest()
// add extras here to the request, for example, for not presonalized Ads
GADRewardedAd.load(withAdUnitID: rewardId, request: request, completionHandler: {rewardedAd, error in
if error != nil {
// loading the rewarded Ad failed :(
return
}
self.rewardedAd = rewardedAd
})
}
func showAd(rewardFunction: #escaping () -> Void) -> Bool {
guard let rewardedAd = rewardedAd else {
return false
}
guard let root = UIApplication.shared.keyWindowPresentedController else {
return false
}
rewardedAd.present(fromRootViewController: root, userDidEarnRewardHandler: rewardFunction)
return true
}
}
I also added an extension to get the root view easier:
extension UIApplication {
var keyWindow: UIWindow? {
return UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first(where: { $0 is UIWindowScene })
.flatMap({ $0 as? UIWindowScene })?.windows
.first(where: \.isKeyWindow)
}
var keyWindowPresentedController: UIViewController? {
var viewController = self.keyWindow?.rootViewController
if let presentedController = viewController as? UITabBarController {
viewController = presentedController.selectedViewController
}
while let presentedController = viewController?.presentedViewController {
if let presentedController = presentedController as? UITabBarController {
viewController = presentedController.selectedViewController
} else {
viewController = presentedController
}
}
return viewController
}
}
If you add that to your project you can just call it like this in your SwiftUI code:
var rewardAd: RewardedAd
init(){
self.rewardAd = RewardedAd()
rewardAd.load()
}
var body: some View {
Button(action: {
self.rewardAd.showAd(rewardFunction: {
// TODO: give the user a reward for watching
self.collectedDiamonds += 1
})
}) {
Text("Watch Ad - Earn 💎")
.foregroundColor(.white)
.padding()
}
.background(Capsule().fill(Color.blue))
}