Reputation: 7537
Problem
I would like to use the MailGun service to send emails from a pure Swift app.
Research So Far
As I understand it, there are two methods to send an email via MailGun. One is to email MailGun with the emails, and MailGun will redirect it (See Send via SMTP). That will, as I understand it, not work, as iOS cannot programatically automatically send mail, and must use methods that require user intervention. As such, I should use the API directly. As I understand it, I need to open a URL to do this, and so I should use some form of NSURLSession
, as per this SO answer
Code
MailGun provides documentation for Python, which is as follows:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages",
auth=("api", "key-(Personal info)"),
data={"from": "Excited User <(Personal info)>",
"to": ["[email protected]", "(Personal info)"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
with (Personal info) being substituted for keys/information/emails.
Question
How do I do that in Swift?
Thanks!
Upvotes: 3
Views: 4388
Reputation: 901
people are getting 400 or 401 errors because none of the other answers construct the url correctly. here is some code that works in swift 5 and iOS15:
func sendEmail() {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = "https://api:[email protected]/v3/YOUR_DOMAIN/messages?"
let emailRecipient = "[email protected]"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: mailgunAPIPath + "from=USER@YOUR_DOMAIN&to=\(emailRecipient)&subject=A%20New%20Test%21&text=\(emailMessage)")! as URL)
// POST and report back with any errors and response codes
request.httpMethod = "POST"
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
Upvotes: 3
Reputation: 40
Swift 3 answer:
func test() {
let session = URLSession.shared
var request = URLRequest(url: URL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.httpMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [[email protected],(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.httpBody = data.data(using: .ascii)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
Upvotes: 0
Reputation: 55
I spent hours trying to get the selected answer working, but to no avail.
Although I was finally able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.
// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
keys = NSDictionary(contentsOfFile: path)
}
if let dict = keys {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = dict["mailgunAPIPath"] as? String
let emailRecipient = "[email protected]"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler@<my domain>.com%3E&to=reservations@<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)
// POST and report back with any errors and response codes
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:
https://API:key-<my key>@api.mailgun.net/v3/<my domain>.com/messages?
Hope this offers a solution to anyone else having issues with MailGun and wanting to avoid a 3rd party solution!
Upvotes: 0
Reputation: 28819
In python, the auth
is being passed in the header.
You have to do a http post request, passing both the header and the body.
This is a working code:
func test() {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.HTTPMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [[email protected],(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
Upvotes: 4
Reputation: 36116
requests.post
sends an HTTP POST request, encoding key/value pairs as application/x-www-form-urlencoded
. You need to do the same.
application/x-www-form-urlencoded
as per How to escape the HTTP params in SwiftUpvotes: 0