Send email from Cocoa
Solution 1
UPDATE: As others suggested, from 10.9 you can use NSSharingService that supports attachments as well!
Swift example:
let emailImage = NSImage.init(named: "ImageToShare")!
let emailBody = "Email Body"
let emailService = NSSharingService.init(named: NSSharingServiceNameComposeEmail)!
emailService.recipients = ["[email protected]"]
emailService.subject = "App Support"
if emailService.canPerform(withItems: [emailBody,emailImage]) {
// email can be sent
emailService.perform(withItems: [emailBody,emailImage])
} else {
// email cannot be sent, perhaps no email client is set up
// Show alert with email address and instructions
}
OLD UPDATE: My old answers worked fine until I had to sandbox my apps for the App Store.~~
Since then the only solution I found was using simply a mailto: link.
- (void)sendEmailWithMail:(NSString *) senderAddress Address:(NSString *) toAddress Subject:(NSString *) subject Body:(NSString *) bodyText {
NSString *mailtoAddress = [[NSString stringWithFormat:@"mailto:%@?Subject=%@&body=%@",toAddress,subject,bodyText] stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:mailtoAddress]];
NSLog(@"Mailto:%@",mailtoAddress);
}
Disadvantage: No attachment! If you know how to make it work on Mac let me know!
OLD ANSWER: You can Apple Script, Apple's scripting bridge framework (Solution 2) or a Python script (Solution 3)
Solution 1 (Apple script):
attachments is an array of stings containing file paths
- (void)sendEmailWithMail:(NSString *) toAddress withSubject:(NSString *) subject Attachments:(NSArray *) attachments {
NSString *bodyText = @"Your body text \n\r";
NSString *emailString = [NSString stringWithFormat:@"\
tell application \"Mail\"\n\
set newMessage to make new outgoing message with properties {subject:\"%@\", content:\"%@\" & return} \n\
tell newMessage\n\
set visible to false\n\
set sender to \"%@\"\n\
make new to recipient at end of to recipients with properties {name:\"%@\", address:\"%@\"}\n\
tell content\n\
",subject, bodyText, @"McAlarm alert", @"McAlarm User", toAddress ];
//add attachments to script
for (NSString *alarmPhoto in attachments) {
emailString = [emailString stringByAppendingFormat:@"make new attachment with properties {file name:\"%@\"} at after the last paragraph\n\
",alarmPhoto];
}
//finish script
emailString = [emailString stringByAppendingFormat:@"\
end tell\n\
send\n\
end tell\n\
end tell"];
//NSLog(@"%@",emailString);
NSAppleScript *emailScript = [[NSAppleScript alloc] initWithSource:emailString];
[emailScript executeAndReturnError:nil];
[emailScript release];
/* send the message */
NSLog(@"Message passed to Mail");
}
Solution 2 (Apple scriptingbridge framework):
You can use Apple's scriptingbridge framework to use Mail to send your message
Apple's exmaple link it's pretty straightforward, you only need to fiddle with adding a rule and Mail.app to your project. Read Readme.txt carefully.
Change "emailMessage.visible = YES;" to "emailMessage.visible = NO;" so it sends it in the background.
Disadvantage: users need to have valid accounts under Mail.
Solution 3 (Python Script (no user account):
You can also use a python script to send a message.
Disadvantage: users have to enter SMTP details unless you grab them from Mail (but then you can use Solution 1 above directly), or you have to have a reliable SMTP relay hardcoded in your app (you can set up a gmail account and use it for that purpose, however if your apps send too many emails google can delete your account (SPAM))
I use this python script:
import sys
import smtplib
import os
import optparse
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
username = sys.argv[1]
hostname = sys.argv[2]
port = sys.argv[3]
from_addr = sys.argv[4]
to_addr = sys.argv[5]
subject = sys.argv[6]
text = sys.argv[7]
password = getpass.getpass() if sys.stdin.isatty() else sys.stdin.readline().rstrip('\n')
message = MIMEMultipart()
message['From'] = from_addr
message['To'] = to_addr
message['Date'] = formatdate(localtime=True)
message['Subject'] = subject
#message['Cc'] = COMMASPACE.join(cc)
message.attach(MIMEText(text))
i = 0
for file in sys.argv:
if i > 7:
part = MIMEBase('application', 'octet-stream')
part.set_payload(open(file, 'rb').read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
message.attach(part)
i = i + 1
smtp = smtplib.SMTP(hostname,port)
smtp.starttls()
smtp.login(username, password)
del password
smtp.sendmail(from_addr, to_addr, message.as_string())
smtp.close()
And I call it form this method to send an email using a gmail account
- (bool) sendEmail:(NSTask *) task toAddress:(NSString *) toAddress withSubject:(NSString *) subject Attachments:(NSArray *) attachments {
NSLog(@"Trying to send email message");
//set arguments including attachments
NSString *username = @"[email protected]";
NSString *hostname = @"smtp.gmail.com";
NSString *port = @"587";
NSString *fromAddress = @"[email protected]";
NSString *bodyText = @"Body text \n\r";
NSMutableArray *arguments = [NSMutableArray arrayWithObjects:
programPath,
username,
hostname,
port,
fromAddress,
toAddress,
subject,
bodyText,
nil];
for (int i = 0; i < [attachments count]; i++) {
[arguments addObject:[attachments objectAtIndex:i]];
}
NSData *passwordData = [@"myGmailPassword" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *environment = [NSDictionary dictionaryWithObjectsAndKeys:
@"", @"PYTHONPATH",
@"/bin:/usr/bin:/usr/local/bin", @"PATH",
nil];
[task setEnvironment:environment];
[task setLaunchPath:@"/usr/bin/python"];
[task setArguments:arguments];
NSPipe *stdinPipe = [NSPipe pipe];
[task setStandardInput:stdinPipe];
[task launch];
[[stdinPipe fileHandleForReading] closeFile];
NSFileHandle *stdinFH = [stdinPipe fileHandleForWriting];
[stdinFH writeData:passwordData];
[stdinFH writeData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinFH writeData:[@"Description" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinFH closeFile];
[task waitUntilExit];
if ([task terminationStatus] == 0) {
NSLog(@"Message successfully sent");
return YES;
} else {
NSLog(@"Message not sent");
return NO;
}
}
I hope it helps
Solution 2
Those response are outdated Mac OS X 10.8 and more you should use NSSharingService
NSArray *shareItems=@[body,imageA,imageB];
NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
service.delegate = self;
service.recipients=@[@"[email protected]"];
service.subject= [ NSString stringWithFormat:@"%@ %@",NSLocalizedString(@"SLYRunner console",nil),currentDate];
[service performWithItems:shareItems];
The sharing service documentation page
Solution 3
This post should help - it cites example code too.
You also need to change line 114 in Controller.m to send the message in the background:
emailMessage.visible = NO;
Comments
-
Lenny Magico about 2 years
How can I send an email from a Cocoa app without using any email clients ? I have NSURL but it opens up an email client. I would like to send the email without this happening.
-
Lenny Magico about 13 yearsI have had a look at that post before but it opens up the mail application when yous end the email which I don't really want, just want to send the email, basically I want to make my own mail application :), but thanks for the answer :D
-
Dave DeLong about 13 yearsScripting Bridge... What if I use Thunderbird? Or Outlook? Or something else?
-
Tibidabo about 13 yearsThen you can use the perl script. Mail is the most popular email client, if it is not present you can use ask the user to enter his/her SMTP details or you can hardcode yours.
-
Martin Hering over 11 yearsYou can try using [[NSWorkspace sharedWorkspace] openFile:filePath withApplication:@"Mail"] to create a new email with an attachment. You can't prefill it with recipient however and you need to have access to a shared folder to save the file to (Downloads folder e.g.).
-
Mark Bridges over 10 yearsYou could try using the new NSSharingService in Mavericks 'NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail]; [service setRecipients:[NSArray arrayWithObject:@"Your email"]]; [service setSubject:@"Your subject"]; [service performWithItems:yourAttachments, nil];'
-
fzwo almost 10 yearsThis should be the accepted answer. Be aware though that if the user is using Outlook, the attachments won't get attached.
-
Z S almost 10 yearsAnother problem with NSSharingService is that you cannot set the email body of the composer beforehand (if that is something you need to do).
-
insys over 9 years@ZS this is not correct. See performWithItems: to set the message body.
-
Vincent Tourraine over 9 yearsThe
NSSharingService
is 10.8+, but therecipients
andsubject
properties are 10.9+ only, so you should add arespondToSelector:
test if you target 10.8. -
Hedin almost 9 yearsThis doesn't work on El Capitan. It throws
NSSoftLinking - The ShareKit framework's library couldn't be loaded from /System/Library/PrivateFrameworks/ShareKit.framework/Versions/A/ShareKit.
to the console and nothing happens. Seems it has some restrictions on using private frameworks. FYI the app is not sandboxed. -
Hedin almost 9 yearsUPD: the same with Yosemite. I think, I need to link some framework to the app, not sure what.
-
Hedin almost 9 yearsUPD: for anyone who gets the same error: ShareKit is 64 bit-only.
-
jimwan about 7 yearsi cannot attach file using this
NSArray *shareItems=@[body,fileURL];
-
lifjoy over 4 yearsTo clarify: specify attachments as file URLs, like so:
NSArray *theShareItems = @[aMessageStr, [NSURL fileURLWithPath:anAttachmentsPath]];