UIAlertView on main queue crashing. - iphone

I have an issue showing a UIAlertView on the main thread. I'm not sure why but it keeps crashing, despite me running on the main thread. The following block is on the background thread, but I have the alert on the main as below:
void (^removeFromCalendar)(NSString *, NSString *, EKEventStore *) = ^(NSString *error, NSString *eventKey, EKEventStore *eventDB) {
EKEvent *myEvent = [eventDB eventWithIdentifier:eventKey];
NSError *err = noErr;
if(myEvent != NULL && myEvent != (id)[NSNull null]) {
[eventDB removeEvent:myEvent span:EKSpanThisEvent error:&err];
} else {
// Event was not found, nothing to do
return;
}
[eventDB release];
if (!err || err == noErr) {
NSLog(#"Deleted event %#", myEvent.title);
// Show alert on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
});
return;
}
error = err.description;
};
If I comment out the bottom where it shows the alert, everything is fine. But for the alert, I keep getting a EXC_BAD_ACCESS error. Can somebody explain why? It's on the correct thread, and I cant for the life of me understand where the memory issue could come from!

May be you view is being released when you finish until you finish with the background queue. So, for safety why dont you use it like this;
...........
UIViewController __weak *myController = self;
dispathch_async(backgroundQueue, ^{
UIViewController __strong *myStrongController = myController;
...............
dispatch_async(dispatch_get_main_queue(), ^{
if(myStrongController){
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
}
});
}).

This is how you present an alert view:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"<#(NSString *)#>" message:#"<#(NSString *)#>" delegate:self cancelButtonTitle:#"<#(NSString *)#>" otherButtonTitles:nil];
[alert show];
[alert release];

Instead of using dispatch_async, why not use the objective C call:
[self performSelectorOnMainThread
You might have to package it up in its own method. Alternatively, call it using:
[self performSelector:#selector(myAlertMethod) withObject:nil afterDelay:0.25]
These methods have been tried and true since day 1.

Related

SWIFT: CDA to iOS 10 Health app

Is there any way to send a xml file (CDA) to health app from another application? Because my application is getting a xml file from a source and I want to send it directly to the new health app (iOS 10).
Create an HKCDADocumentSample and save it with an HKHealthStore.
First, check for authorization
(void) checkForAuthorization {
if ([HKHealthStore isHealthDataAvailable]) {
NSSet *setRead = [NSSet setWithObjects [HKObjectTypedocumentTypeForIdentifier:HKDocumentTypeIdentifierCDA], nil];
NSSet *setWrite = [NSSet setWithObjects:[HKObjectType documentTypeForIdentifier:HKDocumentTypeIdentifierCDA], nil];
[_store requestAuthorizationToShareTypes:setWrite readTypes:nil completion:^(BOOL success, NSError * _Nullable error) {
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
} else if (success) {
[self performSelectorOnMainThread:#selector(addDocumentToHealthApp) withObject:nil waitUntilDone:NO];
}
NSLog(#" Success = %#",success? #"YES" : #"NO");
} ];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Health Kit not supported in device." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
Second, add Record wmthod this will add a Health record to health app.
(void) addRecordToHealthApp
{
NSURL *cdaPath = [[NSBundle mainBundle] URLForResource:#"sample" withExtension:#"xml"];
NSString*stringPath = [cdaPath absoluteString];
NSData *dataOfCDAFile = [NSData dataWithContentsOfURL:[NSURL URLWithString:stringPath]];
NSDate *now = [NSDate date];
int daysToAdd = 7;
NSDate *newDate1 = [now dateByAddingTimeInterval:60*60*24*daysToAdd];
NSError *err;
HKCDADocumentSample *doc = [HKCDADocumentSample CDADocumentSampleWithData:dataOfCDAFile startDate:[NSDate date] endDate:newDate1 metadata:nil validationError:&err ];
UIAlertView *alert;
if (err) {
alert = [[UIAlertView alloc] initWithTitle:#"Error" message:err.localizedDescription delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
[_store saveObject:doc withCompletion:^(BOOL success, NSError * _Nullable error) {
NSLog("Stored %#",success?#"YES":#"NO");
}];
}

Warning: Attempt to present <SLTwitterComposeViewController: 0x233141c0> on <MainController: 0x2083ebb0> whose view is not in the window hierarchy

I am receiving this error when trying to post an image to Twitter using the following code, cannot think what I am doing wrong any ideas for how to solve?;
- (void)PostToTwitter
{
UIImage* pxImage = [[self GetResultImage] retain];
TWTweetComposeViewController *twitter = [[[TWTweetComposeViewController alloc] init] autorelease];
[twitter setInitialText:#"Twitter Pic"];
[twitter addImage:pxImage];
FaceAppDelegate* app = (FaceAppDelegate*)[[UIApplication sharedApplication] delegate];
[app.mainCtrl presentViewController:twitter animated:YES completion:NULL];
twitter.completionHandler = ^(TWTweetComposeViewControllerResult res)
{
if(res == TWTweetComposeViewControllerResultDone)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Success" message:#"You have posted successfully." delegate:self cancelButtonTitle:#"Close" otherButtonTitles: nil];
[alert show];
[alert release];
}
else if(res == TWTweetComposeViewControllerResultCancelled)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Cancelled" message:#"You have cancelled posting to Twitter." delegate:NULL cancelButtonTitle:#"Close" otherButtonTitles: nil];
[alert show];
[alert release];
}
[app.mainCtrl dismissModalViewControllerAnimated:YES];
};
[pxImage release];
}
I think, that your view isn't presented. Try to use app.window.rootViewController, not property mainContr.

iPhone iOS 5 SDK in app SMS error

thanks in advance every time I call to create an in app SMS i get the following error...
Application tried to push a nil view controller on target .
This is the code I have that worked in OS 4 SDK...
MFMailComposeViewController *MailController;
MFMessageComposeViewController *SMScontroller;
NSError *error;
NSString *EmailMessage;
NSString *SubjectMessage;
-(IBAction)sendInAppSMS
{
if([MFMessageComposeViewController canSendText])
{
SMScontroller = [[MFMessageComposeViewController alloc] init];
SMScontroller.messageComposeDelegate = self;
NSString *MessageString = #"Hello";
SMScontroller.body = MessageString;
SMScontroller.navigationBar.tintColor = [UIColor blackColor];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
[self presentModalViewController:SMScontroller animated:YES];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
}
else{
// alertView to tell user to setup a mail account.
NSString *message = [[NSString alloc] initWithFormat:#"To use this feature, you need to have an iPhone with SMS abilities."];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"SMS Account Not Detected" message:message delegate:nil cancelButtonTitle:#"Understood" otherButtonTitles:nil];
[alert show];
}
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
switch (result) {
case MessageComposeResultCancelled:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"I Like It" message:#"User cancelled sending the SMS"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
break;
case MessageComposeResultFailed:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"I Like It" message:#"Error occured while sending the SMS"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
break;
case MessageComposeResultSent:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"I Like It" message:#"SMS sent successfully..!"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
break;
default:
break;
}
[self dismissModalViewControllerAnimated:YES];
}
I believe if you are running this in the simulator you'll get the error since it doesn't have sms. I get the same error in simulator but if i connect to my phone it works fine.

UIAlert View-for yes/no condition

my app needs alert msg and if yes button pressed then one more alert msg and then i have to called a method.This is my code:
-(IBAction)resetPressed:(id)sender
{
NSString *title= [NSString stringWithFormat:#"Warning"];
NSString *message = [NSString stringWithFormat:#"Are you sure you want to Reset"];
NSString *ok = [NSString stringWithFormat:#"No"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:ok otherButtonTitles:#"Yes",nil];
[alert show];
[alert release];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag ==1)
{
NSString *title= [NSString stringWithFormat:#"Warning"];
NSString *message = [NSString stringWithFormat:#"Are you sure you want to Reset"];
NSString *ok = [NSString stringWithFormat:#"No"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:ok otherButtonTitles:#"Yes",nil];
alert.tag =2;
[alert show];
[alert release];
}
else if(alertView.tag ==2)
{
[self resetArray];
}
}
Thanks.
I'm not sure what your goal is but a few things look wrong to me anyways:
First of all you should create your strings this way:
NSString *title= #"Warning";
There's no need to use stringWithFormat in your case.
Then, it doesn't seem you properly set the first UIAlert's tag to 1, and the default value for tags is 0 so I guess the if statements in didDismissWithButtonIndex are never true.
Also, you should check which button was pressed using buttonIndex, otherwise you are going to show both alert and call [self resetArray] whichever button is pressed by the user.
Hope that helps.
In your code, you create the first alert, but never actually set the tag on it. You should do:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:ok otherButtonTitles:#"Yes",nil];
alert.tag = 1; //Or 2, or something.
[alert show];
[alert release];
Then the code in your delegate method will run.
Please define two separate UIAlertView in .h file
#interface XYZViewController:UIViewController
{
UIAlertView *firstAlertView;
UIAlertView *secondAlertView;
}
Now in your .m file modify as below:
-(IBAction)resetPressed:(id)sender
{
NSString *title= [NSString stringWithFormat:#"Warning"];
NSString *message = [NSString stringWithFormat:#"Are you sure you want to Reset"];
NSString *ok = [NSString stringWithFormat:#"No"];
if(firstAlertView == nil)
{
firstAlertView = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:ok otherButtonTitles:#"Yes",nil];
}
[firstAlertView show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView == firstAlertView)
{
NSString *title= [NSString stringWithFormat:#"Warning"];
NSString *message = [NSString stringWithFormat:#"Are you sure you want to Reset"];
NSString *ok = [NSString stringWithFormat:#"No"];
if(secondAlertView == nil)
{
secondAlertView = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:ok otherButtonTitles:#"Yes",nil];
}
[secondAlertView show];
}
else if(alertView == secondAlertView)
{
[self resetArray];
}
}
and in dealloc method please release the allocated UIAlertviews.
Hope i am clear to you.
Thanks,
Jim.

Resolving the wait situation for a detached thread in IPhone?

I am using a private MBProgressHUD
Now I am using the indicator view on my add button in which I am calling my addrecord service .
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
doneFlag = YES;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
[request release];
This is all running fine except the instruments gives leak of the uialertview may be it is conflicting with the mbprogreshud.
So I thought to remove the alert from the calling method and put it in the caller the method like this:
the caller method now :
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
//it should wait for the above line to be executing ******* then to exexute the be //below condition but how ?
if (doneFlag == NO) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
} else {
[favoritesButton setTitle:#"Remove" forState:UIControlStateNormal];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
}
else
{
doneFlag = YES;
}
[request release];
In the launching of the progresshud thread is detaching something like this :
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil]
Now My question is that If I follow the first scenario . How can I assure the the alertview leak will not come
Or If I am following the second scenario How can I assure the if condition will be executed after completing this line executed :
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
Regarding the first scenario, it is in general a bad idea to do UI updates from threads other than the applications main thread. UIKit is NOT thread safe and doing threaded UI updates can cause all sorts of strange things to happen. Now, I'm not sure if this is the cause for the leak but I would avoid showing an UIAlertView in addingToFavorites. Use performSelectorOnMainThread or the second scenario described below.
Regarding the second scenario, move everything below the showWhileExecuting call to the hudWasHidden delegate method. At this point you can be sure that your code was fully executed and the doneFlag was set.
To use performSelectorOnMainThread, define a new method, put your code in it and than call performSelectorOnMainThread.
I.e.,
- (void)showAlert {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"The network is not available.\n Please check the Internet connection." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
Call with,
[self performSelectorOnMainThread:#selector(showAlert) withObject:nil waitUntilDone:NO];
I would go with the second scenario though.
Other answers notwithstanding, you were creating the UIAlertView leak with this sequence:
[alert show];
alert = nil;
[alert release];
The last two lines should be swapped:
[alert show];
[alert release];
alert = nil;