MFMailComposeViewController problem - iphone

This problem is probably not specific to MFMailComposeViewController, but that is where I am having the problem...
I am building the NSString
"myEmailString" for the messageBody of
the MFMailComposeViewController and
storing it in an iVar before
displaying the
MFMailComposeViewController as a
modal view controller.
I pass the string into the MFMailComposeViewController, then present it as a modal view controller.
When the modal view controller is dismissed, my iVar becomes invalid,
and the app crashes when I release the emailString iVar in dealloc
Code below, what am I doing wrong?
-(void)buildEmailMessage {
int mySection;
int myRow;
NSString *buildString = [NSString stringWithFormat:#"<b><p>Ten Essentials Check List</b><br />%#</p>", [myList valueForKey:#"listName"]];
for (mySection = 0; mySection < [[fetchedResultsController sections] count]; mySection ++) {
NSString *sectionName = [NSString stringWithFormat:#"<p><b>%# Group</b></p><ul>", [[[fetchedResultsController sections] objectAtIndex:mySection] name]];
buildString = [buildString stringByAppendingString:sectionName];
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:mySection];
for (myRow = 0; myRow < [sectionInfo numberOfObjects]; myRow ++) {
// Get the managedObject
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:myRow inSection:mySection];
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
//Get the related Item object
Item *item = [managedObject valueForKey:#"item"];
NSString *itemName = [NSString stringWithFormat:#"<li>%#</li>", item.itemName];
buildString = [buildString stringByAppendingString:itemName];
}
buildString = [buildString stringByAppendingString:#"</ul>"];
}
myEmailString = [NSString stringWithString:buildString];
NSLog(#"email string = :\n%#", myEmailString);
[self showPicker];
}
#pragma mark -
#pragma mark Send Mail
-(void)showPicker {
// This code can run on devices running iPhone OS 2.0 or later
// The MFMailComposeViewController class is only available in iPhone OS 3.0 or later.
// So, we must verify the existence of the above class and provide a workaround for devices running
// earlier versions of the iPhone OS.
// We display an email composition interface if MFMailComposeViewController exists and the device can send emails.
// We launch the Mail application on the device, otherwise.
NSLog(#"Checking OS for MFMailComposeViewController");
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil)
{
// We must always check whether the current device is configured for sending emails
if ([mailClass canSendMail])
{
[self displayComposerSheet];
}
else
{
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
// Displays an email composition interface inside the application. Populates all the Mail fields.
-(void)displayComposerSheet {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
picker.navigationBar.barStyle = UIBarStyleBlack;
[picker setSubject:#"Here is your gear check list!"];
// Attach an image to the email
NSString *path = [[NSBundle mainBundle] pathForResource:#"Checkmark_icon" ofType:#"png"];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:#"image/png" fileName:#"Checkmark_icon"];
// Fill out the email body text
//***** NOTE: This is where I pass the value from my iVar *****
// into the MFMailComposeViewController
//
NSString *emailBody = [NSString stringWithString:myEmailString];
[picker setMessageBody:emailBody isHTML:YES];
NSLog (#"DIsplaying Composer Sheet");
[self presentModalViewController:picker animated:YES];
[picker release];
}
// Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
//message.hidden = NO;
// Notifies users about errors associated with the interface
switch (result)
{
case MFMailComposeResultCancelled:
NSLog (#"Result: canceled");
break;
case MFMailComposeResultSaved:
NSLog (#"Result: saved");
break;
case MFMailComposeResultSent:
NSLog (#"Result: sent");
break;
case MFMailComposeResultFailed:
NSLog (#"Result: failed");
break;
default:
NSLog (#"Result: not sent");
break;
}
[self dismissModalViewControllerAnimated:YES];
// ***** NOTE: Line below was added to fix the invalid iVar problem *****
myEmailString = #"";
}
#pragma mark -
#pragma mark Workaround
// Launches the Mail application on the device.
-(void)launchMailAppOnDevice {
NSString *recipients = #"mailto:first#example.com?cc=second#example.com,third#example.com&subject=Here is your gear check list!";
NSString *body = myEmailString;
NSString *email = [NSString stringWithFormat:#"%#%#", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
- (void)dealloc {
[managedObjectContext release];
[fetchedResultsController release];
[tableview release];
[myList release];
[myEmailString release];
[super dealloc];
}

how is your ivar declared? is it declared as a property? in any case, it is not automatically retained for you.
Either you need to do
myEmailString = [[NSString stringWithString:buildString] retain];
or
self.myEmailString = [NSString stringWithString:buildString];
if you have myEmailString declared as
#property (nonatomic, retain) NSString *myEmailString
Think about it: if all ivars were automatically retained for you, then how would you have a variable that you didn't want to retain? That's why it doesn't work that way.

when you are creating the myEmail string in buildEmailMessage you are never retaining the string. Thus after leaving the function it is autoreleased. Your retain count then when dealloc is called will be 0, which will cause the crash. If you want to keep the variable you will need to have the line as follows
myEmailString = [[NSString stringWithString:buildString] retain];
then you can call [myEmailString release] safely

stringWithString: creates a new string and autoreleases it before returning it to you. Unless you retain the returned string, you don't need to release it in your dealloc method.

You should be retaining your string before storing it in your iVar:
myEmailString = [[NSString stringWithString:buildString] retain];
It becomes invalid without this due to it being autoreleased later during the execution of your program. This will also ensure it's still allocated when your destructor is called, preventing release crashing.

Related

E-mail dialog box is not open in iphone sdk

In my app, I am fetching the e-mail id from address box using PersonPicker view.
When i select any e-mail id, i try to open the e-mail dialog. But it will just open & close immediatly.
I can't able to solve this issue.
I got code from Here
My code is as follow:
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
// Get the first and the last name. Actually, copy their values using the person object and the appropriate
// properties into two string variables equivalently.
// Watch out the ABRecordCopyValue method below. Also, notice that we cast to NSString *.
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
// Compose the full name.
NSString *fullName = #"";
// Before adding the first and the last name in the fullName string make sure that these values are filled in.
if (firstName != nil) {
fullName = [fullName stringByAppendingString:firstName];
}
if (lastName != nil) {
fullName = [fullName stringByAppendingString:#" "];
fullName = [fullName stringByAppendingString:lastName];
}
// Get the multivalue e-mail property.
CFTypeRef multivalue = ABRecordCopyValue(person, property);
// Get the index of the selected e-mail. Remember that the e-mail multi-value property is being returned as an array.
CFIndex index = ABMultiValueGetIndexForIdentifier(multivalue, identifier);
// Copy the e-mail value into a string.
email = (NSString *)ABMultiValueCopyValueAtIndex(multivalue, index);
NSLog(#"%#",email);
// Create a temp array in which we'll add all the desired values.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:fullName];
// Save the email into the tempArray array.
[tempArray addObject:email];
// Now add the tempArray into the contactsArray.
[contactsArray addObject:tempArray];
NSLog(#"%#",contactsArray);
// Release the tempArray.
[tempArray release];
// Reload the table to display the new data.
[table reloadData];
// Dismiss the contacts view controller.
[contacts dismissModalViewControllerAnimated:YES];
[contacts release];
MFMailComposeViewController* Apicker = [[MFMailComposeViewController alloc] init];
if (Apicker != nil)
{
[Apicker setSubject:#""];
NSString * someString = nil;
someString=#"Google";
[Apicker setMessageBody:someString isHTML:YES];
NSArray *toRecipients = [NSArray arrayWithObjects:email, nil];
[Apicker setToRecipients:toRecipients];
Apicker.mailComposeDelegate = self;
[self presentModalViewController:Apicker animated:YES];
[Apicker release];
}
return NO;
}
I think it may be the issue of dismiss & present the modal view.
your problem with dismiss and present is that the two overlap. -- dismiss it and THEN show it kinda like you did BUT you run into a problem because the stuff is animated. dont animate there or delay the presetting till after the dismissal

how to do JSON parsing in Registration Page

I am facing trouble in JSON parsing. When I have completed the code and run this, then the problem is occurred:
no known class method for selector 'show alert:with message'.
Following is my code:
if (!isValid) {
[AppDelegate showAlert:#"Alert!" withMessage:strMessage];
}
return isValid;
}
-(void)registerUser{
[Loading startLoading:YES];
NSString *urlString = [NSString stringWithFormat:#"URL=//%#",Access_Token];
//username, password, name, email, country
NSString *parameter = [NSString stringWithFormat:#"firstname=%#&lastname=%#email=%#&username=%#&password=%#&confirmpassword=#",fnameField.text,lnameField.text,eamilField.text,unameField.text,passField.text,cpassField];
NSConnection *conn = [[NSConnection alloc] initWithDelegate:self];
[conn sendRequest:urlString withParaMeter:parameter withMethod:#"POST" withTag:1];
[conn startAsynchronousRequest];
}
-(void)NSConnection:(NSConnection*)conn didFailWithError:(NSError*)error withTag:(int)tag{
NSLog(#"error is:- %#",[error description]);
}
-(void)NSConnection:(NSConnection*)request didSuccessWithItems:(NSDictionary*)dicData withData:(NSData*)data withTag:(int)tag{
NSLog(#"all data is:- %#",dicData);
int returnCode = [[dicData valueForKeyPath:#"process.returncode"] intValue];
NSString *strMessage = #"";
returnCode = 0;
switch (returnCode) {
case 0:
break;
case 1:
strMessage = #"User not logged in/process Id not available.";
break;
case 2:
strMessage = #"Invalid parameters passed.";
break;
case 3:
strMessage = #"Access token doesn't exist.";
break;
default:
strMessage = #"User name allready exist.";
break;
}
[AppDelegate showAlert:#"Alert!" withMessage:strMessage];
[Loading stopLoading];
[self.navigationController popViewControllerAnimated:YES];
Please help me out.
Thats because you are trying to call that method on object AppDelegate
However the AppDelegate class does not implement it.
Hence the error. Make sure it is implemented.
Two chances for this issue
You are not declared the showAlert: withMessage: method signature in your #interface
You may be declared it as a instance method, you are calling it using the Class name.
Fixes:
Declare the method signature in your #interface
Either define your method as Class method else call it using your app delegate's instance

saving NSMutable Array Data

I have a UITableView that is populated using a NSMutableArray . I am using xcode 4.2
the data in the NSMutable array stays in case I switch applications but it gets erased in these two cases :
1-the user switch view and come back .
2-the application is completley closed (i.e. the user double click on the Main button and remove it from the list of running application)
here is the code that I am using
-(NSString *)dataFilePath{
NSString *dataFilePath;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
dataFilePath = [documentDirectory stringByAppendingPathComponent:#"Test App-Info.plist"];
return dataFilePath;
}
-(void)saveData{
[NSKeyedArchiver archiveRootObject:[data copy] toFile:[self dataFilePath]];
}
- (void)loadData
{
data = [NSKeyedUnarchiver unarchiveObjectWithFile:self.dataFilePath];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
...
//saving the history
NSArray *archivedArray =[NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
if (archivedArray == nil) {
data = [[NSMutableArray alloc] init];
}
else {
[self loadData];
[mainTableView reloadData];
}
}
Please let me know if I am missing something
Thanks
Edited :
the save data function is loaded in two locations :
1- the app that I am developing scans QR codes , so save data is called in the following function :
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
....
id<NSFastEnumeration> results =
[info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results){
// EXAMPLE: just grab the first barcode
break;
}
if(!symbol)
return;
// EXAMPLE: do something useful with the barcode data
theBarcodeString = symbol.data;
//adding the string to the list
[data addObject:theBarcodeString];
[self saveData];
[mainTableView reloadData];
[self endText];
stringLabel.text=theBarcodeString;
...
}
it is also called in when the data is edited :
-(IBAction)editTable{
UIBarButtonItem *leftItem;
[mainTableView setEditing:!mainTableView.editing animated:YES];
if (mainTableView.editing) {
leftItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(editTable)];
}
else {
leftItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
}
[self saveData];
[mainTableView reloadData];
}
You need to look into the following calls:
applicationDidEnterBackground
applicationWillEnterForeground
applicationDidBecomeActive
applicationWillTerminate
They are all methods of UIApplication. Your needs will dictate which of the above store and restore your data depending on when it should happen.

How to call a variable with a value calculated from one method in another method?

My code does a calculation upon a button press in "IBAction" and returns the result in a string as the "message" in "UIAlertView".
else{
NSString *str = [[NSString alloc] initWithFormat:#"You require an average GPA of at least %.2f to achieve your Goal of %#", gpagoal, (NSString *)[myPickerDelegate.myGoal objectAtIndex: [myPicker selectedRowInComponent:0]]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Nothing is Impossible"
message:str
delegate:self
cancelButtonTitle:#"Good Luck"
otherButtonTitles:#"Tweet",nil];
//show alert
[alert show];
[alert release];
NSLog(#"All Valid");
}
i have a problem of how to pull the "gpagoal" value from the calculation's "IBAction" method.
below is the code for the standalone button for tweeting, which works if i am able to port over the gpagoal value from the other method.
- (IBAction)sendEasyTweet:(id)sender {
// Set up the built-in twitter composition view controller.
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
// i have problem trying to pull the result "gpagoal" from the calculation's "IBAction" method as i dunno how to pull out variable from a method to another method.
NSString *str2 = [[NSString alloc] initWithFormat:#"I need an average GPA of at least %.2f this semester to achieve my Goal of %#", gpagoal, (NSString *)[myPickerDelegate.myGoal objectAtIndex: [myPicker selectedRowInComponent:0]]];
// Set the initial tweet text. See the framework for additional properties that can be set.
[tweetViewController setInitialText:str2];
// Create the completion handler block.
[tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
//NSString *output;
switch (result) {
case TWTweetComposeViewControllerResultCancelled:
// The cancel button was tapped.
//output = #"Tweet cancelled.";
NSLog(#"Tweet cancelled");
break;
case TWTweetComposeViewControllerResultDone:
// The tweet was sent.
//output = #"Tweet done.";
NSLog(#"Tweet done");
break;
default:
break;
}
// Dismiss the tweet composition view controller.
[self dismissModalViewControllerAnimated:YES];
}];
// Present the tweet composition view controller modally.
[self presentModalViewController:tweetViewController animated:YES];
}
You simply need a variable that is available throughout your class
// ViewController.h
...
#interface ViewController : UIViewController {
double gpagoal;
}
Make sure it is properly initialized and updated as needed.

NSMutableString append data error

I have AsyncSocket like this.
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
[data getBytes:&tdata];
if (tdata > 5) {
if(bheader){
if(!charS){
if([message isEqualToString:#"S"]){
CMSG = message;
charS=YES;
}
}
else{
NSMutableString *tmp = [[NSMutableString alloc] initWithString:#""];
[tmp appendString:CMSG]; <<<<< This is code error at loop 2,
[tmp appendString:message]; the first loop success but second is fail
CMSG = tmp;
[tmp release];
}
}
else{
if (message){
cmessage = [[NSString alloc]initWithFormat:#"%#%#",cmessage,message] ;
}
else
NSLog(#"Error converting received data into UTF-8 String");
cdata++;
if(cdata==idata) {
msgComplete=YES;
}
}
if (msgComplete) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:cmessage forKey:kNotificationMessage];
[notificationCenter postNotificationName:kNotification object:self userInfo:userInfo];
cmessage=#"";
CMSG=#"";
msgComplete=NO;
bheader=YES;
cdata=0;
charS=NO;
[cmessage release];
}
}
[sock readDataToLength:1 withTimeout:-1 tag:0];
}
This code fail at second loop at [tmp appendString:CMSG]; Thread 1: Program received signal "SIGABRT"
How fix this error ?
Thanks gurus,
CMSG is assigned to tmp but tmp is released right afterwards. CMSG needs to be retained throughout the loop.
CMSG = tmp; //<< should be CMSG = [tmp retain];
[tmp release];
Now you have another potential problem. You are using a deprecated method here
[data getBytes:&tdata];
if (tdata > 5) {
getBytes: was deprecated because of a potential buffer overrun and the result of getBytes is not appropriate to use for if(tdata > 5). If you want to check the length of bytes get that that from the NSData object.
I see an error that may or may not be the ultimate problem here. In the final conditional statement, you have:
cmessage=#"";
...
[cmessage release];
I don't think you should be releasing that string. I don't actually know all of the ins and outs of NSString objects that are defined in that way (as opposed to stringWithFormat: or initWithString:), but I don't think that you have ownership of that string object. You shouldn't have to release it.
Remove that release message and see if it helps.