0

I am having an issue getting my simple UI for an Admob banner to work using swiftUI and view controller.

Controller:

import UIKit
import Foundation
import GoogleMobileAds
import ToastViewSwift


public class AdsScreenViewController: UIViewController, GADBannerViewDelegate {
   
    var auID = ""
   
    init (auID: String){
        self.auID = auID
        super.init(nibName: nil, bundle: nil)
    }
   
   
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
   
    public override func viewDidLoad() {
        super.viewDidLoad()
       
        var bannerView: GADBannerView!
       
        bannerView = GADBannerView(adSize: kGADAdSizeBanner)

        addBannerViewToView(bannerView)

        bannerView.adUnitID = auID
        bannerView.rootViewController = self

        bannerView.load(GADRequest())

        bannerView.delegate = self

       
    }

    func addBannerViewToView(_ bannerView: GADBannerView) {
    bannerView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(bannerView)
    view.addConstraints(
      [NSLayoutConstraint(item: bannerView,
                          attribute: .bottom,
                          relatedBy: .equal,
                          toItem: bottomLayoutGuide,
                          attribute: .top,
                          multiplier: 1,
                          constant: 0),
       NSLayoutConstraint(item: bannerView,
                          attribute: .centerX,
                          relatedBy: .equal,
                          toItem: view,
                          attribute: .centerX,
                          multiplier: 1,
                          constant: 0)
      ])
   }

public func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
  print("bannerViewDidReceiveAd")
}

public func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
  print("bannerView:didFailToReceiveAdWithError: \(error.localizedDescription)")
}

public func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
  print("bannerViewDidRecordImpression")
}

public func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
  print("bannerViewWillPresentScreen")
}

public func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
  print("bannerViewWillDIsmissScreen")
}

public func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
  print("bannerViewDidDismissScreen")
}

}

Swift UI:

import SwiftUI
import UIKit


struct TestAdsView: View {
    @State private var auID = ""
    @State private var auType = 1
    @State private var isPresented = false

    var body: some View {
        List {
            VStack(alignment: .leading, content: {
                Text("AdUnit")
                    .font(.footnote).fontWeight(.medium)
                TextField("adunitid", text: $auID)
                    .font(.headline)
            })
            VStack(alignment: .leading, content: {
                    Button(action: {
                        self.auID = auID
                    }, label: {
                        HStack {
                            Text("Show Ad")
                        }
                    })
       
        BannerViewController(auID: auID)
 
    }
   )}
}


struct TestAdsView_Previews: PreviewProvider {
    static var previews: some View {
        TestAdsView()
    }
}

struct BannerViewController: UIViewControllerRepresentable {
    var auID: String
   
    public typealias UIViewControllerType = UIViewController
   
    func makeUIViewController(context: UIViewControllerRepresentableContext<BannerViewController>) -> BannerViewController.UIViewControllerType {
   
        return AdsScreenViewController(auID: auID)

    }

    func updateUIViewController(_ uiViewController: BannerViewController.UIViewControllerType, context: UIViewControllerRepresentableContext<BannerViewController>) {
       
        let controller = AdsScreenViewController(auID: auID)

        controller.auID = self.auID
    }
}

Everything compiles fine and it runs showing the TextView. But when entering the id it's not invoking the bannerView.load, I thought the UIViewControllerRepresentable auto updates like a listener on the view and it should be invoked but nothing is happening.

9
  • stackoverflow.com/… Commented Nov 12, 2021 at 12:23
  • @loremipsum been trying for two days now, trying to get it to work with Admob callbacks as well. Tried most of those links and got here. Not massively familiar with ios development, just need some guidance where this is going wrong. Thanks Commented Nov 12, 2021 at 12:48
  • Why would it? What part of your code is supposed to call load when the id is entered? As of now it is only called when viewDidLoad so you call it once with the id is blank. Commented Nov 12, 2021 at 13:52
  • Also, you shouldn't be trying to replace the controller on updateUIViewController you make once and update or call methods the rest of the times. There are many examples in SO on how to implement google ads. Commented Nov 12, 2021 at 14:00
  • if you replace var auID: String with @Binding var auID: String in BannerViewController you might get it to work but you will likely get flagged by Google because you will be recreating the ad too many times. Because of let controller = AdsScreenViewController(auID: auID) on updateUIViewController Commented Nov 12, 2021 at 14:02

1 Answer 1

1

Here is a little sample. Like I said I don't have Google Ads in anything right now but it should be straightforward

import SwiftUI
struct TestAdsView: View {
    @StateObject var vm: AdsScreenViewModel = AdsScreenViewModel()
    var body: some View {
        VStack{
            Text(vm.adStatus.rawValue)
            TextField("adId", text: $vm.adUnitId)
            Button("load Ads", action: {
                vm.loadAds()
            })
            //You might have to play with the position of this.
            AdsScreenView_UI(viewModel: vm).frame(width: 0, height: 0)
        }
    }
}
//This is the source of truth the user input will be held here
class AdsScreenViewModel: ObservableObject, MyAdsViewModelProtocol{
    ///reference to UIKit
    var uiViewController: MyAdsViewControllerProtocol? = nil
    
    @Published var adUnitId: String = ""
    @Published var adStatus: AdStatus = .unknown
    //MARK: MyAdsViewControllerProtocol
    func loadAds() {
        print(#function)
        uiViewController?.loadAds()
    }
    
    func setAdStatus(adStatus: AdStatus) {
        print(#function)
        self.adStatus = adStatus
    }
    func getAdId() -> String {
        print(#function)
        return adUnitId
    }
    
}
struct AdsScreenView_UI: UIViewControllerRepresentable{
    @ObservedObject var viewModel: AdsScreenViewModel
    func makeUIViewController(context: Context) -> some AdsScreenViewController {
        print(#function)
        return AdsScreenViewController(viewModel: viewModel)
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print(#function)
    }
}
//This can mirror the google sample
class AdsScreenViewController: UIViewController, MyAdsViewControllerProtocol {
    ///SwiftUI Delegate
    var viewModel: MyAdsViewModelProtocol
    
    init(viewModel: MyAdsViewModelProtocol) {
        print(#function)
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: .main)
        //Link between UIKit and SwiftUI
        self.viewModel.uiViewController = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        print(#function)
        viewModel.setAdStatus(adStatus: .initialized)
        //Put the current code you have here
    }
    //MARK: MyAdsViewModelProtocol
    func loadAds() {
        print(#function)
        print("ad id \(viewModel.getAdId())")
        viewModel.setAdStatus(adStatus: .loading)
        //You would load here not in viewDidLoad
    }
}
//Protocols aren't needed but it makes the code reusable and you can see the connection protocol = interface
protocol MyAdsViewModelProtocol{
    ///Reference to the google view controller
    var uiViewController: MyAdsViewControllerProtocol? { get set }
    
    ///Tells the viewController to load the ad
    func loadAds()
    ///Retrieves the AdId
    func getAdId() -> String
    ///Sets the Ad Status
    func setAdStatus(adStatus: AdStatus)
}
protocol MyAdsViewControllerProtocol: UIViewController{
    ///Reference to the SwiftUI ViewModel
    var viewModel: MyAdsViewModelProtocol { get set }
    ///Tells Google to load the ad
    func loadAds()
}
enum AdStatus: String{
    case initialized
    case loading
    case unknown
    case error
}

struct TestAdsView_Previews: PreviewProvider {
    static var previews: some View {
        TestAdsView()
    }
}

enter image description here

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you I will give this a go. Just really ill at the moment. Will likely be tomorrow on first attempt. Really appreciate it
For some reason the uiViewController?.loadAds() isn't being called in the AdsScreenViewModel class. So all that is being printed is the loadAds function from the model class.
@Sam Did u change the code? In the view controller init you have to set the uiViewController variable in the view model. Look at the comment in my code where it says “Link between UIKit and SwiftUI”
@Sam I posted a screenshot of my console after typing Abc in the textField and pressing the button to load the ads.
Thank you so much. My bad I didn't include the AdsScreenViewUI into the swift ui. I was writing it out whilst looking not copying and pasting to understand it. Just need to work out how to seperate into seperate files now and add the adtype which I will pass into your view model. Thank you again so much.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.