Adding Support Mail


While including a flow for contacting support often comes as a last minute after-thought,
having a great complaining experience can be instrumental in further improving the app.
It is by no means a complex task, but there are a few gotchas along the way.

Gathering Context

Feedback is often caused by a regression, introduced in a certain release version,
or only occurs in a specific environment.
Therefore, it’s useful to include this sort of data in the initial support request. This avoids unnecessary back and forth. Here’s an example of gathering some commonly required context.

var app: String {
	Bundle.main.info("CFBundleDisplayName") + " " +
	Bundle.main.info("CFBundleShortVersionString")
}

var os: String {
#if os(iOS)
	let platform = "iOS "
#elseif os(macOS)
	let platform = "MacOS "
#endif
	return platform + ProcessInfo.processInfo.operatingSystemVersionString
}

I’m using this extension to make fetching data from the app’s bundle more convenient.

extension Bundle {
	func info(_ key: String) -> String {
		infoDictionary.flatMap { $0[key] as? String }
		?? "Missing " + key
	}
}

Constructing Link

The mailto: URI scheme supports specifying subject and body using query parameters,
which are then parsed by the user’s default mail client.

var mailTo: URL {
	URL(string: "mailto:\(supportEmail)")!
		.appending(
			queryItems: [
				URLQueryItem(name: "subject", value: "Support: \(app)"),
				URLQueryItem(name: "body", value: "\n\n\nOS: \(os)\nApp: \(app)"),
			]
		)
}

User Interface

When all is done, SwiftUI offers various ways of opening the constructed link:

Button

Using openURL environment variable

struct ContactSupportView: View {
	@Environment(\.openURL) private var openURL

	var body: some View {
		Button("Contact Support") { openURL(mailTo) }
	}
}

Inline

Since iOS 15 SwiftUI supports limited markdown rendering, with one caveat.
If we want to use anything else than simple string literals (like interpolation),
LocalizedStringKey(stringLiteral:) initialiser has to be used explicitly.

Text(
	LocalizedStringKey(
		stringLiteral: "Contact us [\(supportEmail)](\(mailTo))!"
	)
)

Link

The simplest option!

Link("Contact Support", destination: mailTo)

Result

Now users can create a simple mail template with a single tap!

image

As a bonus, consistent titles allow for some automation when handling support.
Especially in cases where multiple apps are served by a single email address.