Home > database >  Programatically sending a HTML email
Programatically sending a HTML email

Time:10-30

I want to do something relatively easy - at least I thought it was - let the user send an email from a button in my SwiftUI macOS app.

I've tried two approaches:

  1. Link("Send email", destination: "mailto:[email protected]?subject=Test&body=...")
  2. using NSSharingService(named: .composeEmail)

In both instances I have a String for the body which compiles to something like:

let body = <strong>Please describe your issue below:</strong><br><br><hr><strong>Operating system:</strong> \(ProcessInfo.processInfo.operatingSystemVersionString)

However in both Mail.app and Outlook.app the body translates directly as is, and doesn't convert into HTML.

In iOS this same String seems to render correctly when calling the compose window, but macOS doesn't.

I've looked for anything that allows me to set the Content-Type or MimeType but nothing seems to return in the docs.

Test code

let body = <strong>Please describe your issue below:</strong><br><br><hr><strong>Operating system:</strong> \(ProcessInfo.processInfo.operatingSystemVersionString)


// option 1
let mailto = "mailto:[email protected]?subject=Test Email&body=\(body)"
Link("Send an email", destination: mailto)

// option 2
func sendEmail(to: [String], subject: String, message: String) {
  let service = NSSharingService(named: .composeEmail)!
  service.recipients = [to]
  service.subject = subject
  service.perform(withItems: [message])
}

sendEmail(to: ["[email protected]], subject: "Test Email", message: body)

Preferably I wouldn't want to use a 3rd party library as it is a single button in the whole app.

CodePudding user response:

After some deep diving into the topic, I found this site: https://indiespark.top/featured/programmatically-sending-rich-text-mail-attachment-mac/

The author Sasmito Adibowo details how the mailto: is limited, and if you want to have RichText or HTML body then you need to use the NSSharingService.

It also goes into detail on how to add an attachment too.

Blog code

// create an attributed string
NSString* htmlText = @"<html><body>Hello, <b>World</b>!</body></html>";
NSData* textData = [NSData dataWithBytes:[htmlText UTF8String] length:[htmlText lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
NSAttributedString* textAttributedString = [[NSAttributedString alloc] initWithHTML:textData options:options documentAttributes:nil];

// create a file to attach
NSUUID* uuid = [NSUUID new];
NSString* tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[uuid UUIDString]];
NSFileManager* fm = [NSFileManager new];
[fm createDirectoryAtPath:tempDir withIntermediateDirectories:YES attributes:nil error:nil];
 
NSString* tempFile = [tempDir stringByAppendingPathComponent:@"report.csv"];
NSURL* tempFileURL = [NSURL fileURLWithPath:tempFile];
NSData* csv = ...; // generate the data here
[csv writeToURL:tempFileURL atomically:NO];

// share it
NSSharingService* mailShare = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
NSArray* shareItems = @[textAttributedString,tempFileURL];
[mailShare performWithItems:shareItems];

I'm not 100% across Objective-C but in terms of the original sendEmail function:

func sendEmail(to: [String], subject: String, message: String) {
  let service = NSSharingService(named: .composeEmail)!
  service.recipients = [to]
  service.subject = subject

  // convert String to NSAttributedString
  let data = message.data(using: .utf8)
  let attributedString = NSAttributedString(html: data!, documentAttributes: nil)

  service.perform(withItems: [attributedString!])
}

sendEmail(to: ["[email protected]], subject: "Test Email", message: body)
  • Related