Skip to content

Instantly share code, notes, and snippets.

@serjooo
Last active May 26, 2022 09:10
Show Gist options
  • Save serjooo/9628e6cc7eec4d0d16433660aa5184ad to your computer and use it in GitHub Desktop.
Save serjooo/9628e6cc7eec4d0d16433660aa5184ad to your computer and use it in GitHub Desktop.
Check and Deep link into supported mail apps on iOS
enum MailClient: CaseIterable {
case gmail
case outlook
case yahoo
case spark
case apple
}
import UIKit
protocol URLHandlerProtocol {
func canOpenURL(_ url: URL) -> Bool
func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any], completionHandler completion: ((Bool) -> Void)?)
}
extension UIApplication: URLHandlerProtocol {}
// GIST is sponsored by the following StackOverflow answer:
// https://stackoverflow.com/a/55765362
struct MailClientProvider {
let content: MailContent
let urlHandler: URLHandlerProtocol
func getFirstSupportedMailClientURL() -> URL? {
getAllSupportedMailClients()
.first
.flatMap(getURL(for:))
}
func openFirstSupportedMailClient(completion: ((Bool) -> Void)?) {
guard let firstSupportedMailClientURL = getFirstSupportedMailClientURL() else {
print("Failed to open any mail client, none of them are supported")
completion?(false)
return
}
urlHandler.open(firstSupportedMailClientURL, options: [:], completionHandler: completion)
}
func getAllSupportedMailClients() -> [MailClient] {
return MailClient.allCases.filter(canOpenMailClient(_:))
}
func canOpenMailClient(_ client: MailClient) -> Bool {
guard let url = getURL(for: client) else {
print("Mail Client URL corrupted \(client)")
return false
}
return urlHandler.canOpenURL(url)
}
func getURL(for client: MailClient) -> URL? {
let toURLPart = "to=\(content.recipientEmail)"
let subjectURLPart = "subject=\(content.subject)"
let bodyURLPart = "body=\(content.body)"
let subjectAndBodyPart = [subjectURLPart, bodyURLPart].joined(separator: "&")
let fullURLPart = [toURLPart, subjectURLPart, bodyURLPart].joined(separator: "&")
switch client {
case .gmail:
return URL(string: "googlegmail://co?\(fullURLPart)")
case .outlook:
return URL(string: "ms-outlook://compose?\(fullURLPart)")
case .yahoo:
return URL(string: "ymail://mail/compose?\(fullURLPart)")
case .spark:
return URL(string: "readdle-spark://compose?recipient=\(content.recipientEmail)&\(subjectAndBodyPart)")
case .apple:
return URL(string: "mailto:\(content.recipientEmail)?\(subjectAndBodyPart)")
}
}
}
struct MailContent {
let recipientEmail: String
@URLPercentEncoded var body: String
@URLPercentEncoded var subject: String
}
// MARK: - Extension
extension String {
var urlPercentEncoded: String {
addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? self
}
}
// MARK: - Property Wrapper
@propertyWrapper
struct URLPercentEncoded {
var wrappedValue: String {
didSet {
wrappedValue = wrappedValue.urlPercentEncoded
}
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.urlPercentEncoded
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment