Monetize your NativeScript apps with AdMob (part 1 - iOS)


The whole world goes mobile, so are the software trends. But what’s the reason to create a mobile app after all? Well, surely, when things get serious, after the “it’s just for tests and fun” phase, it’s all about the money. You can create an app for the employees of your company or for the employees of somebody else (B2E, B2B). In this case your company benefits directly from the services you provide to its employees, or you take a commission for the app you create. But if your customers are the end-users (B2C) or if you create an app for a business customer that will sell it to the masses, then you should surely be aware of the different app monetization methods where two of the main ones are in-app purchases and mobile ads.

Today, we will talk about mobile ads and how you can enable them in your NativeScript application using the Google AdMob service and SDKs. This article will focus on iOS and in the next part we will cover the Android platform.

googleads-bannerview googleads-interstitial


For many scenarios NativeScript is already covered by existing NPM plugins, created even before the NativeScript project was started. And, there are also plugins exclusively written for NativeScript to solve particular native scenarios, where some of them are officially approved by us and listed at the Verified Plugins Marketplace.

The number of plugins is big, but the number of scenarios is always bigger, so inevitably there would be functionality that is not plugin-covered. For those cases a very cool feature of NativeScript comes into play - its ability to directly use the native APIs of an iOS (or Android) library in its JavaScript form, without any pre-built wrappers or else. You can just consult with the documentation of that desired functionality, follow some rules to JavaScript-ize the native code and voila, you are in the game.

So, following the existing technical AdMob documentation, today we will plug ads in our NativeScript app.

You of course need an AdMob account for that. There is a nice walkthrough by our evangelist Jen Looper on using a AdMob account in the first half of her blog: Make it Rain! Monetize Your Mobile App with Cordova Plugins

Technically, we will focus on these two types of ads:
  • Banner ads- this is the view that is displayed usually at the bottom of your app during the whole app lifetime.
  • Interstitial ads - these ads appear at a specified moment(s) of the app lifetime, taking the whole device screen. The end-user can dismiss them by tapping a close button.

CocoaPods integration

The Google Mobile Ads SDK for iOS is distributed either as a simple Framework library file, or via CocoaPods. The CocoaPods is definitely the modern and prefered way to get iOS libraries, so we will use this approach. To make things even easier for you, I created a very simple NPM plugin that just loads the Google Mobile Ads SDK via CocoaPods.

Using the CLI

For our demo purposes, we will start from the default CLI app project template. To create an app from that template, simply execute this command in the terminal:

tns create googleadmobproject

Then, let’s navigate to the main directory of that project:

cd googleadmobproject

and run the following command to install the plugin:

tns plugin add nativescript-google-mobile-ads

This will install the native library at the appropriate place, so that you can start coding on top of it with JavaScript immediately.

Using AppBuilder IDE

Instead of the Command Line Interface tools, you can also use the AppBuilder IDE that Telerik provides for building NativeScript apps. The AppBuilder IDE builds the apps in the Telerik cloud, so actually no Mac is needed for building an iOS app. Since the build happens in the cloud, no setup of additional SDKs and tools is needed. A dedicated blog post will follow about using the AppBuilder with plugins. For now, you can get a free Telerik Platform Trial from here and explore how you can create NativeScript apps following the Getting Started >> Interactive Tutorials link from the top Telerik Platform menu strip.


Using Banner ads

The framework is set, using the CLI or the AppBuilder, now’s coding time and we will start with coding a Banner ad.

First, let’s define our layout. Since our native mobile ads functionality does not have any common JS wrapper code around it, we can use the Placeholder component to directly consume the native view that should appear to display the ads. This Placeholder should be docked at the bottom of the screen with the rest of the contents filling the remaining area, so, let’s put it all in a DockLayout. The result, starting from the default CLI app template would be:

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded">
    <DockLayout>
        <Placeholder dock="bottom" creatingView="creatingView" id="bannerView"/>
        <StackLayout dock="top">
            <Label text="Tap the button" class="title"/>
            <Button text="TAP" tap="{{ tapAction }}" />
            <Label text="{{ message }}" class="message" textWrap="true"/>
        </StackLayout>
    </DockLayout>
</Page>

We can define the height of the Placeholder (hence the banner view) in the XML like that:

<Placeholder dock="bottom" height=”50” creatingView="creatingView" id="bannerView"/>

But, instead, let’s define the height in the main-page.js file using the Google-recommended constants for banner view height. We should do so when we create the native BannerView object using its initWithAdSize initializer. And, all this should happen in the creatingView method which is called as the Placeholder should determine which is the object that it should represent:

function creatingView(args) {
    if(platformModule.device.os == platformModule.platformNames.ios) {
        bannerView = GADBannerView.alloc().initWithAdSize(kGADAdSizeSmartBannerPortrait);
        args.view = bannerView;
    }
    else {}
}
 
exports.creatingView = creatingView;

Further, as you could notice from the XML above, we handle the loaded event of the Page using a pageLoaded method to set some vital to the already created BannerView properties:

function pageLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
 
    var placeholder = page.getViewById("bannerView");
    var bannerView;
 
    if(platformModule.device.os == platformModule.platformNames.ios) {
        bannerView = placeholder.ios;
        bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716";
        bannerView.strongDelegateRef = bannerView.delegate = GADBannerViewDelegateImpl.new();
        bannerView.rootViewController = page.ios;
        var request = GADRequest.request();
        request.testDevices = [kGADSimulatorID];
        bannerView.loadRequest(request);
    }
    else {}
}

These include the adUnitID that you are given to your account (we are using a test one here), the rootViewController (take it as the very main page frame of your app), the device that is taken is considered as a test device (otherwise, there could be a penalty from Google including account suspension) and also the actual request that request an ad to be delivered and displayed in the banner view. 

You can also notice that we are setting a delegate that will enable a few callback method that are called during the ad lifecycle . But we have not created the delegate type that will enable these callbacks. Let’s do it now:

if(platformModule.device.os == platformModule.platformNames.ios) {
    var GADBannerViewDelegateImpl = (function (_super) {
        __extends(GADBannerViewDelegateImpl, _super);
        function GADBannerViewDelegateImpl() {
            _super.apply(this, arguments);
        }
        GADBannerViewDelegateImpl.prototype.adViewWillLeaveApplication = function (bannerView) {
            // do sth as the user is leaving the app, because of a clicked ad
            console.log("Leaving the app, bye bye!");
        };
        GADBannerViewDelegateImpl.ObjCProtocols = [GADBannerViewDelegate];
        return GADBannerViewDelegateImpl;
    })(NSObject);
}

There are as few methods that the delegate supports as described in the AdMob documentation

As you can see, for test purposes we are taking advantage of the adViewWillLeaveApplication which is called when the user leave the app because of a clicked ad.

Well, this was actually it, so nice, so easy. Here is how the app will look and behave:




You can find the complete NativeScript project working with Banner ads at GitHub.

Using Interstitial ads

Interstitial ads… ahh, these ads that appear unexpectedly from bottom to top, as if they are blowing up your app, breaking your end-users’ good feelings and hearts about your app. Like it or not, this ad type is also popular and bring you money if used in the appropriate moment, for example between two game levels.

Let’s now see how we can implement an Interstitial ad. It’s not that different from a banner ad, but we will not include the interstitial view in any XML layout, as the Interstitial will appear using a native method call.

In this demo, we will call the Interstitial on a simple button click, using the provided button that the CLI app template gives.

Let’s create a method for creating the interstitial as we may want to show interstitial a few times during the app lifetime:

function createAndLoadiOSInterstitial() {
    var interstitial = GADInterstitial.alloc().initWithAdUnitID("ca-app-pub-3940256099942544/4411468910");
    var request = GADRequest.request();
    interstitial.strongDelegateRef = interstitial.delegate = frameModule.topmost().currentPage.delegate;
    request.testDevices = [kGADSimulatorID];
    interstitial.loadRequest(request);
 
    return interstitial;
}
 
exports.createAndLoadiOSInterstitial = createAndLoadiOSInterstitial;

Like with the banner view, we are setting AdUnitID, a device to test on, and also a request that request the ad to be displayed. You can also see that we are setting a delegate (which is instantiated in the pageLoaded method) to enable the usage of a few callback methods that are called for the ad lifecycle. Here is how the delegate type should be defined:

if(platformModule.device.os == platformModule.platformNames.ios) {
    var GADInterstitialDelegateImpl = (function (_super) {
        __extends(GADInterstitialDelegateImpl, _super);
        function GADInterstitialDelegateImpl() {
            _super.apply(this, arguments);
        }
        GADInterstitialDelegateImpl.prototype.interstitialDidDismissScreen = function (gadinterstitial) {
            frameModule.topmost().currentPage.interstitial = createAndLoadiOSInterstitial();
        };
        GADInterstitialDelegateImpl.ObjCProtocols = [GADInterstitialDelegate];
        return GADInterstitialDelegateImpl;
    })(NSObject);
}

An important callback method here to notice is the interstitialDidDismissScreen which is called when the user taps the close button of an already displayed interstitial. According to the AdMob docs, the recommended way for requesting an interstitial ad is to do it on dismissing previously displayed interstitial, thus making sure that your Interstitial will always be ready to be displayed.

Well, talking about readiness, let’s load one Interstitial ad on the loaded event of the page, so that it is ready the first time we tap a button:

var frameModule = require("ui/frame");
 
function pageLoaded(args) {
    var page = args.object;
    page.bindingContext = vmModule.mainViewModel;
     
    if(platformModule.device.os == platformModule.platformNames.ios) {
        frameModule.topmost().currentPage.delegate = GADInterstitialDelegateImpl.new();
        frameModule.topmost().currentPage.interstitial = createAndLoadiOSInterstitial();
    }
    else {}
}
 
exports.pageLoaded = pageLoaded;

Note that we are setting the interstitial object returned from createAndLoadiOSInterstitial to a property of the current page. Why are we doing that? First, we need global access to the interstitial object in the main-page. So, we could have defined it globally. Yes, but depending on the scenario, this may lead to potential memory leaks. Therefore, the best approach when you need to have global access is to attach it to a control of that page, or the page itself.

Last, but not least, here is the tap event implementation of the button, where where we display the Interstitial (keep your fingers crossed it’s ready to appear):

function buttonTapped(args) {
    var interstitial = frameModule.topmost().currentPage.interstitial;
 
    if(platformModule.device.os == platformModule.platformNames.ios) {
        if(interstitial.isReady) {
            interstitial.presentFromRootViewController(args.object.page.frame.ios.controller);
        }
    }
    else {}
}
 
exports.buttonTapped = buttonTapped;

And here is the result:



You can find the complete NativeScript project working with Interstitial ads at GitHub.

I hope this article helped you understand how easy is to get an existing native library and work with it directly in a NativeScript app without common any JS wrapper code. Still, as we acknowledge the popularity of the AdMob service, we may come up with a verified plugin for AdMob pretty soon. Stay tuned!

For more information and updates on NativeScript, please follow @nativescript at Twitter or follow the NativeScript project at GitHub.

Comments


Comments are disabled in preview mode.
NativeScript is licensed under the Apache 2.0 license
© 2020 All Rights Reserved.