I get this question a lot, not necessarily for NativeScript, but for other mobile frameworks. The question is, how do you send emails from within your Android and iOS application without launching one of the already installed mail apps? In other words how do you send emails via a custom form within the application or in the background?
A solution to this question would be to use a backend web server that can send mail and create an API endpoint to it that can be accessed from your mobile app. However, this solution takes work and other resources. Instead you can make use of a transactional email service.
There are many of these email services available, but for this example we’re going to look at using the Mailgun service in a NativeScript application.
Let me start by saying that Mailgun has a free tier that in many cases is more than enough. The free tier can accomplish everything I need. You’ll also need to create an account to use Mailgun.
With a Mailgun account established we can now create a new NativeScript application. From the Command Prompt (Windows) or Terminal (Mac and Linux), execute the following commands:
tns create MailgunProject --template tsc
cd MailgunProject
tns platform add ios
tns platform add android
A few things to note here. First off, we’re going to be using TypeScript instead of JavaScript, hence the --template tsc
flag. Second, if you’re not using a Mac then you cannot add and build for the iOS platform.
We’re going to work with three different files in this project. We’re going to manipulate the app/main-view-model.ts file to handle our data bindings with the font-end. We’re going to modify the app/main-page.ts file to make use of the new class name in our view model. Finally we’re going to manipulate the app/main-page.xml file to contain an email form.
Let’s start with the view model first. I’ll break it down in a moment, but open app/main-view-model.ts and add the following code:
import observable = require("data/observable");
import http = require("http");
export class MailgunModel extends observable.Observable {
private _url: string = "MAILGUN_DOMAIN_HERE";
private _api: string = "BASE64_API:KEY-YOUR_KEY_HERE";
private _recipient: string;
private _subject: string;
private _message: string;
get message(): string {
return this._message;
}
set message(value: string) {
if (this._message !== value) {
this._message = value;
this.notifyPropertyChange("message", value);
}
}
get subject(): string {
return this._subject;
}
set subject(value: string) {
if (this._subject !== value) {
this._subject = value;
this.notifyPropertyChange("subject", value);
}
}
get recipient(): string {
return this._recipient;
}
set recipient(value: string) {
if (this._recipient !== value) {
this._recipient = value;
this.notifyPropertyChange("recipient", value);
}
}
constructor() {
super();
}
public send() {
http.request({
url: "https://api.mailgun.net/v3/" + this._url + "/messages",
method: "POST",
headers: { "Authorization": "Basic " + this._api },
content: "from=test@example.com&to=" + this.recipient + "&subject=" + this.subject + "&text=" + this.message
}).then(success => {
console.log("SUCCESS", JSON.stringify(success));
}, error => {
console.log("ERROR", error);
});
}
}
The above file is where all the magic happens.
The first thing to notice that didn’t already exist in the file is the importing of the http
module. This is so we can make POST requests against the Mailgun API. Going deeper, we’ve changed the class name to something that makes a little more sense. This time we’re calling it MailgunModel
.
Now let’s take a look at our five private class variables:
private _url: string = "MAILGUN_DOMAIN";
private _api: string = "BASE64_API:KEY-YOUR_KEY_HERE";
private _recipient: string;
private _subject: string;
private _message: string;
Our email application will only have three fields to it. What really matters is the _url
and _api
variables. The _url
is either going to be the Mailgun sandbox URL assigned to you for testing, or the domain you registered with Mailgun. The _api
variable is going to be a base64 encoded username and password string. The username and password are as follows:
It is important that your API key is prefixed with key-
. I made the mistake of not including it and was banging my head for some time.
Remember you need the base64 version of this. In a typical JavaScript setting you would run a command like this:
window.btoa("username:password");
The problem is that NativeScript doesn’t use window
. You’ll have to rig together your own base64 encoding function or just encode it externally and plug it in. It doesn’t change so generating externally is fine.
Every form variable will have its own getter and setter methods. For each of the setters you’re essentially checking if the value has changed and issue a change notification to the observable if it has.
Most of the magic happens in the send()
method as seen here:
public send() {
http.request({
url: "https://api.mailgun.net/v3/" + this._url + "/messages",
method: "POST",
headers: { "Authorization": "Basic " + this._api },
content: "from=test@example.com&to=" + this.recipient + "&subject=" + this.subject + "&text=" + this.message
}).then(success => {
console.log("SUCCESS", JSON.stringify(success));
}, error => {
console.log("ERROR", error);
});
}
We’re using the http
module that we imported to create a POST request. The URL was taken from the Mailgun documentation with our domain url slipped in. We’re using basic authorization with the base64 encoded credentials and we’re sending form data. The Mailgun API does not accept JSON data unfortunately so we have to create a massive string out of it.
With the view model out of the way we can update the app/main-page.ts file with the new class name. Open the file and add the following code:
import { EventData } from "data/observable";
import { Page } from "ui/page";
import { MailgunModel } from "./main-view-model";
export function navigatingTo(args: EventData) {
var page = <Page>args.object;
page.bindingContext = new MailgunModel();
}
Nothing really changed except the renaming of the class.
Finally we can take a look at the UI file that contains our email form. It is going to be very simplistic, but open app/main-page.xml and include the following code:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<StackLayout>
<TextField id="recipient" hint="Recipient (To)" autocapitalizationType="none" text="{{ recipient }}" />
<TextField id="subject" hint="Subject" text="{{ subject }}" />
<TextView id="message" hint="Message" text="{{ message }}" />
<StackLayout orientation="horizontal">
<Button text="Send" tap="{{ send }}" />
</StackLayout>
</StackLayout>
</Page>
Above is a StackLayout
with three input fields and a send button. There is a two-way bind happening here. In each of the text
properties there is a reference to a variable in our app/main-view-model.ts file. These variables will be updated when something changes in either direction, whether that be from the front-end or the back-end logic.
At this point you should be good to go. Try sending an email and see what happens!
Many applications need email features lately. Maybe you want to create a feedback form within your application, but you don’t want the default mail app to open. Or maybe you want to send crash logs to an email address without even prompting the user. Making use of a transactional email service is a great solution to this and Mailgun is a perfect example.