• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Die Bildungsoffensive hier im Forum geht weiter! Jetzt sollen Kreativität und technische Möglichkeiten einen neue Dimension erreichen. Das Thema in diesem Monat lautet - Verkehrte Welt - Hier geht es lang --> Klick

MailComposer-SampleCode in eigene Anwendung einbinden verursacht einen Crash

denio

Empire
Registriert
23.01.10
Beiträge
88
Hey Leute,

hab mal wieder ein kleines Problem :)

Seit Version 3.0 besteht ja die Möglichkeit eMails direkt in einer Applikation zu schreiben und diese von dort aus zu versenden anstatt über
Code:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailto]];
das interne Mailprogramm aufzurufen und die eigene Anwendung zu verlassen. Soweit die Theorie :)

Ich habe mir dazu folgenden https://developer.apple.com/iphone/library/samplecode/MailComposer/Introduction/Intro.html Beispielcode von der AppleDeveloperseite heruntergeladen und angeschaut.

Im Beispielcode klappt es auch soweit ganz gut.

Hab nun versucht den Beispielcode in meine Anwendung einzubinden und den Beispielcode einwenig modifizierten und folgenden Code

Code:
-(void)showPicker
{
	NSLog(@"MCVC showPicker");
	// This sample 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.
	
	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];
	}
}


#pragma mark -
#pragma mark Compose Mail

// Displays an email composition interface inside the application. Populates all the Mail fields. 
-(void)displayComposerSheet 
{
	NSLog(@"MCVC displayComposerSheet");
	MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
	
	[picker setSubject:@"Hello from California!"];
	

	// Set up recipients
	NSArray *toRecipients = [NSArray arrayWithObject:@"[email protected]"]; 
	NSArray *ccRecipients = [NSArray arrayWithObjects:@"[email protected]", @"[email protected]", nil]; 
	NSArray *bccRecipients = [NSArray arrayWithObject:@"[email protected]"]; 
	
	[picker setToRecipients:toRecipients];
	[picker setCcRecipients:ccRecipients];	
	[picker setBccRecipients:bccRecipients];
	
	// Attach an image to the email
	NSString *path = [[NSBundle mainBundle] pathForResource:@"rainy" ofType:@"png"];
    NSData *myData = [NSData dataWithContentsOfFile:path];
	[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"rainy"];
	
	// Fill out the email body text
	NSString *emailBody = @"It is raining in sunny California!";
	[picker setMessageBody:emailBody isHTML:NO];
	
	[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 
{	
	NSLog(@"MCVC mailComposeController");
	// 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];
}


#pragma mark -
#pragma mark Workaround

// Launches the Mail application on the device.
-(void)launchMailAppOnDevice
{
	NSLog(@"MCVC launchMailAppOnDevice");
	NSString *recipients = @"mailto:[email protected][email protected],[email protected]&subject=Hello from California!";
	NSString *body = @"&body=It is raining in sunny California!";
	
	NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
	email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	
	[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}

in die jeweilige Datei eingebunden. Ich hab die Imports und das @synthesize gesetzt und das Framework MessageUI eingebunden und der Compiler übersetzt den Code auch sauber.

Aber die Applikation crashed immer mit folgender Fehlermeldung
Code:
2010-06-22 18:01:57.182 myApp[5036:4f0f] *** -[Data setIdData:]: unrecognized selector sent to instance 0x4b7fb90
2010-06-22 18:01:57.182 myApp[5036:4f0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[Data setIdData:]: unrecognized selector sent to instance 0x4b7fb90'
2010-06-22 18:01:57.183 myApp[5036:4f0f] Stack: (
    31220827,
    2475881737,
    31602747,
    31172214,
    31024834,
    59777,
    2135453,
    34718460,
    34742277,
    2131775,
    56764,
    1951124,
    1950979,
    36341172,
    36339220,
    36342418,
    36001745,
    31004897,
    31001672,
    1768693,
    56040,
    1593197,
    1592072,
    2545559581,
    2545559202
)


Das Merkwürde an der Sache ist, das der Crash passiert bevor ich an die entsprechenden Stelle im Code komme wo der obige Code ausgeführt wird?

Mein Programm liefert eine Tableview-Kurzübersicht mit Daten, der Nutzer kann sich dann für eine Row entscheiden und dann wird normalerweise der Datensatz dieser Row im Detail gezeigt und erst in der Detailsicht soll der Nutzer per Knopfdruck die Mailanwendung starten können, aber der Crash kommt bereits in der Tableview-Kurzübersicht zustande?

Die Tableview-Kurzübersicht und die Detailansicht werden direkt per Code und ohne Interfacebuilder XIBs dynamisch aufgebaut.

Wäre echt super wenn jemand bescheid wüsste besten Dank schon mal denio :)
 
1) Breakpoints setzen, dann weisst du mehr oder weniger genau die Zeilen vor und nach dem Fehler - je nachdem, wie du die jeweiligen Breakoints gesetzt hast.

2) Schau mal dort nach, wo Instanzen der Klasse "Data" vorkommen.

3) am eingefügten Code kann es fast nicht liegen, denn wie du sagst, wird dieser nie ausgeführt und zweitens werden "Data"-Instanzen nicht gebraucht.
 
1) Breakpoints setzen, dann weisst du mehr oder weniger genau die Zeilen vor und nach dem Fehler - je nachdem, wie du die jeweiligen Breakoints gesetzt hast.

2) Schau mal dort nach, wo Instanzen der Klasse "Data" vorkommen.

3) am eingefügten Code kann es fast nicht liegen, denn wie du sagst, wird dieser nie ausgeführt und zweitens werden "Data"-Instanzen nicht gebraucht.

Hey Poljpocket hab mich heute mal wieder mit dem MailComposer beschäftigt nachdem ich ihn erstmal entfernt hatte und mich um andere Sachen gekümmert habe.

Ich habe mich bei dem Parser der Applikation an dem Cocoa-XML-Parser aus dem Beispiel-Code von Apple orientiert und der funktioniert auch sehr schnell und gut. Aber sobald ich den MailComposer-Code einbinden crashed die Appl.

Der Crash kamm immer in der Methode

Code:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

aus der XML-Parser-Klasse zustande, und zwar nach der Zeile "if ([elementName isEqualToString:kName_Entry])" innerhalb der Methode egal welcher XML-String ausgelesen wird. Zufällig war idDAta aus dem Data-Object der erste Wert. Also dachte ich mir ich kommentiere mal alle Zuweisungen aus. Hat aber ebenfalls nichts gebracht :/ Weil jetzt crashed die App. in der Methode
Code:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
aus der ViewController-Klasse, die die Übersicht aufbaut und zwar mit der selben Fehlermeldung lediglich mit einem anderem XML-Wert.

Leider kommt es auch hier zum Crash in der Zeile "cell.textLabel.text = data.bla;" und wenn ich jetzt das "data.bla"
@"" ersetze. Kommt später in der Detail-Klasse ein Crash :/ sobald das erste Row dargestellt werden soll. Das kann doch irgendwie nicht sein :/ danach hatte ich keine Lust mehr den Crash "weiterzuführen".

Der Code läuft absolut fehlerfrei durch sobald die Parts mit dem MailComposer nicht mehr im Code sind :/. Ein Kollege von mir meinte das es an dem Delegate liegen könnte, aber dies wird ja erst aufgerufen/genutzt wenn ich den MailComposer aufrufe wozu ich ja noch nicht mal komme dank der Crashes :/
 
Wenn es dir nichts ausmacht, poste mal dein Projekt! Vom Schiff aus kann dir hier wohl keiner helfen.

Gruss ppocket
 
Wenn es dir nichts ausmacht, poste mal dein Projekt! Vom Schiff aus kann dir hier wohl keiner helfen.

Gruss ppocket

Heyy ppocket, kann ich leider nicht machen :/ ist ein Firmen-Intern Lösung nur für Mitarbeiter. Hab aber das Problem mittlerweile "eingegrenzt". Ich probiere ja viel über den Simulator und compiliere es nur für mein TestDevice wenn ich Funktionen testen will, die nur auf einem realen Device funktionieren. Nun ist mir aufgefallen, das die Anwendung auf dem Device fehlerfrei durchläuft? aber im Simulator crashd? also immer noch sehr merkwürdig :/

Naja hab mittlerweile die bisherige Anfrage genommen, mit neuen "Parser-Begriffen" angepasst und neu eingebunden und dort geht der Code problemlos durch auch auf dem Simulator, wobei die Struktur des Codes identisch ist bis auf einige Anpassungen. Der ursprüngliche Code Crased jedoch immer noch während der "neue" problemlos läuft? Offensichtlich reicht schon die alleinige Existenz des "MessageUI.Frameworks"?

Wirklich sehr merkwürdig :) naja et läuft und ich werd dies Nuss noch im Laufe der Woche "knacken" :) trotzdem Danke :D
 
Zuletzt bearbeitet:
Hey Leute hab mal wieder ein Frage :) sobald der inApp-ViewController dargestellt wird, kann der Benutzer ja nach belieben

1.) durch drücken der Abbrechen Taste zurück zum vorherigen View (bei editieren => abbrechen / sichern / nicht sichern)
2.) die Mail eintippen und mit Senden versenden

In der Methode "displayComposerSheet" werden die nötigen Vorbereitungen getroffen

Code:
- (void)displayComposerSheet:(NSString *)receivedMail
{
	MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
	picker.mailComposeDelegate = self;
	
	[picker setSubject:@""];
	
	// Set up recipients
	NSArray *toRecipients = [NSArray arrayWithObject:receivedMail]; 
	NSArray *ccRecipients = [NSArray arrayWithObjects:@"", nil]; 
	NSArray *bccRecipients = [NSArray arrayWithObject:@""]; 
	
	[picker setToRecipients:toRecipients];
	[picker setCcRecipients:ccRecipients];	
	[picker setBccRecipients:bccRecipients];
	
	// Fill out the email body text
	NSString *emailBody = (@"blabla");
	[picker setMessageBody:emailBody isHTML:NO];
	
	[self presentModalViewController:picker animated:YES];
	[picker release];
}

Je nach Aktion wird dann die Methode

Code:
- (void)mailComposeController:(MFMailComposeViewController*)controller
          didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error

{
	// 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];
}

aufgerufen und ich kann innerhalb der Methode im jeweiligen switch (result) bestimmte Aktionen durchführen wie z.B. ein AlertView aufrufen wie "Mail wurde erfolgreich versandt", oder so.

Nun zu meiner Frage: Ich würde gerne sofern die eMail versandt wurde den Inhalte der Felder

"An:"
"Kopie:"
"Blindkopie:"
"Betreff:"
"Den Inhalt der Mail"

in eine Datenbank eintragen, sofern der Nutzer auf senden geklickt hat und die Mail versendet wurde. Ich brauche die Funktion damit die gesendeten Mails auch in unserer Datenbank vorhanden sind und nicht nur im gesendet-Ordner der iPhones der jeweiligen Mitarbeiter :). Das würde Ihnen die Mühe ersparen jeden Abend vor Feierabend per Hand die gesendeten Mails in die Datenbank übertragen zu müssen :)

Eigentlich müssten ja diese Daten in dem ViewController picker vorhanden sein, aber dieser befindet sich ja nur innerhalb des Scoops der "displayComposerSheet"-Methode und wird nach dem Aufruf released. Kann ich diese Daten irgendwie an die Methode

Code:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error

weiterreichen und nutzen?

Wäre super wenn jemand bescheid wüsste und mir helfen kann :) besten Dank schon mal im voraus :D
 
In der Delegate Methode wird doch der Controller mitgegeben.
 
In der Delegate Methode wird doch der Controller mitgegeben.

Hey floorjiann Danke erstmal für den Hinweis, hab ich voll übersehen :innocent:. Hoffe das ist jetzt keine allzu doofe Frage, aber sag mal wie kann ich z.B. den Stringinhalt von bspw. "setToRecipients" oder "setMessageBody" ansprechen bzw. in einer NSLog ausgeben lassen?
 
Okay, mein Fehler. Es sind nur Setter vorhanden, ich dachte es wären komplette Properties.
Du kannst dir ja die Werte, welche du zuvor übergeben hast in einem NSDictionary o.Ä. speichern. Dann hast du auch in der Methode des Delegate-Protkolls Zugriff darauf.
 
Okay, mein Fehler. Es sind nur Setter vorhanden, ich dachte es wären komplette Properties.
Du kannst dir ja die Werte, welche du zuvor übergeben hast in einem NSDictionary o.Ä. speichern. Dann hast du auch in der Methode des Delegate-Protkolls Zugriff darauf.

Das Problem wird ja sein, das sich die Werte ändern, wenn der Nutzer z.B. das Subject ändert oder jemanden in den cc packt, selbiges gilt ja auch z.B. für "setMessageBody",etc.. Ich weiß halt nicht wie ich überprüfen soll, was der letzte Stand der Arrays ist, daher dachte ich halt ich checke beim Delegate die Werte und speichere diese dann ab, weil dann ist die Aktion ja bereits gelaufen.

Könnte ich mir die Getter nicht erzeugen, bzw. diese mit übergeben? oder ist dies bei Call-Methoden nicht vorgesehen?
 
In der Delegate Methode wird doch der Controller mitgegeben.

Hey floorjiann,

was müsste ich tun um eine eigene "Delegate/Callback"-Methode zu schreiben, um diese zu aufzurufen anstatt der vorgesehene

Code:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
"Delegate/Callback"-Methode?

Und kann ich der eigenen "Delegate/Callback-Methode" z.B. die letzten Werte für "setSubject", "emailBody", etc. mit übergeben zusätzlich zum "controller", "result" und "error"?

Ich kann mir vorstellen das ich hierfür in der Methode

Code:
- (void)displayComposerSheet:(NSString *)receivedMail
beim
Code:
picker.mailComposeDelegate = self;
den Delegate ändern muss, weis aber nicht ganz wie und wie ich ihm dabei die benötigten Werte für "setSubject",... mit geben soll.

Wäre super wenn ihr ein paar Tips hättet, besten dank schonmal :)