3

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": ["bar@example.com", "(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!

Community
  • 1
  • 1
rocket101
  • 7,369
  • 11
  • 45
  • 64

5 Answers5

4

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: [bar@example.com,(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()
    }
William Kinaan
  • 28,059
  • 20
  • 85
  • 118
  • 1
    I am using this code but am currently getting HTTP error code 401 - any ideas? My mailgun works fine using curl – RJH Dec 29 '16 at 18:42
  • @RJH could you please post a new question and I will try to find time to help you – William Kinaan Dec 29 '16 at 18:53
  • posted here William - http://stackoverflow.com/questions/41387402/sending-email-with-swift-and-mailgun – RJH Dec 29 '16 at 21:25
  • u get a 401 error because u need to take out "key-" from the api key value. see my answer for how the construct the url correctly – ngb Sep 06 '21 at 03:20
1

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:YOUR_API_KEY@api.mailgun.net/v3/YOUR_DOMAIN/messages?"
    let emailRecipient = "RECIPIENT@EMAIL.COM"
    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()
}
ngb
  • 821
  • 9
  • 21
0

requests.post sends an HTTP POST request, encoding key/value pairs as application/x-www-form-urlencoded. You need to do the same.

Community
  • 1
  • 1
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
0

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 = "bar@foo.com"
    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!

  • this doesn't work. it gives error 401. u need to take out "key-" in the mailgun path. – ngb Sep 06 '21 at 03:12
0

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: [bar@example.com,(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()
    }
HLie
  • 40
  • 3
  • Hi there, when I tested my API key response return 401 and 2020-12-05 21:31:42.624789+0300 ExamMachine[6413:3043388] [connection] nw_resolver_start_query_timer_block_invoke [C2] Query fired: did not receive all answers in time for api.mailgun.net:443 – Mahmut Bedir Dec 05 '20 at 18:34
  • u get a 401 error because u need to take out "key-" from the api key value. see my answer for how the construct the url correctly – ngb Sep 06 '21 at 03:23