I am trying to make it so that if I buy an item on my app it would allow me to hide the iAd that I have put.
However, when I test it out only my restore button is tappable and I cannot tap the buy button.
Did I miss something with my app?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
bool saved = [saveapp boolForKey:k_Save];
if (!saved) {
/// not save code here
} else {
///saved code here
Label.text = #"item has been purchased";
}
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
bool saved = [saveapp boolForKey:k_Save];
if (!saved) {
/// not save code here
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[banner setAlpha:1];
[UIView commitAnimations];
} else {
///saved code here
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[banner setAlpha:0];
[UIView commitAnimations];
}
- (IBAction)PurchaseItem:(id)sender {
_purchaseController = [[PurchasedViewController alloc] initWithNibName:nil bundle:nil];
_purchaseController.productID = #"com.myname.appname.iap1";
[[SKPaymentQueue defaultQueue] addTransactionObserver:_purchaseController];
[self presentViewController:_purchaseController animated:YES completion:NULL];
[_purchaseController getProductID:self];
}
-(void)Purchased {
Label.text = #"item has been purchased";
iadBanner.hidden = YES;
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
[saveapp setBool:TRUE forKey:k_Save];
[saveapp synchronize];
}
Purchase View
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
_buyButton.enabled = NO;
}
- (IBAction)BuyProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)Restore:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self UnlockPurchase];
}
-(void)getProductID:(ViewController *)viewController {
_homeViewController = viewController;
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.productID]];
request.delegate = self;
[request start];
} else
_productDescription.text = #"Please enable in app purchase in your settings";
}
#pragma mark _
#pragma mark SKProductsRequestDelegate
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count != 0) {
_product = products[0];
_buyButton.enabled = YES;
_productTitle.text = _product.localizedTitle;
_productDescription.text = _product.localizedDescription;
} else {
_productTitle.text = #"Product Not Found";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
NSLog(#"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 {
_buyButton.enabled = NO;
[_buyButton setTitle:#"Purchased" forState:UIControlStateDisabled];
[_homeViewController Purchased];
}
You are explicitly disabling it with: _buyButton.enabled = NO; on viewDidLoad and are only setting it enabled when you receive products.. which you can only do by tapping the button in the first place.
Related
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;
}
I have too many crash reports on system code. How can I find the issue on my application?
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x81566238
libobjc.A.dylib objc_msgSend + 5 respondsToSelector:
StoreKit __34-[SKProductsRequest _handleReply:]_block_invoke + 446
libdispatch.dylib _dispatch_call_block_and_release + 10
UIKit UIApplicationMain + 1136
MyApp main.m line 13
UPDATED
The full code to process in-app purchases on the application:
#pragma mark In-App Purchase
- (NSString*)getProductId:(NSString*)feature {
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
NSString *bundleIdentifier = [info objectForKey: #"CFBundleIdentifier"];
return [NSString stringWithFormat:feature, [bundleIdentifier stringByReplacingOccurrencesOfString:#"-" withString:#""]];
}
- (void)requestItems:(NSString*)feature {
NSSet *productIdentifiers = [NSSet setWithObject:[self getProductId:feature]];
if ([feature isEqualToString:g100Items]) {
if (product100ItemsRequest) {
[product100ItemsRequest release];
product100ItemsRequest = nil;
}
product100ItemsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
product100ItemsRequest.delegate = self;
[product100ItemsRequest start];
// we will release the request object in the delegate callback
} else
if ([feature isEqualToString:gUnlimitedItems]) {
if (productUnlimitedItemsRequest) {
[productUnlimitedItemsRequest release];
productUnlimitedItemsRequest = nil;
}
productUnlimitedItemsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productUnlimitedItemsRequest.delegate = self;
[productUnlimitedItemsRequest start];
// we will release the request object in the delegate callback
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
[products addObjectsFromArray:response.products];
for (SKProduct *product in response.products) {
if (product && [product.productIdentifier isEqualToString:[self getProductId:g100Items]]) {
[button100Items setTitle:[NSString stringWithFormat:g100ItemsButton, product.localizedPrice] forState:UIControlStateNormal];
// finally release the reqest we alloc/init’ed in requestItems
[product100ItemsRequest release];
product100ItemsRequest = nil;
}
if (product && [product.productIdentifier isEqualToString:[self getProductId:gUnlimitedItems]]) {
[buttonUnlimitedItems setTitle:[NSString stringWithFormat:gUnlimitedItemsButton, product.localizedPrice] forState:UIControlStateNormal];
// finally release the reqest we alloc/init’ed in requestItems
[productUnlimitedItemsRequest release];
productUnlimitedItemsRequest = nil;
}
}
for (NSString *invalidProductId in response.invalidProductIdentifiers) {
NSLog(#"Invalid product id: %#" , invalidProductId);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
// call this method once on startup
- (void)loadStore:(BOOL)tryAgain {
if (tryAgain) {
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
NSMutableArray *oldProducts = products;
products = [[[NSMutableArray alloc] initWithObjects: nil] retain];
[oldProducts release];
}
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestItems:g100Items];
[self requestItems:gUnlimitedItems];
}
// call this before making a purchase
- (BOOL)canMakePurchases {
return [SKPaymentQueue canMakePayments];
}
// kick off the upgrade transaction
- (void)purchaseItems:(NSString*)feature {
bool ok = false;
for (SKProduct *product in products) {
if ([product.productIdentifier isEqualToString:[self getProductId:feature]]) {
SKPayment *payment = [SKPayment paymentWithProduct:product];
if (payment) {
[[SKPaymentQueue defaultQueue] addPayment:payment];
// Calling AppStore Dialog
}
ok = true;
break;
}
}
if (!ok) {
[self loadStore:YES];
[self showAlert:gInAppAlertTitle alertStr:gNoProductsToMakePurchase];
return;
}
}
// saves a record of the transaction by storing the receipt to disk
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
if ([transaction.payment.productIdentifier isEqualToString:[self getProductId:g100Items]]) {
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:[NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]/*transaction.transactionReceipt*/ forKey:[self getProductId:g100Items]];
[[NSUserDefaults standardUserDefaults] synchronize];
} else
if ([transaction.payment.productIdentifier isEqualToString:[self getProductId:gUnlimitedItems]]) {
// save the transaction receipt to disk
[[NSUserDefaults standardUserDefaults] setValue:[NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]/*transaction.transactionReceipt*/ forKey:[self getProductId:gUnlimitedItems]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
// enable pro features
- (bool)provideContent:(NSString *)productId {
if (productId) {
FirstViewController* firstViewController = [[tabBarController viewControllers] objectAtIndex:gSourceTabIndex];
if ([productId isEqualToString:[self getProductId:g100Items]]) {
firstViewController.itemCount += 100;
[firstViewController saveItems];
// 100 Items Provided
return true;
} else
if ([productId isEqualToString:[self getProductId:gUnlimitedItems]]) {
firstViewController.itemCount = gItemUnlimitedCount;
[firstViewController saveItems];
// Unlimited Items Provided
return true;
}
}
return false;
}
// removes the transaction from the queue and posts a notification with the transaction result
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful {
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, #"transaction" , nil];
if (wasSuccessful) {
// send out a notification that we’ve finished the transaction
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
}
else {
// send out a notification for the failed transaction
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
}
}
// called when the transaction was successful
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
[self recordTransaction:transaction];
bool provided = [self provideContent:transaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has been restored and successfully completed
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[self recordTransaction:transaction.originalTransaction];
[self provideContent:transaction.originalTransaction.payment.productIdentifier];
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled) {
// error!
[self finishTransaction:transaction wasSuccessful:NO];
[self showAlert:gInAppAlertTitle alertStr:[transaction.error localizedDescription]];
} else {
// this is fine, the user just cancelled, so don’t notify
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
- (IBAction)process100Items:(id)sender {
if ([self canMakePurchases]) {
[self purchaseItems:g100Items];
} else {
[self showAlert:gInAppAlertTitle alertStr:gCanNotMakePurchases];
}
// Buy 100 Items Button Pressed
}
- (IBAction)processUnlimitedItems:(id)sender {
if ([self canMakePurchases]) {
[self purchaseItems:gUnlimitedItems];
} else {
[self showAlert:gInAppAlertTitle alertStr:gCanNotMakePurchases];
}
// Buy Unlimited Items Button Pressed
}
- (IBAction)restoreCompletedTransactions:(id)sender {
if ([products count] == 0) {
[self loadStore:YES];
[self showAlert:gInAppAlertTitle alertStr:gNoProductsToMakePurchase];
return;
}
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
The code that is passed as a block to handleReply tries call a method on an object that is already released. Without further information than the provided stack trace, there is probably nothing more that can be said.
I have coded In App Purchasing into my app and have made it work so that a user can make a purchase by pressing a button.
I now want to create a button for the user to be able to Restore Purchase. I am using a Shared Instance in 'UpgradeViewController' to retrieve methods from 'IAHelper.h' so I have shown both pieces of code incase it helps
How do I add a 'Restore Purchase' method to my In App Purchase?
IAHelper.m
#import "IAPHelper.h"
// Add to top of file
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
#implementation IAPHelper
{
// 3
SKProductsRequest * _productsRequest;
// 4
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
NSArray *_products;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products... %#", response.products);
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
// Add new method
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
- (NSArray*)getProductArrray {
return _products;
}
#end
UpgradeViewController
#import "UpgradeViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import "RageIAPHelper.h"
#import <StoreKit/StoreKit.h>
#interface UpgradeViewController ()
{
NSMutableArray *_objects;
NSArray *_products;
NSNumberFormatter * _priceFormatter;
}
#property (retain, nonatomic) IBOutlet JSAnimatedImagesView *animatedImagesView;
#property (assign, nonatomic) IBOutlet UIButton *installFullAppButton;
#end
#implementation UpgradeViewController
#synthesize menuBtn, animatedImagesView = _animatedImagesView, scrolly, bannerView, labelPrice;
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (NSString *)publisherIdForAdSdkBannerView:(AdSdkBannerView *)banner {
return #"e0616d4190bff65279ed5c20de1b5653";
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[RageIAPHelper sharedInstance];
_products = nil;
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
}
}];
// Price New
SKProduct * product = (SKProduct *) [_products objectAtIndex:0];
([[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier]);
// Unlock your features code comes here
UIButton *buyButton = [[UIButton alloc] initWithFrame:CGRectMake(-1, 370, 320, 60)];
UIImage *btnImage = [UIImage imageNamed:#"upgrade-new.png"];
[buyButton setImage:btnImage forState:UIControlStateNormal];
[buyButton.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]];
[buyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[buyButton.titleLabel setShadowColor:[UIColor colorWithWhite:0.1 alpha:1.0]];
[buyButton.titleLabel setShadowOffset:CGSizeMake(0, -1)];
[buyButton addTarget:self action:#selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
buyButton.tag = 0;
[[self view] addSubview:buyButton];
// Purchase Action End
[super viewDidLoad];
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_priceFormatter setLocale:product.priceLocale];
labelPrice.text = [_priceFormatter stringFromNumber:product.price];
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 30, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.screenName = #"Upgrade";
}
#pragma mark - UI
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)buyButtonTapped:(id)sender {
UIButton *buyButton = (UIButton *)sender;
SKProduct *product = [_products objectAtIndex:buyButton.tag];
NSLog(#"Buying %#...", product.productIdentifier);
[[RageIAPHelper sharedInstance] buyProduct:product];
}
#end
Apple will refuse your app if you do not have a button to do this, well they did me even though they recognised the app automatically checked in background.
Any way if you add this to your helper;
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
And call like this from VC;
- (IBAction)restoreAction:(id)sender
{
[[RageIAPHelper sharedInstance] restoreCompletedTransactions];
}
After you have doen this;
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
And call like this from VC;
- (IBAction)restoreAction:(id)sender
{
[[RageIAPHelper sharedInstance] restoreCompletedTransactions];
}
This methods needs to be changed to this;
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products... %#", response.products);
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
// Check is the Handler exists
if(_completionHandler)
{
_completionHandler(YES, skProducts);
}
_completionHandler = nil;
}
I am trying to remove ads with In App Purchase but It does not work. When I open my app the banner comes up and when I tap the banner it says it is working correctly. However after a few seconds it disappears and then appears again after a few minutes. Just keeping on the same screen. Is my code correct for this?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
bool saved = [saveapp boolForKey:k_Save];
if (!saved) {
/// not save code here
} else {
///saved code here
Label.text = #"item has been purchased";
}
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
bool saved = [saveapp boolForKey:k_Save];
if (!saved) {
/// not save code here
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[banner setAlpha:1];
[UIView commitAnimations];
} else {
///saved code here
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[banner setAlpha:0];
[UIView commitAnimations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
This is for saving
- (IBAction)PurchaseItem:(id)sender {
_purchaseController = [[PurchasedViewController alloc] initWithNibName:nil bundle:nil];
_purchaseController.productID = #"com.myname.test.iap1";
[[SKPaymentQueue defaultQueue] addTransactionObserver:_purchaseController];
[self presentViewController:_purchaseController animated:YES completion:NULL];
[_purchaseController getProductID:self];
}
-(void)Purchased {
Label.text = #"item has been purchased";
iadBanner.hidden = YES;
NSUserDefaults *saveapp = [NSUserDefaults standardUserDefaults];
[saveapp setBool:TRUE forKey:k_Save];
[saveapp synchronize];
}
- (IBAction)BuyProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)Restore:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self UnlockPurchase];
}
-(void)getProductID:(ViewController *)viewController {
_homeViewController = viewController;
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.productID]];
request.delegate = self;
[request start];
} else
_productDescription.text = #"Please enable in app purchase in your settings";
}
#pragma mark _
#pragma mark SKProductsRequestDelegate
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count != 0) {
_product = products[0];
_buyButton.enabled = YES;
_productTitle.text = _product.localizedTitle;
_productDescription.text = _product.localizedDescription;
} else {
_productTitle.text = #"Product Not Found";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
NSLog(#"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 {
_buyButton.enabled = NO;
[_buyButton setTitle:#"Purchased" forState:UIControlStateDisabled];
[_homeViewController Purchased];
}
Well, at least one potential issue I see is that you try to hide with:
iadBanner.hidden = YES;
But I don't see that property referred to anywhere else. Is it synthesized? Hooked up in IB?
With ios7 use self.canDisplayBannerAds instead , its easier and more straight forward
- (void)viewDidLoad
{
[super viewDidLoad];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
bool adsRemoved = [userDefaults boolForKey:#"removeAds"];
self.canDisplayBannerAds=!adsRemoved;
}
When I run the app with the code below, i buy one of the products using my test account and I get this in my console:
queue
queue
unlocked:
buy20
Transaction Failed
Somehow the purchase manages to be successful in the - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions method then immediately after, fails. Do you see any problems with the code below or have you heard of this problem before?
#import "BuyWindowViewController.h"
#interface BuyWindowViewController ()
#end
#implementation BuyWindowViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[_closeButton setTitle:#"Close" forState:UIControlStateNormal];
[_closeButton.titleLabel setFont:[UIFont fontWithName:#"Helvetica" size:16.0f]];
[_titleBar setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"Button 400x50 Yellow.png"]]];
_productIDs = [iAPBundleIDs returnAllIDs];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[self getProductID];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)closeButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTitleBar:nil];
[super viewDidUnload];
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate {
return YES;
}
- (IBAction)infoButtonPressed:(UIButton *)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Serpentine!" message:#"" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
if (sender.tag == 1) {
//buy 20
alert.title = #"Instantly get 20 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 2) {
//buy 45
alert.title = #"Instantly get 45 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 3) {
//buy 100
alert.title = #"Instantly get 100 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 4) {
//double
alert.title = #"Permanently double the upgrade points you receive at the end of each game!";
} else if (sender.tag == 5) {
//remove ads + 20
alert.title = #"Remove those annoying iAds and get 20 upgrade points with one purchase!";
}
[alert show];
}
- (IBAction)purchaseButtonPressed:(UIButton *)sender {
_whichProduct = 0;
if (sender.tag == 1) {
//buy 20
_whichProduct = 1;
} else if (sender.tag == 2) {
//buy 45
_whichProduct = 2;
} else if (sender.tag == 3) {
//buy 100
_whichProduct = 0;
} else if (sender.tag == 4) {
//double
_whichProduct = 3;
} else if (sender.tag == 5) {
//remove ads + 20
_whichProduct = 4;
}
SKPayment *payment = [SKPayment paymentWithProduct:_products[_whichProduct]];
[[SKPaymentQueue defaultQueue] addPayment:payment];
[self showLoadingWithText:#"Purchasing item..."];
}
#pragma mark - Loading Stuff
- (void)couldNotConnectToStoreWithText:(NSString *)text {
[_spinner removeFromSuperview];
[_spinner startAnimating];
[_spinnerLabel setText:text];
}
- (void)hideLoading {
[UIView animateWithDuration:0.5f animations:^{
_spinnerBackground.alpha = 0;
} completion:^(BOOL finished) {
[_spinner removeFromSuperview];
[_spinnerLabel removeFromSuperview];
[_spinnerBackground removeFromSuperview];
}];
}
- (void)showLoadingWithText:(NSString *)text {
[_spinnerBackground addSubview:_spinner];
[_spinnerBackground addSubview:_spinnerLabel];
_spinnerLabel.text = text;
[self.view addSubview:_spinnerBackground];
[self.view bringSubviewToFront:_spinnerBackground];
[UIView animateWithDuration:0.5f animations:^{
_spinnerBackground.alpha = 0;
}];
}
#pragma mark - iAP
- (void)getProductID {
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIDs];
request.delegate = self;
[request start];
} else {
[self couldNotConnectToStoreWithText:#"Error: Could not connect to store. Try enabling in-app-purchases in settings"];
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count != 0) {
_products = products;
[self hideLoading];
} else {
[self couldNotConnectToStoreWithText:#"Error: Could not connect to iTunes"];
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
NSLog(#"Product not found: %#", product);
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
- (void)unlockPurchase {
NSLog(#"unlocked:");
if (_whichProduct == 1) {
//buy 20
NSLog(#" buy20");
} else if (_whichProduct == 2) {
//buy 45
NSLog(#" buy45");
} else if (_whichProduct == 0) {
//buy 100
NSLog(#" buy100");
} else if (_whichProduct == 3) {
//double
NSLog(#" buydouble");
} else if (_whichProduct == 4) {
//remove ads + 20
NSLog(#" buyads");
}
[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Purchase successful!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[self hideLoading];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self unlockPurchase];
NSLog(#"restore");
}
- (void)restore {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Thanks
In your code :
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
please add break; statement as below
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// we want skip next case
break; // skipping next "case"
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
The problem is next case is executed after first. It should be skipped explicitly. That is common problem in c-like languages.
languages. We sometimes forget about this.