in Mobile, SwiftUI

How To Use MoPub Banner Ads in SwiftUI

Here’s some example code and a Demo app showing how to use implement MoPub banner ads inside SwiftUI apps.

MoPub doesn’t currently provide support for SwiftUI, so I put together this demo app: MoPub SwiftUI Demo.

Whenever we need to incorporate a view not currently covered in SwiftUI, such as a banner ad from the MoPub SDK, we’ll need to create a UIViewRepresentable. There. are a few steps to this, but once you get the hang of it, it gets fairly easy to replicate.

Additionally, once we have our Representable set up, we’ll be able to drop our MoPubBannerView anywhere in our SwiftUI environment.

Follow along for a more detailed explanation on integrating MoPub Banner Ads in SwiftUI

Step 0:

You will need to integrate the SDK into your own app using MoPub’s Documentation.

Step 1. Initialize MoPub SDK

This step is basically the same as the docs, except you may need to first add an AppDelegate to your SwiftUI app.

import SwiftUI
import MoPubSDK

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        //Initialize Mopub
        let sdkConfig = MPMoPubConfiguration(adUnitIdForAppInitialization: "0ac59b0996d947309c33f59d6676399f")
        sdkConfig.loggingLevel = .info
        MoPub.sharedInstance().initializeSdk(with: sdkConfig)
        
        return true
    }
}

@main
struct MoPubSwiftUIDemoApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Step 2: Create a SwiftUI UIViewRepresentable

In order to use custom views, or native UIViews that do not yet have a SwiftUI equivalent, we’ll need to create a UIViewRepresentable. This is where the bulk of the work comes in to play. I’ll break down some of the important parts here, and the full code is at the bottom:

2a: makeUIView

This is where we do the MPAdView creation. The adSize must contain the correct dimensions of the ad to be loaded, such as height:320, width:50. While the sdk offers ad size constants, such as kMPPresetMaxAdSize50Height, I found that these have the height set as -1 for some reason. This results in the sdk not loading any ad here. The SDK also doesn’t play nice with how SwiftUI manages the parent view’s frame, so kMPPresetMaxAdSizeMatchFrame doesn’t work either.

    func makeUIView(context: Context) -> MPAdView {
        
        let moPubBannerView = type(of: MPAdView()).init(adUnitId:adUnitID)!
        
        //adSize needs to be defined with the exact dimensions of the desired ad, ex: 320x50.
        //Only defining the height using  something like kMPPresetMaxAdSize50Height will define a width of 0, causing the ad not to load
        moPubBannerView.frame.size = adSize
        
        moPubBannerView.delegate = context.coordinator
                        
        moPubBannerView.loadAd()
        
        return moPubBannerView
    }

2b: The delegate

In makeUIView above, the the delegate is passed through context.coordinator. We actually set up the Delegate on the Coordinator class. This also allows us to listen for any events.

    class Coordinator: NSObject, UINavigationControllerDelegate, MPAdViewDelegate {
        
        private let parent: MoPubBannerViewRepresentable
        
        init(_ mopubView: MoPubBannerViewRepresentable) {
            self.parent = mopubView
        }
        
        func viewControllerForPresentingModalView() -> UIViewController! {
            return  UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
        }
        
        //Events from MPAdViewDelegate can go here
        //These can be removed if not used
        func adViewDidLoadAd(_ view: MPAdView!, adSize: CGSize) {
            print("ad loaded with size: \(adSize)")
        }
        
        func adView(_ view: MPAdView!, didFailToLoadAdWithError error: Error!) {
            if let error = error {
                print("failed to load ad with error: \(error)")
            }
        }
        
        func adViewDidFail(toLoadAd view: MPAdView!) {
            print("failed to load ad")
        }
        
    }

2c: Set up the SwiftUI view

For UIViewRepresentable implementations, I like to set up a mini View to serve as an API of sorts between our Representable implementation and the rest of our app code. This allows for setting a frame to match the banner size, and anything else you need to do on all banner views. Alternatively, you could add MoPubBannerViewRepresentable directly to your View.

struct MoPubBannerView: View {
    let adUnitID: String
    let adSize: CGSize
    
    var body: some View {
        
        MoPubBannerViewRepresentable(adUnitID: adUnitID, adSize: adSize)
            .frame(width: adSize.width, height: adSize.height)
    }
}

Step 3: Add to our content View

All we need to do now is drop the BannerView into our view, passing it an adUnitId and the ad size. Here’s a sample banner using the MoPub Test Ad Ids:

struct ContentView: View {
    var body: some View {
        MoPubBannerView(adUnitID: "0ac59b0996d947309c33f59d6676399f", adSize: CGSize(width: 320, height: 50))
    }
}

Full Code

To see a working app with more examples, check out my MoPub SwiftUI Demo App. The full UIViewRepresentable code is below

//
//  MoPubBannerView.swift
//  MoPubSwiftUIDemo
//
//  Created by Adam Paxton on 3/10/21.
//

import MoPubSDK
import SwiftUI
import UIKit

struct MoPubBannerView: View {
    let adUnitID: String
    let adSize: CGSize
    
    var body: some View {
        
        MoPubBannerViewRepresentable(adUnitID: adUnitID, adSize: adSize)
            .frame(width: adSize.width, height: adSize.height)
    }
}

struct MoPubBannerViewRepresentable: UIViewRepresentable {
    let adUnitID: String
    let adSize: CGSize
    
    
    func makeUIView(context: Context) -> MPAdView {
        
        let moPubBannerView = type(of: MPAdView()).init(adUnitId:adUnitID)!
        
        //adSize needs to be defined with the exact dimensions of the desired ad, ex: 320x50.
        //Only defining the height using  something like kMPPresetMaxAdSize50Height will define a width of 0, causing the ad not to load
        moPubBannerView.frame.size = adSize
        
        moPubBannerView.delegate = context.coordinator
                        
        moPubBannerView.loadAd()
        
        return moPubBannerView
    }
    
    func updateUIView(_ uiView: MPAdView, context: Context) { }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UINavigationControllerDelegate, MPAdViewDelegate {
        
        private let parent: MoPubBannerViewRepresentable
        
        init(_ mopubView: MoPubBannerViewRepresentable) {
            self.parent = mopubView
        }
        
        func viewControllerForPresentingModalView() -> UIViewController! {
            return  UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
        }
        
        //Events from MPAdViewDelegate can go here
        //These can be removed if not used
        func adViewDidLoadAd(_ view: MPAdView!, adSize: CGSize) {
            print("ad loaded with size: \(adSize)")
        }
        
        func adView(_ view: MPAdView!, didFailToLoadAdWithError error: Error!) {
            if let error = error {
                print("failed to load ad with error: \(error)")
            }
        }
        
        func adViewDidFail(toLoadAd view: MPAdView!) {
            print("failed to load ad")
        }
        
    }
}

https://github.com/adampax/MoPubSwiftUIDemo/blob/main/MoPubSwiftUIDemo/MoPubBannerView.swift