Frustrating memory leak when loading an interstitial ad in Swift - ios

I am dealing with an infuriating memory leak in regards to loading an interstitial ad via AdMob. When observing the memory in Xcode & Instruments, the memory jumps 10 MB every time I visit the view controller hosting the ad. Also when closing the app on my phone and reopening it, causes the memory to jump 30-40 mb as well which is just ridiculous.
I have tried profiling this in Instruments and the memory being allocated are all system libraries and nothing that points out to what is wrong. I have read other Stack Overflow answers such as ADMOB Memory Leaking?
but no answer has helped me so far. Maybe somebody can tell me what is wrong with my code? I have followed the exact documentation AdMob provides which is https://developers.google.com/admob/ios/interstitial and all works fine except this crazy memory leak. Here is the exact code that is causing the leak below.
class ViewController: UIViewController, GADInterstitialDelegate {
var interstitial: GADInterstitial!
override func viewDidLoad() {
interstitial = createAndLoadInterstitial()
interstitial.delegate = self
}
func update() {
if interstitial.isReady {
interstitial.present(fromRootViewController: self)
}
}
func createAndLoadInterstitial() -> GADInterstitial {
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
interstitial.delegate = self
let request = GADRequest()
interstitial.load(request)
return interstitial
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
interstitial = createAndLoadInterstitial()
}
// I am calling update() inside a button when pressed in this VC.

I just want to say that this actually has been solved by me. I did as one of the above stated in the comments. I took my code that was on the view controller hosting the interstitial ad and moved it to the app delegate. I simply then just referenced the interstitial ad object whenever I need it in my project. This brought the memory back down to whatever it allocated upon visiting the controller hosting the ad. For those that want to see a very simple example of what this looks like in the app delegate:
import UIKit
import GoogleMobileAds
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {
var myInterstitial: GADInterstitial!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GADMobileAds.sharedInstance().start(completionHandler: nil)
myInterstitial = createAndLoadInterstitial()
return true
}
func createAndLoadInterstitial() -> GADInterstitial {
let interstitial = GADInterstitial(adUnitID: "yourAdID")
interstitial.delegate = self
let request = GADRequest()
interstitial.load(request)
return interstitial
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
myInterstitial = createAndLoadInterstitial()
}

I also had this issue and turns out I just needed a pod update.
In version 7.53.0 the update included 'Fixed the GADBlockSignalSource memory leak that occurred when loading ads' which may relate to the interstitial memory leak issue we were experiencing.
Release notes:
https://developers.google.com/admob/ios/rel-notes

Related

Firebase Crashes iMessage Extension

Inside of my MessagesViewController (first view controller to load) I have the code:
override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
ref = Database.database().reference()
}
It works to load firebase into my iMessage extension initially, but then it crashed the extension when it tries to run again once the app exits and you are viewing the transcript. I used breakpoints to pinpoint the error, and it is FirebaseApp.configure() running for the second time. Does anyone know how to prevent this crash?
I got it to work through:
override func viewDidLoad() {
super.viewDidLoad()
if(FirebaseApp.app() == nil){
FirebaseApp.configure()
ref = Database.database().reference()
}
}

Swift Admob Interstitial Error

This is pretty long but it's probably a really easy fix.
Most of this is code, just skip to the bottom and there'll be a tl;dr
Hey, so I've followed about 10 tutorials on getting Admob interstitials to work in my Swift Spritekit game. Except all the tutorials I've seen use an IBAction with a button.
Currently I would like the ads to pop up every once in a while on the menu. (just to begin with, I might change it later).
This all works perfectly, but once I hit the play button (going to a different scene) and then return to the menu scene. (GameScene) The interstitial function no longer happens. There is no error, it just doesn't do it any more until the app is completely closed and re-opened.
Here's the code.
In my GameViewController class I have:
var interstital: GADInterstitial!
And in the GameViewController did load I have:
if let scene = GameScene(fileNamed:"GameScene") {
let skView = self.view as? SKView
skView!.ignoresSiblingOrder = false
scene.scaleMode = .AspectFill
scene.viewController = self
skView!.presentScene(scene)
interstital = GADInterstitial(adUnitID: "ca-app-pub-9776687746813194/9687097060")
let request = GADRequest()
interstital.loadRequest(request)
Also in the GameViewController class I have these functions:
func ShowAd() {
print("doing the showadfunc now")
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
interstital = CreateAd()
}
else{
print("The interstitial wasn't ready.")
}
}
func CreateAd() -> GADInterstitial {
let interstital = GADInterstitial(adUnitID: "ca-app-pub-9776687748683194/9687247060")
interstital.loadRequest(GADRequest())
return interstital
}
In my GameScene's (The menu) class I have:
var viewController: GameViewController!
And in my GameScene's class I have this function for displaying an interstitial ad.
func adThing(){
//viewController?.ShowAd()
print("starting the ad function")
let waitForInterstitial = SKAction.waitForDuration(15.0)
let waitForInterstitialShort = SKAction.waitForDuration(3.0)
let showInterstitialAd = SKAction.runBlock({self.viewController?.ShowAd(); print("the function has been called..")})
let waitThenShowInterstitial = SKAction.sequence([showInterstitialAd,waitForInterstitial])
let waitThenShowInterStitialForever = SKAction.repeatActionForever(waitThenShowInterstitial)
//self.runAction(waitThenShowInterStitialForever)
self.runAction(waitForInterstitialShort, completion: {
print("its waited 3 seconds and will now start the ad thingo")
self.runAction(waitThenShowInterStitialForever)
})
print("done")
}
So, I call the ShowAd function (which is located in gameviewcontroller) in my GameScene every 20 seconds, (I have confirmed it at least thinks it is calling the function.) It works perfectly when the app is opened to begin with. But when I change scene and then return to the menu scene (GameScene), the Prints in my GameScene function keep happening, and my code thinks that it is calling the ShowAd function, but it seems that nothing is happening, no interstitials pop up, not even the prints that are in the ShowAd function in the GameViewController.
So it seems like after I change scenes and return, my game forgets what the GameViewController is. If I refer to the GameViewController without a '?' it will crash my game with the error "unexpectedly found nil while unwrapping an optional." So after changing scenes, maybe GameViewController becomes nil? How can I fix this.
Please help me!!! (I'm new to programming in case you can't tell. I know it must be tempting to make a snide comment about my code, it is really bare bones and not that good at the moment.)
Thank you so much if you can help me, and wow.. thanks for reading all this.. ;)
You problem most likely is that once you transition to a new scene the viewController property is set to nil hence a crash if you use !.
In general its not the best practice to reference the viewController in your SKScenes, you should use Delegation or NSNotification Center.
1) Notification Center (easiest way)
In your ViewController where you have the function to show ads add a NSNotificationCenter observer in ViewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(showAd), name: "ShowInterAdKey", object: nil)
(Selector is the function to call and name is the key to reference this observer)
Than in your SKScenes when you want to show the ad you simply post the notification
NSNotificationCenter.defaultCenter().postNotificationName("ShowInterAdKey", object: nil)
2) For delegation examples check out these articles.
http://stephenradford.me/creating-a-delegate-in-swift/
https://www.natashatherobot.com/ios-weak-delegates-swift/
3) I also noticed that you are not preloading the ads before you show them, which is not very efficient and can cause delays showing ads.
You should call
interstital = CreateAd()
in viewDidLoad so it will load the first ad ASAP. Than you should use the adMob delegate method
func interstitialDidDismissScreen(ad: GADInterstitial!) {
// load next ad
interstital = CreateAd()
to immediately load the next ad once the current one is closed.
To use the delegates you can create an extension in ViewController
extension MyViewController: GADInterstitialDelegate {
func interstitialDidReceiveAd(ad: GADInterstitial!) {
print("AdMob interstitial did receive ad from: \(ad.adNetworkClassName)")
}
func interstitialWillPresentScreen(ad: GADInterstitial!) {
print("AdMob interstitial will present")
// pause game/app
}
func interstitialWillDismissScreen(ad: GADInterstitial!) {
print("AdMob interstitial about to be closed")
}
func interstitialDidDismissScreen(ad: GADInterstitial!) {
print("AdMob interstitial closed, reloading...")
interstitial = createAd()
}
func interstitialWillLeaveApplication(ad: GADInterstitial!) {
print("AdMob interstitial will leave application")
// pause game/app
}
func interstitialDidFailToPresentScreen(ad: GADInterstitial!) {
print("AdMob interstitial did fail to present")
}
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
print(error.localizedDescription)
}
}
To make sure these get called go to your createAd function and add this line before returning the ad
let interstitial = ...
interstitial.delegate = self
Finally your showAd method should look like this
func showAd() {
print("doing the showadfunc now")
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
} else{
print("The interstitial wasn't ready, reloading again.")
interstital = CreateAd()
}
}
4) You can also check out my helper on gitHub if you are looking for a more reusable solution.
https://github.com/crashoverride777/Swift-AdMob-AppLovinTVOS-CustomAds-Helpers
As a general tip you should also follow the swift naming conventions, your func and properties should start with small letters. Only classes, structs, enums and protocols should start with capital letters.
It makes your code harder to read for yourself and on stackoverflow because it is marked blue but shouldn't. You are sometimes doing and sometimes not.
Hope this helps
I'd bet this is your issue:
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
interstital = CreateAd()
You're presenting your interstitial and then immediately overwriting your interstitial. You should be implementing the GADInterstitial's delegate methods and calling your func CreateAd() -> GADInterstitial once func interstitialDidDismissScreen(ad: GADInterstitial!) is called.
if your device keeps crashing because of:
interstitial.delegate = self
then put it in your CreatAndLoadInterstitial(), not your viewDidLoad()

AdMob Interstitial: No ad to load (works on one project but not on another)

I've run into a very strange issue with AdMob. On my main project I receive error that there is no ad to load (when trying to fetch GADInterstitial).
However when I created a new project, copied the AdMob related sources, launched the project it loaded the ad without any problem (it looks like it always loads the test ad; I gave it few tries just to be sure).
On both projects I'm using the same Google pods:
pod 'Google-Mobile-Ads-SDK'
pod 'Google/Analytics'
I've been trying various solutions, generating different ad unit id's, tested the new project without analytics, then attached analytics to it. It worked like a charm during every step.
On my main project I got the ad once, strangely it was for ad unit id (banner view). Sadly since then I cannot get neither banner or interstitial ad (I couldn't retrieve the interstitial ad before).
My target platform is iOS 8+.
Sources, the AdMobManager
import GoogleMobileAds
class AdMobManager {
static let sharedInstance = AdMobManager()
private(set) var interstitial: GADInterstitial!
func loadInterstitialAd(delegate: GADInterstitialDelegate) -> GADInterstitial {
interstitial = GADInterstitial(adUnitID: "same-ad-unit-id-for-both-projects")
interstitial.delegate = delegate
let request = GADRequest()
request.testDevices = [ "same-test-id-for-both-projects" ]
interstitial.loadRequest(request)
return interstitial
}
}
And the ViewController
import UIKit
import GoogleMobileAds
class ViewController: UIViewController {
private var interstitial: GADInterstitial!
override func viewDidLoad() {
interstitial = AdMobManager.sharedInstance.loadInterstitialAd(self)
}
}
extension ViewController: GADInterstitialDelegate {
func interstitialDidReceiveAd(ad: GADInterstitial!) {
if interstitial.isReady {
interstitial.presentFromRootViewController(self)
} else {
NSLog("Not ready")
}
}
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
NSLog("Error: \(error.debugDescription)")
}
}
Cause of my problem was custom User-Agent.
Hope that save somebody hours of debugging since AdMob documentation doesn't cover that.

How to show a real Admob ad rather than the test ad?

I wrote this piece of code with Swift so that when I push a button an Admob ad shows up, and now my app is showing a test Admob ad. My question is how I can transition from showing a test ad to a real world ad? I'm using the simulator on Xcode by the way since I don't have an iPhone!
import UIKit
import Parse
import GoogleMobileAds
class NewsPageViewController: UIViewController {
var interstitial:GADInterstitial!
override func viewDidLoad() {
super.viewDidLoad()
self.interstitial = self.createAndLoadAd()
}
func createAndLoadAd() -> GADInterstitial {
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
var request = GADRequest()
request.testDevices = ["2077ef9a63d2b398840261c8221a0c9b"]
interstitial.loadRequest(request)
return interstitial
}
#IBAction func buttonTapped(sender: AnyObject) {
if (self.interstitial.isReady) {
self.interstitial.presentFromRootViewController(self)
self.interstitial = self.createAndLoadAd()
}
}
Do this:
1-remove "request.testDevices = ["2077ef9a63d2b398840261c8221a0c9b"]
2- Create an Admob account on google's website, then create an app in your account,then, replace the default adUnitID given in the google's tutorial with the one in the created app.
3- Then, instead of test ad, you might get a full black screen ad. This one is because it takes a few hours before google starts to send real ads. Hope that helps.

iAd Interstitials not showing consistently? And not at all on the simulator

iAd interstitials aren't showing up at all on the iPhone simulator, and they don't show up consistently on my iPhone. I've gone to the Developer settings, changed the fill rate to 100%, and turned on Unlimited Ad Presentation. No difference... an interstitial will generally show the first time it's supposed to, and then won't show again for anywhere from a few minutes to fifteen minutes. No idea what is causing the difference in time.
Also, there doesn't seem to be a way to track if the interstitial is going to show or not / if it actually showed or didn't. I realize there's an interstitial delegate, but it seems that isn't used anymore. The way I am calling my interstitial is using viewController.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Automatic
Thanks!
So it seems that using requestInterstitialAdPresentation is intended to only be used when your ADInterstitialPresentationPolicy is set to Automatic. When implementing your interstitials using a Manual ADInterstitialPresentationPolicy you must use presentInView to present the ad at your own intervals. When presenting your interstitial in this manner it does not load with its own close button to dismiss itself. So, what I've done is created a UIView to present the interstitial in and used the interstitials delegate methods to dismiss the UIView. The inconsistency with receiving an ad from the iAd network still arises when testing. Sometimes you receive an ad, sometimes it fails to load which allows us to request a new ad, and sometimes nothing happens at all. This just seems to be the nature of iAd's interstitials.
import UIKit
import iAd // Import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate { // Include the delegate
var iAdInterstitial = ADInterstitialAd() // Our ad
var iAdInterstitialView = UIView() // View to present our ad in
var adLoaded = false // Bool to keep track if an ad is loaded or not
override func viewDidLoad() {
super.viewDidLoad()
setupAd()
}
func setupAd() {
// Set presentation to manual so we can choose when to present the interstitial
// Setting this will also fetch an interstitial ad for us
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Manual
iAdInterstitial.delegate = self // Set the delegate
// Make our view the same size as the view we will be presenting in
iAdInterstitialView.frame = self.view.bounds
}
func requestNewAd() {
// This will fetch an ad for us
ViewController.prepareInterstitialAds()
println("Requesting new ad")
}
#IBAction func presentAdButton(sender: AnyObject) {
if (adLoaded) {
// We have an ad that is loaded so lets present it
self.view.addSubview(iAdInterstitialView)
iAdInterstitial.presentInView(iAdInterstitialView)
}
else {
// No ad has been loaded
println("Ad not loaded")
}
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
// Kinda works as expected
// Sometimes is called prematurely
// Sometimes takes minutes after ad is dismissed to be called
println("interstitialAdDidUnload")
// Get new ad
adLoaded = false
iAdInterstitialView.removeFromSuperview()
requestNewAd()
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
// Failed to load ad so lets try again
println("didFailWithError: \(error)")
requestNewAd()
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
// There is an ad and it has begun to download
println("interstitialAdWillLoad")
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
// We got an ad
println("interstitialAdDidLoad")
adLoaded = true
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
println("interstitialAdActionShouldBegin")
return true;
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
// Done with this ad. Lets get a new one
println("interstitialAdActionDidFinish")
iAdInterstitialView.removeFromSuperview()
adLoaded = false
requestNewAd()
}

Resources