i am newbie on ios programming and this is my first time to use in app purchases. All of my IAPs are consumable and i have no error on my code. However, somethings are wrong. Because i tested my IAPs with sandbox environment and when i tried to purchase my first item which is tier 1, alert view showed me tier 5 item.
You can check all of my code about IAP:
#interface ViewController (){
NSString *currencyString;
SKProduct *validProduct;
}
- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction {
VerificationController * verifier = [VerificationController sharedInstance];
[verifier verifyPurchase:transaction completionHandler:^(BOOL success) {
if (success) {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"Transaction successful!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Purchase Unsuccessful"
message:#"Your purchase failed. Please try again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
}];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:#"100LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 100;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"500LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 500;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"1000LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 1000;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"5000LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 5000;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"10000LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 10000;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"50000LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 50000;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
else if ([productIdentifier isEqualToString:#"100000LJ"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int j = [defaults integerForKey:#"j"];
j += 100000;
[defaults setInteger:j forKey:#"j"];
[defaults synchronize];
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:{
}
break;
case SKPaymentTransactionStatePurchased:{
[_activityView stopAnimating];
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
break;
case SKPaymentTransactionStateRestored:{
[_activityView stopAnimating];
[self validateReceiptForTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];}
break;
case SKPaymentTransactionStateFailed:{
[_activityView stopAnimating];
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Purchase Unsuccessful"
message:#"Your purchase failed. Please try again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];}
break;
default:{}
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[formatter setLocale:validProduct.priceLocale];
currencyString = [formatter stringFromNumber:validProduct.price];
if([validProduct.productIdentifier isEqual: #"100LJ"]){
[_storebuy1 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"500LJ"]){
[_storebuy2 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"1000LJ"]){
[_storebuy3 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"5000LJ"]){
[_storebuy4 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"10000LJ"]){
[_storebuy5 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"50000LJ"]){
[_storebuy6 setTitle:currencyString forState:UIControlStateNormal];
}
else if([validProduct.productIdentifier isEqual: #"100000LJ"]){
[_storebuy7 setTitle:currencyString forState:UIControlStateNormal];
}
}
}
I have 7 buttons to buy IAP items.
This is my button to buy tier 1 IAP item:
- (IBAction)storebuy1:(UIButton *)sender{
_activityView=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[_activityView.layer setBackgroundColor:[[UIColor colorWithWhite: 0.0 alpha:0.30] CGColor]];
_activityView.center=self.view.center;
_activityView.hidesWhenStopped = YES;
[_activityView startAnimating];
[self.view addSubview:_activityView];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"100LJ"]];
request.delegate = self;
[request start];
if ([SKPaymentQueue canMakePayments]) {
SKPayment *payment = [SKPayment paymentWithProduct:validProduct];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Prohibited"
message:#"Parental Control is enabled!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
}
Hope you can help me.
Best Regards,
Taha
Because validProduct is set to whatever it was last set to when productsRequest:didReceiveResponse: was called, and that is unlikely to be what you want.
In general, your approach is wrong. You should be making one SKProductsRequest for all of your product identifiers and then storing all of the products that you get back. You can loop over the products to update your button titles (sort the products first). When a button is pressed you should then find the product with the appropriate identifier and create your SKPayment from that.
So, get rid of SKProduct *validProduct; and store the products in an array or a dictionary instead.
Related
I have been trying to connect a switch to an SMS text which worked out fine, i was even able to save the state the switch was left in.
However now i need to save the state only if the SMS is sent, and if the user doesn't send the message but a switch is already turned on it should then alert them with a AlertView.
So far i have been able to save the state the switch was left it and connect a label to signify if it is "ON" or "OFF" in the SMS.
- (void)viewDidLoad {
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
self->switch1.on = ([[standardDefaults stringForKey:#"switchKey"]
isEqualToString:#"On"]) ? (YES) : (NO);
if(switch1.on){
label.text = #"ON";
NSUserDefaults *defults = [NSUserDefaults standardUserDefaults];
[defults setObject:label.text forKey:#"labelkey"];
[defults synchronize];
} else label.text = #"OFF"; {
NSUserDefaults *defults = [NSUserDefaults standardUserDefaults];
[defults setObject:label.text forKey:#"labelkey"];
[defults synchronize];
}
label.text = [[NSUserDefaults standardUserDefaults]objectForKey:#"labelkey"];
}
- (IBAction)switchChanged:(UISwitch *)sender {
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
if (sender.on == 0) {
label.text = #"OFF";
[standardDefaults setObject:#"Off" forKey:#"switchKey"];
} else if (sender.on == 1) {
label.text = #"ON";
[standardDefaults setObject:#"On" forKey:#"switchKey"];
}
[standardDefaults synchronize];
}
This is how i send the SMS.
- (IBAction)sendRequest:(id)sender
{
MFMessageComposeViewController *messageVC = [[MFMessageComposeViewController alloc] init];
messageVC.body = [#[label.text] componentsJoinedByString:#""];
messageVC.recipients = #[_phoneNumber.text];
messageVC.messageComposeDelegate = self;
[self presentViewController:messageVC animated:NO completion:NULL];
}
By using MFMessageComposeViewControllerDelegate delegate, you can find Message is sent or cancelled by user.
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult) result
{
switch (result) {
case MessageComposeResultCancelled:
//show alert here as per your requirement break;
case MessageComposeResultFailed:
{
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Failed to send SMS!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[warningAlert show];
break;
}
case MessageComposeResultSent:
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Refer this link -> Send sms
- (void)uploadTicket:(GDataServiceTicket *)ticket
finishedWithEntry:(GDataEntryYouTubeVideo *)videoEntry
error:(NSError *)error
{
NSLog(#"ticket");
UIButton *uploadButton = (UIButton *)[backgroundImage viewWithTag:10];
UIButton *cancleButton = (UIButton *)[backgroundImage viewWithTag:20];
if (error == nil)
{
// tell the user that the add worked
NSLog(#"Video Successfully Uploaded to Youtube");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber *uploadedToYouTube = [defaults objectForKey:#"uploadedToYouTube"];
if(nil == uploadedToYouTube)
{
[defaults setObject:[NSNumber numberWithBool:YES] forKey:#"uploadedToYouTube"];
[defaults synchronize];
}
NSNumber *userOpenedYoutubeView = [defaults objectForKey:#"userOpenedYoutubeToUnlockTheme"];
if(nil != userOpenedYoutubeView)
{
// [defaults setBool:NO forKey:#"Unlock_Theme"];
[defaults setObject:[NSNumber numberWithBool:NO] forKey:#"Unlock_Theme"];
[defaults synchronize];
UIAlertView *alrtView = [[UIAlertView alloc] initWithTitle:#"Congrats...!" message:#"Your new theme is Unlocked" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alrtView show];
[alrtView release];
[self removeFromSuperview];
}
else
{
UIAlertView *alrtView = [[UIAlertView alloc] initWithTitle:#"Success...!" message:#"Your Video is successfully uploaded to Youtube" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alrtView show];
[alrtView release];
[self removeFromSuperview];
}
}
else {
NSLog(#"Fails to upload Video to Youtube");
UIAlertView *alrtView = [[UIAlertView alloc] initWithTitle:#"Sorry" message:#"Fails to upload video to Youtube. Please try again" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alrtView show];
[alrtView release];
}
mProgressView . hidden = YES;
uploadButton . hidden = NO;
cancleButton . enabled = YES;
[mProgressView setProgress: 0.0];
[self setUploadTicket:nil];
}
Every time i try to upload it is showing alert message "failed to upload". I don't get why it is showing like that. Before upgrading to Xcode 7 it works fine. Anyone know please help me.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I'm having trouble in create a NSArray class Method with Text Fields strings to use in another views controllers classes.
Let me show you what i have done:
First,in settings view controller, i'm collecting information in 3 text fields and saving with NSUserdefaults:
- (void)viewDidLoad
{
// Get the stored data before the view loads
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *toEmail = [defaults objectForKey:#"toEmail"];
NSString *ccEmail = [defaults objectForKey:#"ccEmail"];
NSString *bccEmail = [defaults objectForKey:#"bccEmail"];
// Update the UI elements with the saved data
self.toEmailTextField.text = toEmail;
self.ccEmailTextField.text = ccEmail;
self.bccEmailTextField.text = bccEmail;
[super viewDidLoad];
[self sideBarButton];
[self dismissTextFields];
}
- (IBAction)toEmailAction:(id)sender {
NSString *toEmail = self.toEmailTextField.text;
// Store the data
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:toEmail forKey:#"toEmail"];
[defaults synchronize];
NSLog(#"Data saved");
}
- (IBAction)ccEmailAction:(id)sender {
NSString *ccEmail = self.ccEmailTextField.text;
// Store the data
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:ccEmail forKey:#"ccEmail"];
[defaults synchronize];
NSLog(#"Data saved");
}
- (IBAction)bccEmailAction:(id)sender {
NSString *bccEmail = self.bccEmailTextField.text;
// Store the data
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:bccEmail forKey:#"bccEmail"];
[defaults synchronize];
NSLog(#"Data saved");
}
Second I have another view controller with email API where I need to use the strings save with NSUserdefault in the settings view controller.(marked as text 1, text 2, and text 3)
#pragma mark - Email
- (IBAction)showEmail:(id)sender {
// Email Subject
NSString *emailTitle = #"GliLog Email";
// Email Content
NSString *messageBody = #"GliLog Email Test!!!";
// To address
NSArray *toRecipent = [NSArray arrayWithObject:#"text 1"];
NSArray *ccRecipient = [NSArray arrayWithObject:#"text 2"];
NSArray *bccRecipient = [NSArray arrayWithObject:#"text 3"];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];
[mc setToRecipients:toRecipent];
[mc setCcRecipients:ccRecipient];
[mc setBccRecipients:bccRecipient];
// Present mail view controller on screen
[self presentViewController:mc animated:YES completion:NULL];
}
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
How can i "pass" the strings from settings view controller to another view.
Best regards
You already have the answer in your first view, when reading the NSUserDefaults and storing to the strings:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *toEmail = [defaults objectForKey:#"toEmail"];
NSString *ccEmail = [defaults objectForKey:#"ccEmail"];
NSString *bccEmail = [defaults objectForKey:#"bccEmail"];
Just apply the same thing in second view controller,
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *toRecipent = [NSArray arrayWithObject:[defaults objectForKey:#"toEmail"]];
NSArray *ccRecipient = [NSArray arrayWithObject:[defaults objectForKey:#"ccEmail"]];
NSArray *bccRecipient = [NSArray arrayWithObject:[defaults objectForKey:#"bccEmail"]];
if you are storing the data in the NSUserDefaults, just get the value from it.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString * toEmail = [defaults objectForKey:#"toEmail"];
NSString * ccEmail = [defaults objectForKey:#"ccEmail"];
A nice and simple tutorial about NSUserDefaults
I am confused about how to and when to tell the user that they completed the purchase successfully. I got my application rejected during the app review process for this reason:
1. Launch app
2. Tap on learn about the benefits of subscription
3. Tap on Subscribe
4. Tap on Confirm and enter iTunes password
5. No further action occurs
And I am not sure when and how to tell the user they entered their info correctly since that is confirmed on the iTunes server.
I have an IAPHelper class which looks like this:
//
// IAPHelper.m
// BusinessPlan
//
// Created by MacOSLion on 8/12/13.
//
//
// 1
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
// 2
//#interface IAPHelper () <SKProductsRequestDelegate>
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper
{
// 3
SKProductsRequest * _productsRequest;
// 4
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (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);
// SET memory to yes and then use that later.
// Get user data.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
// First time on the app, so set the user cookie.
[standardUserDefaults setBool:YES forKey:#"subscriber"];
// Saving
[[NSUserDefaults standardUserDefaults] synchronize];
}
else
{
NSLog(#"Not purchased: %#", productIdentifier);
}
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
// retrieve the product information from iTunes Connect
- (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...");
_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];
// SET memory to yes and then use that later.
// Get user data.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
// First time on the app, so set the user cookie.
[standardUserDefaults setBool:YES forKey:#"subscriber"];
// Saving
[[NSUserDefaults standardUserDefaults] synchronize];
// Tell user that things are purchased.
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
// UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Success sending purchase request."
// message:#"Just press OK and wait a few moments while iTunes processes the request." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
//
// [message show];
}
- (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);
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Could not complete your transaction"
message:#"Please try again. If the error persists, please email support at: alex#problemio.com" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
// Add to top of file
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
// Add new method
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier
{
//NSLog(#"Provifing content for subsciber: ");
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Subscribed successfully!"
message:#"Now you can ask questions right on the app, and get our monthly business content." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
#end
And my class from which I start the transaction process:
#import "SubscriptionController.h"
// 1
#import "RageIAPHelper.h"
#import <StoreKit/StoreKit.h>
// 2
#interface SubscriptionController ()
{
NSArray *_products;
// Add new instance variable to class extension
NSNumberFormatter * _priceFormatter;
}
#end
#implementation SubscriptionController
// 3
- (void)viewDidLoad
{
[super viewDidLoad];
//self.refreshControl = [[UIRefreshControl alloc] init];
//[self.refreshControl addTarget:self action:#selector(reload) forControlEvents:UIControlEventValueChanged];
[self reload];
//[self.refreshControl beginRefreshing];
// Add to end of viewDidLoad
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.view.backgroundColor = [UIColor colorWithWhite:0.859 alpha:1.000];
}
// 4
- (void)reload
{
_products = nil;
//[self.tableView reloadData];
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products)
{
if (success)
{
_products = products;
//[self.tableView reloadData];
}
//[self.refreshControl endRefreshing];
}];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// 5
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _products.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"a");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
SKProduct * product = (SKProduct *) _products[indexPath.row];
cell.textLabel.text = product.localizedTitle;
// Add to bottom of tableView:cellForRowAtIndexPath (before return cell)
[_priceFormatter setLocale:product.priceLocale];
cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];
if ([[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = nil;
}
else
{
UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buyButton.frame = CGRectMake(0, 0, 72, 37);
[buyButton setTitle:#"Buy" forState:UIControlStateNormal];
buyButton.tag = indexPath.row;
[buyButton addTarget:self action:#selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = buyButton;
}
return cell;
}
//- (IBAction)subscribe:(id)sender
//{
// UIButton *buyButton = (UIButton *)sender;
// SKProduct *product = _products[buyButton.tag];
//
// NSLog(#"Buying %#...", product.productIdentifier);
// [[RageIAPHelper sharedInstance] buyProduct:product];
//}
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)productPurchased:(NSNotification *)notification
{
NSLog(#"PURCHASEDDDDDDDDD");
// NSString * productIdentifier = notification.object;
// [_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop)
// {
// if ([product.productIdentifier isEqualToString:productIdentifier])
// {
// // TODO:
// // Update how the button appears.
//
//
//// [self.table reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:idx inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
// *stop = YES;
// }
// }];
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Purchased successfully"
message:#":)" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
// PUSH TO CONFIRMATION
}
//- (IBAction)subscribe:(id)sender
//{
//
//}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (IBAction)createSub:(id)sender
{
UIButton *buyButton = (UIButton *)sender;
SKProduct *product = _products[buyButton.tag];
if ( product == nil)
{
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Pulling product data from iTunes..."
message:#"Please try again in a few moments." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
}
else
{
// MESSAGE PERSON THAT CAN'T CONNECT TO SERVER
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Success sending purchase request."
message:#"Just press OK and wait a few moments while iTunes processes the request." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
NSLog(#"Buying %#...", product.productIdentifier);
[[RageIAPHelper sharedInstance] buyProduct:product];
}
}
#end
Thank you for your help!
You should have some sort of UI update to tell the user that the payment was successful and the feature is now available/unlocked. Typically, this is done either with an update in your views to correspond to the new content, or a UIAlertView if there are no visual changes made.
The game that I am creating has a highScore integer variable that gets assigned when the player loses. I am using NSUsersDefaults class to save my high score. Here is my code that I am using:
-(void)saveScore {
[[NSUserDefaults standardUserDefaults] setInteger:score forKey:#"highScore"];
[defaults setInteger:score forKey:#"highScore"];
[defaults synchronize];
NSLog(#"High Score: %i ", highScore);
}
-(IBAction)buttonReleased:(id)sender {
[stopWatchTimer invalidate];
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
NSString *label0 = #"Hold to Start";
[labelText setText:label0];
if (score > 0) {
score--;
}
else {
score = 0;
NSLog(#"Changed score to 0");
}
if (score > highScore) {
[self saveScore];
NSString *scoreMessage =[[NSString alloc] initWithFormat:#"Congrats! You have a new High Score! Click Share High Score to share your score of: %i",score];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"High Score!" message:(NSString *)scoreMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
score = 0;
}
else {
NSString *scoreMessage =[[NSString alloc] initWithFormat:#"Game Over! Your score was: %i",score];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"GAME OVER!" message:(NSString *)scoreMessage delegate:nil cancelButtonTitle:#"Try Again" otherButtonTitles: nil];
[alert show];
[alert release];
score = 0;
}
- (void)viewDidLoad
{
[super viewDidLoad];
int highscore = [[NSUserDefaults standardUserDefaults] integerForKey: #"highScore"];
[stopWatchTimer invalidate];
stopWatchTimer=nil;
}
I have been wrestling with this for HOURS! What am I doing wrong?! Note: Can you explain it as simply as possible.
Thanks!
-Matt
Reading it:
int highscore = [[NSUserDefaults standardUserDefaults] integerForKey: #"highScore"];
It will most likely be the default value of int (i.e. 0) when the file is blank.
Also don't forget to force a write of your defaults to "disk" with synchronize:
-(void)saveScore {
NSUSerDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:score forKey:#"highScore"];
[defaults synchronize];
}
You can load the highscore either in viewDidLoad or even in your init (or initWithNibName) method since this part isn't dependent on your view being loaded.
You could declare a property on your Scores view that you set in the viewDidLoad method. Alternatively you could expose the UILabel of that scores class (if that's what you use) as a property of your scores class.
- (void)viewDidLoad:
{
...
self.scoresView.textLabel.text = [NSString stringWithFormat:#"%d", highScore];
...
}
There is a really simple highscore management system which I have written it even has online support. You can find it https://github.com/faizanaziz/HighScore