How to retain Remove Ads in app purchase after app has closed - ios

I have successfully removed ads from the app with an in app purchase.
The problem is that if I close the app and reopen. The ads start up again.
I have 2 main scenes. The GameOverScene and the GameScene. The In App Purchase happens in the GameOverScene.
GameOverScene.m :
- (void)OnRemoveADS {
[self showPurchaseAlert: IAP_Q_RemoveADS :0];
g_bRemoveADS = [[NSUserDefaults standardUserDefaults] boolForKey: #"REMOVEADS"];
// For HZInterstitialAd, HZVideoAd, and HZIncentivizedAd, just check the BOOL to see if an ad should be shown
if (!g_bRemoveADS) {
[HZInterstitialAd show];
[self removeBannerAds];
[self disableAds];
NSLog(#"Disable ads is called");
}
}
- (void)removeBannerAds {
HZBannerAdOptions *options = [[HZBannerAdOptions alloc] init];
[HZBannerAd placeBannerInView:self.view
position:HZBannerPositionBottom
options:options
success:^(HZBannerAd *banner) {
if (g_bRemoveADS) { // case (2)
// Just discard the banner
[banner setHidden: YES];
[banner removeFromSuperview];
banner = nil;
//_currentBannerAd = banner;
NSLog(#"Banner ad removed!GameOverScene");
} else {
// Keep a reference to the current banner ad, so we can remove it from screen later if we want to disable ads.
_currentBannerAd = banner;
}
NSLog(#"Ad Shown! GameOverScene");
}
failure:^(NSError *error) {
NSLog(#"Error = %#",error);
}];
}
- (void)disableAds {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"REMOVEADS"];
[_currentBannerAd removeFromSuperview]; // case (3)
}
GameScene.m :
-(id) init {
if (self = [super init]) {
if (!g_bRemoveADS) {
g_bRemoveADS=FALSE;
[[NSUserDefaults standardUserDefaults] setBool:g_bRemoveADS forKey:#"REMOVEADS"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
g_bRemoveADS=TRUE;
[[NSUserDefaults standardUserDefaults] setBool:g_bRemoveADS forKey:#"REMOVEADS"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}
The way I'm trying to solve it is by using the same code from the GameOverScene.m in the AppDelegate.m that way when the app starts up it will remove the ads.
AppDelegate.m :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
g_bRemoveADS = [[NSUserDefaults standardUserDefaults] boolForKey: #"REMOVEADS"];
if (!g_bRemoveADS) {
[HZInterstitialAd show];
[self disableAds];
NSLog(#"Disable ads is called");
}
}

Out of my perspective you have one negation to much.
if (!g_bRemoveADS) { should be replaced with if (g_bRemoveADS) { in GameOverScene.m.
if (g_bRemoveADS) {
[HZInterstitialAd show];
[self removeBannerAds];
[self disableAds];
NSLog(#"Disable ads is called");
}
g_bRemoveADS evaluates to TRUE when the respective user-default is set. When it is set, then you call the removeBannerAds stuff etc. which seems to be the deactivation action.

You have to synchronise your NSUserDefaults after changing changing a value in your disableAds method with:
[[NSUserDefaults standardUserDefaults]synchronize];

Related

iOS app gets slow and eventually crashes with Admob interstitial ads

After a while playing my iPhone game the phone starts to get really hot, and a while after that the app suddenly gets really slow and laggy, before eventually crashing.
I have followed official docs when implementing Admob ads.
I know for a fact the problem is Admob, since the app functions perfectly when turning off ads.
I know there is quite a lot of ads showing if you play for a while, but limiting the amount of ads displaying will only delay the lag and crash, and will not fix the underlying issue.
This is the code I use to load and display the ads:
- (void)viewDidLoad {
[super viewDidLoad];
[self loadInterstitial];
}
- (void)loadInterstitial {
[GADInterstitialAd loadWithAdUnitID:#"ca-app-pub-xxx/xxx" request:[GADRequest request] completionHandler:^(GADInterstitialAd *ad, NSError *error) {
if (error) {
NSLog(#"Failed to load interstitial ad with error: %#", [error localizedDescription]);
return;
}
self.interstitial.fullScreenContentDelegate = nil;
self.interstitial = ad;
self.interstitial.fullScreenContentDelegate = self;
}];
}
- (void)viewDidAppear:(BOOL)animated {
adCount = [[NSUserDefaults standardUserDefaults] integerForKey:#"adCount"] + 1;
[[NSUserDefaults standardUserDefaults] setInteger:adCount forKey:#"adCount"];
[[NSUserDefaults standardUserDefaults] synchronize];
if (adCount > 11) {
[self displayInterstitial];
}
}
- (void)displayInterstitial {
if (self.interstitial) {
[self.interstitial presentFromRootViewController:self];
adCount = 0;
[[NSUserDefaults standardUserDefaults] setInteger:adCount forKey:#"adCount"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
NSLog(#"Ad wasn't ready");
}
}
- (void)adDidDismissFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {
[self loadInterstitial];
}
Nothing special. To me it seems like the memory gets overloaded or too many leaks after displaying enough interstitial ads.

iAd showing a white banner

I successfully implemented an in-app purchase to remove ads in my app. It worked the first time I tried it but after I ran the app on my phone a few more times it started to just show a white ad banner instead of hidding the ad banner like it used to.
Here is the code for the StartScreen.m of my app as well as the PurchaseViewController.m to buy the IAP to remove ads. I also got a Warning saying I am using 10 instances of ADBanner even though I have them removed whenever the view disappers. Thank you for any and all help.
//
//StartScreen.m
//
#interface StartScreen ()
{
BOOL _bannerIsVisible;
}
#end
#implementation StartScreen
- (void)viewDidLoad {
//Ads
self.adBanner.delegate = self;
}
- (void)viewWillDisappear:(BOOL)animated
{
[self.adBanner removeFromSuperview];
self.adBanner.delegate = nil;
self.adBanner = nil;
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
// Check for Remove Ads IAP
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([prefs boolForKey:#"RemoveAds"] == TRUE) {
self.adBanner.hidden = YES;
_bannerIsVisible = NO;
} else if (!_bannerIsVisible)
{
self.adBanner.hidden = NO;
_bannerIsVisible = YES;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"Failed to retreive ad");
// Check for Remove Ads IAP
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([prefs boolForKey:#"RemoveAds"] == TRUE) {
self.adBanner.hidden = YES;
_bannerIsVisible = NO;
}
}
//
// PurcahseViewController.m
//
#import "PurcahseViewController.h"
#implementation PurcahseViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.productID = #"com.app.iap1";
[self getProductID:self];
self.buyButton.enabled = NO;
NSLog(#"%#", self.productID);
}
- (void)getProductID:(UIViewController *)viewController {
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.productID]];
request.delegate = self;
[request start];
} else {
self.productDescription.text = #"Please enable in app purchase in your settings";
}
}
- (IBAction)purchaseItem:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:self.product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)restoreButton:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self UnlockPurchase];
}
- (void)Purchased {
NSLog(#"Item has been purchased");
}
#pragma mark _
#pragma mark SKProductsRequestDelegate
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count != 0) {
self.product = products[0];
self.buyButton.enabled = YES;
self.productTitle.text = self.product.localizedTitle;
self.productDescription.text = self.product.localizedDescription;
} else {
self.productTitle.text = #"404: Product Not Found";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
NSLog(#"404: Product Not Found: %#", product);
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: [self UnlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:
break;
}
}
}
-(void)UnlockPurchase {
self.buyButton.enabled = NO;
[self.buyButton setTitle:#"Purchased" forState:UIControlStateDisabled];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:TRUE forKey:#"RemoveAds"];
[prefs synchronize];
[self Purchased];
}
First off, to answer your question, the reason your banner isn't hiding when your ad fails is because you're not hiding it. Whether or not [prefs boolForKey:#"RemoveAds"] == TRUE, you're going to want to hide that banner if you don't want the blank white bar in its place. To do so in the simplest possible way (without any animation), simplify your didFailToReceiveAdWithError: method, like so:
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"Failed to retreive ad");
// Check for Remove Ads IAP
self.adBanner.hidden = YES;
_bannerIsVisible = NO;
}
And hide it in your viewDidLoad so it's hidden from the very beginning before any ad has loaded:
- (void)viewDidLoad {
//Ads
self.adBanner.delegate = self;
self.adBanner.hidden = YES;
}
That way, your ad will only unhide in bannerViewDidLoadAd: once an ad has successfully been loaded.
Secondly, your _bannerIsVisible boolean is unnecessary. Instead of using a separate boolean to check whether the banner is visible, you can just check whether it's hidden, with if (self.adBanner.hidden == YES) or if (self.adBanner.hidden == NO) or if (self.adBanner.hidden) or if (!self.adBanner.hidden). Eliminating that unnecessary boolean will cut down on the potential for error.
So, just to summarize, here's my suggestion for how your bannerViewDidLoadAd: and didFailToReceiveAdWithError: methods should look:
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
// Check for Remove Ads IAP
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([prefs boolForKey:#"RemoveAds"] == TRUE) {
self.adBanner.hidden = YES;
} else if (self.adBanner.hidden) {
self.adBanner.hidden = NO;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"Failed to retreive ad");
self.adBanner.hidden = YES;
}

Applovin Interstitials Not Coming Up Every Time, Only Sparingly

So I set up Applovin SDK in my app and Applovin ads only come up sparingly. All of my other apps with Applovin installed have the ads coming up accordingly. Anyone heard of this kind of error?
Here is what I have in the Appdelegate:
[ALSdk initializeSdk];
Chartboost *cb = [Chartboost sharedChartboost];
cb.appId = #"TAKEN OUT";
cb.appSignature = #"TAKEN OUT";
// Required for use of delegate methods. See "Advanced Topics" section below.
cb.delegate = self;
[cb startSession];
[cb cacheMoreApps];
[cb showInterstitial];
//AppLovin Interstitial Ad
if (self.theTimer == nil)
{
self.theTimer = [NSTimer scheduledTimerWithTimeInterval:1000 target:self selector:#selector(countdownTracker) userInfo:nil repeats:YES];//Repeat to yes
}
}
-(void)didCloseInterstitial:(NSString *)location
{
[ALInterstitialAd showOver:self.window];
NSString *savedValue = [[NSUserDefaults standardUserDefaults]
stringForKey:#"preferenceName"];
if ([savedValue isEqualToString:#"yes"]) {
}
NSString *valueToSave = #"yes";
[[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:#"preferenceName"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)stopTimer
{
if (self.theTimer != nil)
{
[self.theTimer invalidate];
self.theTimer = nil;
}
}

Making a View for the first use of an app

I wish to make an app that, when the user opens it for the first time he selects his country from a picker in order to make his flag appear on the main screen. If the user closes the app and opens it again I want the app to start the menu screen with his flag on it directly.
I am using the following code now but it doesn't work at all. Every time the app is opened it takes him to the picker VIew (TappViewController)
- (void)viewDidLoad
{
[super viewDidLoad];
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"FirstLaunch"]) {
secViewController *menu = [[secViewController alloc] init];
[self presentViewController:menu animated:YES completion:^{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];}];
}
else{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Change your code like this:
- (void)viewDidLoad
{
[super viewDidLoad];
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"isPickerOpened"]) {
secViewController *menu = [[secViewController alloc] init];
[self presentViewController:menu animated:YES completion:^{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"isPickerOpened"];
[[NSUserDefaults standardUserDefaults] synchronize];
}];
}
}

Restore Purchase Functionality doesn't work well in ios

I am having an app in which I have 5 Non Consumable In App Purchases.
I am using the following code for Restore In App Purchase.
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
//NSLog(#"into method1");
purchasedItemIDs = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
}
// [customSpinner hide:YES];
if (queue.transactions.count==0)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"FameFace" message:#"It seems you have not buy this Package" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
else
{
NSLog(#"Data available");
NSString *isInApp = [[NSUserDefaults standardUserDefaults]valueForKey:#"Package"];
if ([isInApp isEqualToString:#"Pack1"])
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp1Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp1Once"];
NSLog(#"Launching first time");
requestText= #"Animal";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([isInApp isEqualToString:#"Pack2"])
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp2Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp2Once"];
NSLog(#"Launching first time");
requestText= #"Celebrity";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([isInApp isEqualToString:#"Pack3"])
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp3Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp3Once"];
NSLog(#"Launching first time");
requestText= #"Nature";
[self MakeQueryForUpdate];
[self getdata];
}
}
}
Below is the code when there is successful transaction.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
//[self downloadFromURL:[NSURL URLWithString:#"http://www.iphonedevnation.com/tutorials/ForestGreen.mp3"]];
// flgCheck = 1;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self completeTransaction:transaction];
NSString *isInApp = [[NSUserDefaults standardUserDefaults]valueForKey:#"Package"];
if ([isInApp isEqualToString:#"Pack1"])
{
//by manthan...7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp1Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp1Once"];
NSLog(#"Launching first time");
requestText= #"Animal";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([isInApp isEqualToString:#"Pack2"])
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp2Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp2Once"];
NSLog(#"Launching first time");
requestText= #"Celebrity";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([isInApp isEqualToString:#"Pack3"])
{
//by manthan... 7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp3Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp3Once"];
NSLog(#"Launching first time");
requestText= #"Nature";
[self MakeQueryForUpdate];
[self getdata];
}
}
This is working fine.
What I am doing is that I am inserting some images in my DB when user Purchases a package from In App Purchase.
But as the Product is Non Consumable, If the user deletes the app and trying to Restore Images, My DB doesn't get updated.
Any help will be appreciated.
Thanks...
Edited
I put my code in the method below when user try to restore purchase.
As I have a single restore button and on pressing that button, user will have previously purchased packages that may be 2,3 or 4(any of the 5 packages that user has purchased once as these are non consumable products).
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSString *pk = [[NSUserDefaults standardUserDefaults]valueForKey:#"Package"];
if ([pk isEqualToString:#"Pack1"])
{
//by manthan...7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp1Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp1Once"];
NSLog(#"Launching first time");
requestText= #"Animal";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([pk isEqualToString:#"Pack2"])
{
//by manthan... 7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp2Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp2Once"];
NSLog(#"Launching first time");
requestText= #"Celebrity";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([pk isEqualToString:#"Pack3"])
{
//by manthan... 7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp3Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp3Once"];
NSLog(#"Launching first time");
requestText= #"Nature";
[self MakeQueryForUpdate];
[self getdata];
}
}
when there is only one single purchase I can simply update my DB in SKPaymentTransactionStateRestored.
But when i have multiple non consumable products what should i do?
edited
if ([transaction.payment.productIdentifier isEqualToString:#"Pack1"])
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp1Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp1Once"];
NSLog(#"Launching first time");
requestText= #"Animal";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([transaction.payment.productIdentifier isEqualToString:#"Pack2"])
{
//by manthan... 7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp2Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp2Once"];
NSLog(#"Launching first time");
requestText= #"Celebrity";
[self MakeQueryForUpdate];
[self getdata];
}
}
else if ([transaction.payment.productIdentifier isEqualToString:#"Pack3"])
{
//by manthan... 7th may
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedp3Once"])
{
// app already launched
NSLog(#"Launching secondtime");
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedp3Once"];
NSLog(#"Launching first time");
requestText= #"Nature";
[self MakeQueryForUpdate];
[self getdata];
}
}
You need to use the case of SKPaymentTransactionStateRestored in the updatedTransactions method for doing this.
When restoring this case will be executed.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
//do your stuff
break;
case SKPaymentTransactionStateFailed:
//do your stuff
break;
case SKPaymentTransactionStateRestored:
//do your database stuff
default:
break;
}
};
}
Reference
Payment Transaction States
The state of a transaction.
enum { SKPaymentTransactionStatePurchasing,
SKPaymentTransactionStatePurchased, SKPaymentTransactionStateFailed,
SKPaymentTransactionStateRestored };
typedef NSInteger SKPaymentTransactionState;
Constants
SKPaymentTransactionStatePurchasing
The transaction is being processed by the App Store.
SKPaymentTransactionStatePurchased
The App Store successfully processed payment. Your application should provide the content the user purchased.
SKPaymentTransactionStateFailed
The transaction failed. Check the error property to determine what happened.
SKPaymentTransactionStateRestored
This transaction restores content previously purchased by the user. Read the originalTransaction property to obtain information about the original purchase.
Available in iOS 3.0 and later.
Declared in SKPaymentTransaction.h.
Check SKPaymentTransaction for more

Resources