Showing an UIAlertView causes EXC_BAD_ACCESS [duplicate] - iphone

This question already has answers here:
UIAlertView fails to show and results in “EXC_BAD_ACCESS” error
(6 answers)
Closed 8 years ago.
I'm using AFNetworking to make a web request. After the web request completes. I want a UIAlertView to be shown. I'm using ARC and the code seems to work on devices. If I use a simulator I get an error: EXC_BAD_ACCESS
What am I doing wrong?
UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[op setCompletionBlock:^(void) {
if(op.response.statusCode == 200) {
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
} else {
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
[postSubmitAlertView show];
}];
[op start];

The key problem is that UIKit stuff should be called on the main thread.
Note: For the most part, UIKit classes should be used only from an application’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your application’s user interface in any way.
UIKit Framework Reference
Looking at the docs for NSOperation under setCompletionBlock:
Discussion
The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it.
The simplest solution to modify your code is to call the UIKit stuff on the main thread
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *URL = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[op setCompletionBlock:^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
if(op.response.statusCode == 200) {
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
} else {
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
[postSubmitAlertView show];
});
}];
[op start];
}

The EXC_BAD_ACCESS is caused by accessing a released object. To avoid this make your call to UIAlertView kind of modal:
//Function body:
-(void)checkSaving
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Do you want to add these results to your database?"
message:#"\n\n"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Save", nil];
alert.alertViewStyle = UIAlertViewStyleDefault;
[alert show];
//this prevent the ARC to clean up :
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSDate *d;
d = (NSDate*)[d init];
while ([alert isVisible]) {
[rl runUntilDate:d];
}
}
//Your choice result:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if (buttonIndex == 1)//Save
{
//do something
}
if (buttonIndex == 0)//NO
{
//do something
}
}
//Register the functions in the interface declaration:
#interface yourViewController ()
-(void)checkSaving
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
//...
#end
//To call:
[self checkSaving];
I wish this will help you.

UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.responseString);
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
];
[postSubmitAlertView show];
Hope this will solve your problem.

Related

Post to Facebook not working from delegate

I have added FBSdk in my project along with SBJson classes, what files should i import in my second view controller to work my code ?
I want to post on Fb using the description under url so for that i need to include this code into my second view controller, but i am stuck in a part i founded that the below code should be added in the second view controller but the "delegate:self" not works, as i am doing this first time so can any one guide me how should i code in my second view controller keeping into account the values from the app delegate (How should i get the values from the app delegate of FB to the second view so that i can add this code in second code plz can any one correct this code)
facebook = [[Facebook alloc] initWithAppId:#"YOUR_APP_ID"];
// how should i take this id from the app delegate ?
NSArray* permissions = [NSArray arrayWithObjects:#"read_stream",
#"publish_stream", nil];
[facebook authorize:permissions delegate:self];
NSMutableDictionary* params = [[NSMutableDictionary alloc]
initWithCapacity:1]
[params setObject:#"Testing" forKey:#"name"];
[params setObject:#"IMAGE_URL" forKey:#"picture"];
[params setObject:#"Description" forKey:#"description"];
[facebook requestWithGraphPath:#"me/feed"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
Is the above code correct ?
How should i implement it in my second view controller in -(void)fb_share (method).
My App Delegate File is Like this :
#import "AppDelegate.h"
#import "ViewController.h"
#import "ServerRequests.h"
#import "DBManager.h"
#import "SBJson.h"
#include "FBSession.h"
#import "FBRequest.h"
#import "FBGraphUser.h"
NSString *const FBSessionStateChangedNotification = #"com.myapp:FBSessionStateChangedNotification";
#implementation AppDelegate
#synthesize userId;
#synthesize dbSnyced;
#synthesize fbId;
#synthesize loginView;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ self.fbId = #"-1";
self.dbSnyced = NO;
self.loginView = nil;
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.navigationController = [[[UINavigationController alloc] initWithRootViewController:self.viewController] autorelease];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
if (![self openSessionWithAllowLoginUI:NO]) {
[self showLogin:NO];
}
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self checkAccountLogin];
}
- (void) applicationDidEnterBackground:(UIApplication *)application
{
[self hideLogin:NO];
}
- (void) applicationWillTerminate:(UIApplication *)application
{
NSLog(#"applicationWillTerminate");
[self hideLogin:NO];
[FBSession.activeSession close];
}
- (void) checkAccountLogin
{
self.dbSnyced = NO;
if([ServerRequests serverIsReachableAlertIfNo:YES]) {
DBManager *db = [[[DBManager alloc] initWithPath:DB_NAME] autorelease];
NSDictionary *userDic = [[db smartQueryForArrayWithStatement:#"SELECT * FROM table WHERE id=1"] objectAtIndex:0];
NSString *loginType = [userDic objectForKey:#"login_type"];
if([loginType isEqualToString:#"none"]) {
NSLog(#"no login type");
[self showLogin:NO];
}
else {
self.userId = [[userDic objectForKey:#"user_id"] intValue];
if([loginType isEqualToString:#"Facebook"]) {
NSLog(#"Facebook account");
[FBSession.activeSession handleDidBecomeActive];
}
else if([loginType isEqualToString:#"MyApp"]) {
NSLog(#"MyApp account");
}
}
}
}
#pragma - Fb stuff
/*
* Callback for session changes.
*/
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
NSLog(#"sessionStateChanged");
switch (state) {
case FBSessionStateOpen:
if (!error) {
[self handleFbUserDetails];
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
[[NSNotificationCenter defaultCenter] postNotificationName:FBSessionStateChangedNotification object:session];
if (error) {
NSLog(#"error: %#", error.localizedDescription);
if(self.loginView) {
[self.loginView hideLoading];
}
}
}
/*
* Opens a Facebook session and optionally shows the login UX.
*/
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
return [FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
/*
* If we have a valid session at the time of openURL call, we handle
* Facebook transitions by passing the url argument to handleOpenURL
*/
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
// attempt to extract a token from the url
return [FBSession.activeSession handleOpenURL:url];
}
- (BOOL) activeFBSession
{
return FBSession.activeSession.isOpen;
}
- (void) closeFBSession
{
NSLog(#"closeFBSession");
[FBSession.activeSession closeAndClearTokenInformation];
}
- (void) showLogin:(BOOL)animated
{
if(!self.loginView) {
self.loginView = [[[LoginViewController alloc] init] autorelease];
[[self.navigationController topViewController] presentViewController:self.loginView animated:animated completion:nil];
}
}
- (void) hideLogin:(BOOL)animated
{
if(self.loginView) {
[self.viewController gotToTab:0];
[self.loginView hideLoading];
[[self.navigationController topViewController] dismissViewControllerAnimated:animated completion:^{
self.loginView = nil;
NSLog(#"self.loginView = nil");
}];
}
}
- (void) handleFbUserDetails
{
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
NSLog(#"fb connected");
self.fbId = [user objectForKey:#"id"];
self.userId = -1;
ASIFormDataRequest *request = [ServerRequests ASIFormDataRequestForSrcipt:#"facebookUser"];
[request setPostValue:[user objectForKey:#"id"] forKey:#"fbId"];
[request setPostValue:user.first_name forKey:#"first_name"];
[request setPostValue:user.last_name forKey:#"last_name"];
[request setPostValue:user.location.name forKey:#"location"];
[request setCompletionBlock:^{
NSString *response = [request responseString];
NSLog(#"facebookUser response: %#", response);
if([response rangeOfString:#"ERROR"].location == NSNotFound) {
self.userId = [response intValue];
DBManager *db = [[DBManager alloc] initWithPath:DB_NAME];
NSString *q = [NSString stringWithFormat:#"UPDATE table SET login_type = 'Facebook', user_id = '%#' WHERE id = 1", response];
NSLog(#"%#", q);
[db executeStatement:q];
[db release];
}
else {
}
[self sessionBecameActive];
}];
[request startAsynchronous];
[self hideLogin:YES];
}
else {
NSLog(#"error: %#", error.localizedDescription);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Facebook Request Error" message:#"Cannot connect to Facebook." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
alert.delegate = self;
[alert show];
[alert release];
if(self.loginView) {
[self.loginView hideLoading];
}
}
[ServerRequests SyncDataBase];
}];
}
- (void) signOut
{
[self closeFBSession];
DBManager *db = [[DBManager alloc] initWithPath:DB_NAME];
[db executeStatement:#"UPDATE table SET login_type = 'none' WHERE id = 1"];
[db release];
[self showLogin:YES];
}
- (void) sessionBecameActive
{
[self.viewController sessionBecameActive];
}
#end
Can any one figure out what code should i write in the -(void)fb_share method in second view controller by getting values from the delegate ?
Any Help !
I did this ... and getting Error: HTTP status code: 400 ... but the user name get's displayed on alert
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
// NSArray* permissions = [NSArray arrayWithObjects:#"read_stream",
// #"publish_stream", nil];
// [facebook authorize:permissions delegate:self];
self.params = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"https://developers.facebook.com/ios", #"link",
#"https://developers.facebook.com/attachment/iossdk_logo.png", #"picture",
#"Facebook SDK for iOS", #"name",
#"Build great social apps and get more installs.", #"caption",
#"The Facebook SDK for iOS makes it easier and faster to develop Facebook integrated iOS apps.", #"description",
nil];
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Upload to FB?"
message:[NSString stringWithFormat:#"Upload to ""%#"" Account?", user.name]
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"No",#"Yes", nil];
[FBRequestConnection startWithGraphPath:#"me/feed"
parameters:self.params
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Success"
message:#"PostUploaded"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
}];
[tmp show];
[tmp release];
}
}];
.. what should i do now ?
declare facebook as a property in .h file of your appDelegate
then in second view controller import AppDelegate
in .m file where you want the Facebook object
get it like
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
Facebook *facebook = appDelegate.facebook;

Advertisements not removed after IAP unless app is restarted?

In my app, there is code that, if a button is pressed the user can upgrade the app and remove the advertisements, it calls the MKStoreKit and once the user purchases the upgrade, the adverts do remain in the app UNTIL the app is restarted.
I want to fix this, so the advertisements are removed immediately, but how would I go about doing this?
Here is some of the code;
-(IBAction)removeAds:(id)sender
{
if (![MKStoreManager isFeaturePurchased:#"com.davidsapps.puzzler.removeads"]) { //
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Upgrade App" message:#"To upgrade and remove all advertisements from this app Please click OK to purchase the upgrade!" delegate:self cancelButtonTitle:#"No Thanks" otherButtonTitles:#"Yes Please",nil];
[alert show];
[alert release];
}
}
The alert it links to;
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
//cancel button clicked. Do something here or nothing here
}
else{
//other button indexes clicked
[[MKStoreManager sharedManager] buyFeature:#- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0){
//cancel button clicked. Do something here or nothing here
}
else{
//other button indexes clicked
[[MKStoreManager sharedManager] buyFeature:#"com.idevver.mybatterypal.upgrade"];
}
}
}
Using MKStoreKit
Is there anything I can do to make it remove the advertisements without the user having to restart the app?
Thanks,
Chris
EDIT - the advertisements are Admob Mediation, using this code in the ViewDidLoad
- (void)viewDidLoad {
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
bannerView_.adUnitID = MY_BANNER_UNIT_ID;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
CGFloat screenXPos = (screenWidth /2);
CGFloat screenYPos = screenHeight - 90;
[bannerView_ setCenter:CGPointMake(screenXPos, screenYPos)];
bannerView_.rootViewController = self;
bannerView_.adUnitID = MY_BANNER_UNIT_ID;
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
This is the code that actually calls the advertisement banner, it is in an if/else statement later in the viewDidLoad
if (![MKStoreManager isFeaturePurchased:#"com.davidsapps.puzzler.removeads"]) { //
[bannerView_ loadRequest:[GADRequest request]];
}
So I guess somehow when the feature is purchased, I need to add to the MKStoreManager.m file something to say dismiss the bannerView_ code above?
Would that work?
EDIT 2 - Full .m file for the in-app code (with thanks to MugunthKumar for a wonderful kit)
//
// MKStoreManager.m
// MKStoreKit
//
// Created by Mugunth Kumar on 17-Nov-2010.
// Copyright 2010 Steinlogic. All rights reserved.
// File created using Singleton XCode Template by Mugunth Kumar (http://mugunthkumar.com
// Permission granted to do anything, commercial/non-commercial with this file apart from removing the line/URL above
// Read my blog post at http://mk.sg/1m on how to use this code
// As a side note on using this code, you might consider giving some credit to me by
// 1) linking my website from your app's website
// 2) or crediting me inside the app's credits page
// 3) or a tweet mentioning #mugunthkumar
// 4) A paypal donation to mugunth.kumar#gmail.com
//
// A note on redistribution
// While I'm ok with modifications to this source code,
// if you are re-publishing after editing, please retain the above copyright notices
#import "MKStoreManager.h"
#import "GADBannerView.h"
#import "AppDelegate.h"
#interface MKStoreManager (PrivateMethods)
- (void) requestProductData;
- (BOOL) canCurrentDeviceUseFeature: (NSString*) featureID;
- (BOOL) verifyReceipt:(NSData*) receiptData;
- (void) enableContentForThisSession: (NSString*) productIdentifier;
#end
#implementation MKStoreManager
#synthesize purchasableObjects = _purchasableObjects;
#synthesize storeObserver = _storeObserver;
static NSString *ownServer = nil;
static __weak id<MKStoreKitDelegate> _delegate;
static MKStoreManager* _sharedStoreManager;
- (void)dealloc {
[_purchasableObjects release];
[_storeObserver release];
[_sharedStoreManager release];
[super dealloc];
}
#pragma mark Delegates
+ (id)delegate {
return _delegate;
}
+ (void)setDelegate:(id)newDelegate {
_delegate = newDelegate;
}
#pragma mark Singleton Methods
+ (MKStoreManager*)sharedManager
{
#synchronized(self) {
if (_sharedStoreManager == nil) {
#if TARGET_IPHONE_SIMULATOR
NSLog(#"You are running in Simulator MKStoreKit runs only on devices");
#else
_sharedStoreManager = [[self alloc] init];
_sharedStoreManager.purchasableObjects = [[NSMutableArray alloc] init];
[_sharedStoreManager requestProductData];
_sharedStoreManager.storeObserver = [[MKStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:_sharedStoreManager.storeObserver];
#endif
}
}
return _sharedStoreManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
#synchronized(self) {
if (_sharedStoreManager == nil) {
_sharedStoreManager = [super allocWithZone:zone];
return _sharedStoreManager; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#pragma mark Internal MKStoreKit functions
- (void) restorePreviousTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void) requestProductData
{
SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:
kFeatureAId,
kConsumableFeatureBId,
kConsumableBaseFeatureId,
nil]];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
[self.purchasableObjects addObjectsFromArray:response.products];
#ifndef NDEBUG
for(int i=0;i<[self.purchasableObjects count];i++)
{
SKProduct *product = [self.purchasableObjects objectAtIndex:i];
NSLog(#"Feature: %#, Cost: %f, ID: %#",[product localizedTitle],
[[product price] doubleValue], [product productIdentifier]);
}
for(NSString *invalidProduct in response.invalidProductIdentifiers)
NSLog(#"Problem in iTunes connect configuration for product: %#", invalidProduct);
#endif
[request autorelease];
isProductsAvailable = YES;
if([_delegate respondsToSelector:#selector(productFetchComplete)])
[_delegate productFetchComplete];
}
// call this function to check if the user has already purchased your feature
+ (BOOL) isFeaturePurchased:(NSString*) featureId
{
return [[NSUserDefaults standardUserDefaults] boolForKey:featureId];
}
// Call this function to populate your UI
// this function automatically formats the currency based on the user's locale
- (NSMutableArray*) purchasableObjectsDescription
{
NSMutableArray *productDescriptions = [[NSMutableArray alloc] initWithCapacity:[self.purchasableObjects count]];
for(int i=0;i<[self.purchasableObjects count];i++)
{
SKProduct *product = [self.purchasableObjects objectAtIndex:i];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
[numberFormatter release];
// you might probably need to change this line to suit your UI needs
NSString *description = [NSString stringWithFormat:#"%# (%#)",[product localizedTitle], formattedString];
#ifndef NDEBUG
NSLog(#"Product %d - %#", i, description);
#endif
[productDescriptions addObject: description];
}
[productDescriptions autorelease];
return productDescriptions;
}
- (void) buyFeature:(NSString*) featureId
{
if([self canCurrentDeviceUseFeature: featureId])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Review request approved", #"")
message:NSLocalizedString(#"You can use this feature for reviewing the app.", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles:nil];
[alert show];
[alert release];
[self enableContentForThisSession:featureId];
return;
}
if ([SKPaymentQueue canMakePayments])
{
SKPayment *payment = [SKPayment paymentWithProductIdentifier:featureId];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"In-App Purchasing disabled", #"")
message:NSLocalizedString(#"Check your parental control settings and try again later", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles: nil];
[alert show];
[alert release];
}
}
- (BOOL) canConsumeProduct:(NSString*) productIdentifier
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
return (count > 0);
}
- (BOOL) canConsumeProduct:(NSString*) productIdentifier quantity:(int) quantity
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
return (count >= quantity);
}
- (BOOL) consumeProduct:(NSString*) productIdentifier quantity:(int) quantity
{
int count = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
if(count < quantity)
{
return NO;
}
else
{
count -= quantity;
[[NSUserDefaults standardUserDefaults] setInteger:count forKey:productIdentifier];
return YES;
}
}
-(void) enableContentForThisSession: (NSString*) productIdentifier
{
if([_delegate respondsToSelector:#selector(productPurchased:)])
[_delegate productPurchased:productIdentifier];
}
#pragma mark In-App purchases callbacks
// In most cases you don't have to touch these methods
-(void) provideContent: (NSString*) productIdentifier
forReceipt:(NSData*) receiptData
{
if(ownServer != nil && SERVER_PRODUCT_MODEL)
{
// ping server and get response before serializing the product
// this is a blocking call to post receipt data to your server
// it should normally take a couple of seconds on a good 3G connection
if(![self verifyReceipt:receiptData]) return;
}
NSRange range = [productIdentifier rangeOfString:kConsumableBaseFeatureId];
NSString *countText = [productIdentifier substringFromIndex:range.location+[kConsumableBaseFeatureId length]];
int quantityPurchased = [countText intValue];
if(quantityPurchased != 0)
{
int oldCount = [[NSUserDefaults standardUserDefaults] integerForKey:productIdentifier];
oldCount += quantityPurchased;
[[NSUserDefaults standardUserDefaults] setInteger:oldCount forKey:productIdentifier];
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
}
[[NSUserDefaults standardUserDefaults] synchronize];
if([_delegate respondsToSelector:#selector(productPurchased:)])
[_delegate productPurchased:productIdentifier];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"In-App Upgrade" message:#"Successfull removal of advertisements upgrade - thankyou" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
- (void) transactionCanceled: (SKPaymentTransaction *)transaction
{
#ifndef NDEBUG
NSLog(#"User cancelled transaction: %#", [transaction description]);
#endif
if([_delegate respondsToSelector:#selector(transactionCanceled)])
[_delegate transactionCanceled];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[transaction.error localizedFailureReason]
message:[transaction.error localizedRecoverySuggestion]
delegate:self
cancelButtonTitle:NSLocalizedString(#"Dismiss", #"")
otherButtonTitles: nil];
[alert show];
[alert release];
}
#pragma mark In-App purchases promo codes support
// This function is only used if you want to enable in-app purchases for free for reviewers
// Read my blog post http://mk.sg/31
- (BOOL) canCurrentDeviceUseFeature: (NSString*) featureID
{
NSString *uniqueID = [[UIDevice currentDevice] uniqueIdentifier];
// check udid and featureid with developer's server
if(ownServer == nil) return NO; // sanity check
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", ownServer, #"featureCheck.php"]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSString *postData = [NSString stringWithFormat:#"productid=%#&udid=%#", featureID, uniqueID];
NSString *length = [NSString stringWithFormat:#"%d", [postData length]];
[theRequest setValue:length forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* urlResponse = nil;
NSError *error = [[[NSError alloc] init] autorelease];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&urlResponse
error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
BOOL retVal = NO;
if([responseString isEqualToString:#"YES"])
{
retVal = YES;
}
[responseString release];
return retVal;
}
// This function is only used if you want to enable in-app purchases for free for reviewers
// Read my blog post http://mk.sg/
-(BOOL) verifyReceipt:(NSData*) receiptData
{
if(ownServer == nil) return NO; // sanity check
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", ownServer, #"verifyProduct.php"]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSString *receiptDataString = [[NSString alloc] initWithData:receiptData encoding:NSASCIIStringEncoding];
NSString *postData = [NSString stringWithFormat:#"receiptdata=%#", receiptDataString];
[receiptDataString release];
NSString *length = [NSString stringWithFormat:#"%d", [postData length]];
[theRequest setValue:length forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* urlResponse = nil;
NSError *error = [[[NSError alloc] init] autorelease];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&urlResponse
error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
BOOL retVal = NO;
if([responseString isEqualToString:#"YES"])
{
retVal = YES;
}
[responseString release];
return retVal;
}
#end
When the purchase completes, it is up to you to remove the ads. Do something similar to this.
[bannerView removeFromSuperview];
All you have to do is hide the ads when the purchase completes.

ALAssetLibrary errors from App store but not in if deployed from Xcode

My application appears to work fine when deployed to my iPhone via Xcode however now that it is on the app store when I download it, it crashes. It must have worked for Apple as like this it would never have passed review????
I have tried other phones but they crash also. From looking at the crash logs the code below is the area of concern.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do a tasks in the background
library = [[ALAssetsLibrary alloc] init];
NSMutableArray *itemFileName = [[NSMutableArray alloc] init];
for (Item *it in tag.Items) {
[itemFileName addObject:it.name];
}
void (^assetEnumerator)( ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if(asset != NULL && [asset valueForProperty:ALAssetPropertyType] == ALAssetTypePhoto) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
NSString *fileName = [rep filename];
if ([itemFileName containsObject:fileName]) {
AssetView *assetView = [[AssetView alloc] initWithAsset:asset];
assetView.delegate = self;
assetView.fileName = fileName;
//assetView.index = counter;
[self.assetViews addObject:assetView];
//NSLog(#"%i",index);
}
}
};
void (^assetGroupEnumerator)( ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
}
else{
//[self performSelectorOnMainThread:#selector(reloadTableView) withObject:nil waitUntilDone:YES];
// Hide the HUD in the main tread
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableView];
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
[self loadImageTags];
if([self.assetViews count] != [tag.Items count]){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Info"
message:#"Some items have been deleted from the device. If none remain for this tag it will be deleted."
delegate:self
cancelButtonTitle:nil
otherButtonTitles: #"OK", nil];
[alert show];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.labelText = #"Cleaning...";
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self clearupOrphanedItems];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
});
});
}
});
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"Album Error: %# - %#", [error localizedDescription], [error localizedRecoverySuggestion]] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
NSLog(#"A problem occured %#", [error description]);
};
// Enumerate Albums
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure]; //HERE IS WHERE IT DIES!!!
});
I am not sure if the issue is to due with instantiating the ALAssetLibrary inside the block as either it way to works fine during debugging or even just if deployed to phone via Xcode
Can anyone shed some light on this for me?
To avoid coredata and threading issues, please make sure:
1) that you create only one alassetslibrary Instance for the complete lifecycle of the application. Therefore its recommended you initialize your assetslibrary Instance either in the appdelegate or using dispatch_once
2) Make sure you create the alassetslibrary Instance on the Main thread.
Cheers,
Hendrik

ASIHTTPRequest as instance variable and deallocating and releasing

"Informazioni.h file"
#interface Informazioni : UIViewController{
.....
ASIHTTRequest *mASIHTTPRequest;
}
#property (nonatomic, retain) ASIHTTRequest *mASIHTTPRequest;
----------------------------
#import "Informazioni.h"
#import "Globals.h"
#implementation Informazioni
#synthesize mImageViewImmagine;
....
#synthesize mASIHTTPRequest;
- (void)viewDidLoad {
[super viewDidLoad];
//start fetching based on id_prodotti
[self startFetch:mId_prodotto];
}
- (void) startFetch:(NSString *) pId_prodotto{
//activate ASIHTTPDownloadCache
NSURL *url = [NSURL URLWithString:[JSON_DESCRIZIONE stringByAppendingString:mId_prodotto]];//JSON_DATA
mASIHTTPRequest = [ASIHTTPRequest requestWithURL:url];
[mASIHTTPRequest setDelegate:self];
[mASIHTTPRequest startAsynchronous];
}
- (void)loadDataWithOperation: (NSString *) responseString{
NSLog(#"load data with operation");
NSDictionary *tempDict = [[responseString JSONValue] objectForKey:#"descrizione_prodotto"];
NSLog(#"descrizione_prodotto: %#",tempDict);
[self.mTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
NSLog(#"reloadData called");
}
//start
- (void)requestFinished:(ASIHTTPRequest *)request{
NSLog(#"SUCCESS Http fetching");
// Operation Queue init (autorelease)
NSOperationQueue *queue = [NSOperationQueue new];
// Create our NSInvocationOperation to call loadDataWithOperation, passing in nil
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation:)
object:[request responseString]];
// Add the operation to the queue
[queue addOperation:operation];
[operation release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"%#",[error localizedDescription]);
/*
NSLog(#"Error: %#",[error localizedDescription]);
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"DIUNAMAISHOP"
message:[error localizedDescription]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
*/
/*
//remove activity indicator
if (self.mActivityIndicator.mFlag == YES) {
[self.mActivityIndicator.view
performSelectorOnMainThread:#selector(removeFromSuperview)
withObject:nil waitUntilDone:YES];
}
*/
}
-(void) queueFinished:(ASIHTTPRequest *) queue{
//You could release the queue here if you wanted
NSLog(#"Queue finished");
}
// end
........
- (void)dealloc {
//safely dealllocate
[mASIHTTPRequest clearDelegatesAndCancel];
[mASIHTTPRequest release];
.....
[super dealloc];
NSLog(#"Informazioni deallocated");
}
#end
I simply pushed this view then pressing back will dealloc/release the viewcontroller..
- the problem is it crashes when i press back while it is fetching
- how can i overcome this any suggestion would do tnx
mASIHTTPRequest = [ASIHTTPRequest requestWithURL:url];
You're not retaining this request. You need to retain it to have a valid reference reference to then be able to cancel and release it. Either add a retain, or use self.mASIHTTPRequest.

Adding more than one confirmationalerts (UIAlertView)

Basically, what I try to do is to add multiple confirmationalerts...but I just cant get it to work. No matter what confirmationalert i press, the "Continue" button leads to the exact same thing (a body without text and a subject with "XXXX")...
Any idea how to make the confimationalerts to lead to different things?
EDIT 2; No matter what button I press (continue or dismiss), the app sends the user to mail.app...
-(IBAction)mail {
UIAlertView *mail = [[UIAlertView alloc] init];
[mail setTag:ALERTVIEW_MAIL_TAG];
[mail setTitle:#"Open mail"];
[mail setMessage:#"....."];
[mail setDelegate:self];
[mail addButtonWithTitle:#"Continue"];
[mail addButtonWithTitle:#"Dismiss"];
[mail show];
[mail release];
}
-(IBAction)feedback {
UIAlertView *feedback = [[UIAlertView alloc] init];
[feedback setTag:ALERTVIEW_TIPSA_TAG];
[feedback setTitle:#"Open mail"];
[feedback setMessage:#"....."];
[feedback setDelegate:self];
[feedback addButtonWithTitle:#"Continue"];
[feedback addButtonWithTitle:#"dismiss"];
[feedback show];
[feedback release];
}
- (void)showConfirmAlert
{
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if([alertView tag] == ALERTVIEW_FEEDBACK_TAG) {
NSURL *url = [[NSURL alloc] initWithString:#"mailto:?subject=XXXX"];
[[UIApplication sharedApplication] openURL:url];
[url release];
}
else if (buttonIndex == 1) {
}
else if ([alertView tag] == ALERTVIEW_MAIL_TAG) {
NSString *subject = #"YYYY";
NSString *body = #".....";
NSString *path = [NSString stringWithFormat:#"mailto:?subject=%#&body=%#", subject, body];
NSURL *url = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[[UIApplication sharedApplication] openURL:url];
}
else if (buttonIndex == 1) {
}
}
You'll need to set a tag on your UIAlertView objects and switch on them in your delegate method, this is why the delegate method takes in the UIAlertView, so you can do stuff based on which object the button was pressed.
#define ALERTVIEW_MAIL_TAG 100
#define ALERTVIEW_FEEDBACK_TAG 101
- (IBAction) feedback {
UIAlertView *feedback = [[UIAlertView alloc] init];
[feedback setTag:ALERTVIEW_FEEDBACK_TAG];
//...
}
- (IBAction) mail {
UIAlertView *mail = [[UIAlertView alloc] init];
[mail setTag:ALERTVIEW_MAIL_TAG];
}
-(void) alertView:(UIAlertView *) alertView clickedButtonAtIndex:(NSInteger) buttonIndex {
if([alertView tag] == ALERTVIEW_MAIL_TAG) {
//do stuff...
} else {
//do other stuff...
}
}
The delegate method is specified by the UIAlertViewDelegate protocol, you can't change that.
There are 2 things you can do:
Use 2 different delegates and specify a clickedButtonAtIndex-method for each class.
In the clickedButtonAtIndex-method first check which alertview has sended the message. This requires to tag the UIAlertView (see answer by Jacob Relkin) or to create an instance variable for each UIAlertView.
You should specify which of your buttons is the cancel button, and then you need to check which button was clicked and don't do anything if it was the cancel button. I.e., when you create the alert:
alertView.cancelButtonIndex = 1;
And when you get the button clicked message:
if (buttonIndex == alertView.cancelButtonIndex) return;