What is the best way to add ads to a SwiftUI Grid?

Viewed 341

Hello I want to add ads to a swiftUI grid. The grid contains pictures that I get from a firebase backend and after every couple of pictures I would like to have an ad.

I am quite new to both SwiftUi and working with ads, so I'm not sure how correct my code is, but here is what I got so far.

// Code for the pictures Grid
struct PicturesGrid: View {
private let data: [Item]

var body: some View {
    let gridItems = [GridItem(.fixed(UIScreen.screenWidth / 2),
                              alignment: .leading),
                     GridItem(.fixed(UIScreen.screenWidth / 2),
                              alignment: .leading)]
    
    return ScrollView(showsIndicators: false) {
        LazyVGrid(columns: gridItems) {
            ForEach(0..<self.data.count, id: \.self) { index in
                
                // Using this workaround for the ad to be on the whole width of the screen
                // Also, after every six images I am adding and ad
                if index != 0, index % 6 == 0 {
                    AdView()
                        .frame(width: UIScreen.screenWidth, height: 280)
                        .padding(.top, 20)
                    Spacer()
                    
                    item
                        .frame(width: UIScreen.screenWidth / 2)
                } else {
                    item
                        .frame(width: UIScreen.screenWidth / 2)
                    
                }
            }
        }
    }
}

// this is for the picture
var item: some View {
    NavigationLink(destination: DetailView(viewModel: DetailViewModel(item: itemAtIndexPath))) {
        Cell(viewModel: CellViewModel(item: itemAtIndexPath))
    }
    .buttonStyle(PlainButtonStyle())
}
}

This is the code that I am currently using to load, create and display an ad

// Code for the ad that I am currently using
struct AdView: UIViewControllerRepresentable {

func makeUIViewController(context: Context) -> UIViewController {
    let adController = AdViewController(self)
    
    return adController
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

class AdViewController: UIViewController {
private var adView: AdView

/// The height constraint applied to the ad view, where necessary.
var heightConstraint: NSLayoutConstraint?

/// The ad loader. You must keep a strong reference to the GADAdLoader during the ad loading
/// process.
var adLoader: GADAdLoader!

/// The native ad view that is being presented.
var nativeAdView: GADUnifiedNativeAdView!

/// The ad unit ID.
let adUnitID = "ca-app-pub-3940256099942544/3986624511"

init(_ adView: AdView) {
    self.adView = adView
    
    super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    var nibView: Any?
    nibView = Bundle.main.loadNibNamed("ListAdView", owner: nil, options: nil)?.first
    
    guard let nativeAdView = nibView as? GADUnifiedNativeAdView else {
        return
    }
    setAdView(nativeAdView)
    
    adLoader = GADAdLoader(adUnitID: adUnitID, rootViewController: self,
                           adTypes: [.unifiedNative], options: nil)
    
    
    
    adLoader.delegate = self
    DispatchQueue.global(qos: .background).async {
        self.adLoader.load(GADRequest())
    }
}

func setAdView(_ adView: GADUnifiedNativeAdView) {
    // Remove the previous ad view.
    DispatchQueue.main.async { [weak self] in
        guard let weakSelf = self else {
            return
        }
        weakSelf.nativeAdView = adView
        weakSelf.view.addSubview(weakSelf.nativeAdView)
        weakSelf.nativeAdView.translatesAutoresizingMaskIntoConstraints = false
        
        // Layout constraints for positioning the native ad view to stretch the entire width and height
        let viewDictionary = ["_nativeAdView": weakSelf.nativeAdView!]
        weakSelf.view.addConstraints(
            NSLayoutConstraint.constraints(
                withVisualFormat: "H:|[_nativeAdView]|",
                options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: viewDictionary)
        )
        weakSelf.view.addConstraints(
            NSLayoutConstraint.constraints(
                withVisualFormat: "V:|[_nativeAdView]|",
                options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: viewDictionary)
        )
    }
}
}

extension AdViewController: GADUnifiedNativeAdLoaderDelegate {
    func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: 

GADRequestError) {
    
    print("didFailToReceiveAdWithError: \(error)")
}

func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADUnifiedNativeAd) {
    
    print("Received unified native ad: \(nativeAd)")
    // Deactivate the height constraint that was set when the previous video ad loaded.
    heightConstraint?.isActive = false
    
    // Populate the native ad view with the native ad assets.
    // The headline and mediaContent are guaranteed to be present in every native ad.
    (nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline
    nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent
    
    // This app uses a fixed width for the GADMediaView and changes its height to match the aspect
    // ratio of the media it displays.
    if let mediaView = nativeAdView.mediaView, nativeAd.mediaContent.aspectRatio > 0 {
        heightConstraint = NSLayoutConstraint(
            item: mediaView,
            attribute: .height,
            relatedBy: .equal,
            toItem: mediaView,
            attribute: .width,
            multiplier: CGFloat(1 / nativeAd.mediaContent.aspectRatio),
            constant: 0)
        heightConstraint?.isActive = true
    }
    
    // This asset is not guaranteed to be present. Check that it is before
    // showing or hiding it.
    (nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser
    nativeAdView.advertiserView?.isHidden = nativeAd.advertiser == nil
    
    // In order for the SDK to process touch events properly, user interaction should be disabled.
    nativeAdView.callToActionView?.isUserInteractionEnabled = false
    
    // Associate the native ad view with the native ad object. This is
    // required to make the ad clickable.
    // Note: this should always be done after populating the ad views.
    nativeAdView.nativeAd = nativeAd
}
}

I want to mention that this is working at the moment, but the problems that I want to fix and I don't know how are:

  1. The grid with the pictures load, but when I scroll over an ad, it takes several seconds for the ad to load and display. How could I at least hide it while it loads or make it faster?
  2. If I scroll over an ad, the ad loads and if I continue scrolling, when I scroll back up, the ad is not loaded anymore and I have to wait for it to load again. How can I fix this? Or what is the best practice for this kind of scenario?
  3. Should I use multipleAds? To load them before showing? If yes, then how should I do this?
  4. Does what I am doing here look even a little bit correct? Please...I need some help
1 Answers

The Best Way to show ads in SwiftUI Grids is implementing Native Ads in your app to provide personalized ad experience

Related