Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to open the default mail app on iOS 14 without a compose view?

Tags:

ios

swift

I want to open the default Mail application chosen by the user on iOS 14 - but without showing a compose view. After signing up for an account, the user should confirm their email address, so I want to direct the user there.

There seem to be two known approaches based on the other questions I found:

UIApplication.shared.open(URL(string: "mailto://")!)

and

UIApplication.shared.open(URL(string: "message://")!)

The problem with the first option is that it will open an empty compose mail view in the app that comes up asking the user to type in a new email. That's not what I want. It would confuse users and they make think they have to send us an email. Putting in some text through parameters of the mailto URL syntax where I basically prepopulate the mail compose view with some text instructing to discard that new email draft and asking to check their email instead would work as a workaround but is not very nice.

The problem with the second option is that always opens the Mail.app, even if that is not the default mail app, and presumably it will ask the user to install the Mail.app if they deleted it from their phones because they have chosen e.g. Protonmail as their default mail app instead. Also not a very nice option for anyone who does not use Mail.app mainly.

So neither of the two approaches that have been proposed by other people solve my issue very nicely.

What is the best way to approach this?

Is there maybe some app to query iOS for the default mail app so at least I can try and launch that app if I know that app's custom URL scheme (e.g. googlegmail://)?

like image 619
Raphael Avatar asked Nov 15 '22 00:11

Raphael


1 Answers

I ended up half-solving it by asking the user with an alert view about their preference, because I did not find a way to query iOS about it directly.

So first am showing an alert view like this:

func askUserForTheirPreference(in presentingViewController: UIViewController) {
    let alertController = UIAlertController(title: nil, message: "pleaseConfirmWithApp", preferredStyle: .actionSheet)
    alertController.addAction(UIAlertAction(title: "Apple Mail", style: .default) { action in
        self.open(presentingViewController, .applemail)
    })
    alertController.addAction(UIAlertAction(title: "Google Mail", style: .default) { action in
        self.open(presentingViewController, .googlemail)
    })
    alertController.addAction(UIAlertAction(title: "Microsoft Outlook", style: .default) { action in
        self.open(presentingViewController, .outlook)
    })
    alertController.addAction(UIAlertAction(title: "Protonmail", style: .default) { action in
        self.open(presentingViewController, .protonmail)
    })
    alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
        os_log("Cancelling", log: Self.log, type: .debug)
    })
    presentingViewController.present(alertController, animated: true)
}

Then, I am responding to the user's choice like this:

func open(_ presentingViewController: UIViewController, _ appType: AppType) {
    switch appType {
    case .applemail: UIApplication.shared.open(URL(string: "message:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
    case .googlemail: UIApplication.shared.open(URL(string: "googlegmail:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
    case .outlook: UIApplication.shared.open(URL(string: "ms-outlook:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
    case .protonmail: UIApplication.shared.open(URL(string: "protonmail:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
    }
}

private func handleAppOpenCompletion(_ presentingViewController: UIViewController, _ isSuccess: Bool) {
    guard isSuccess else {
        let alertController = UIAlertController(title: nil, message: "thisAppIsNotInstalled", preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: .cancel))
        presentingViewController.present(alertController, animated: true)
        return
    }
}

enum AppType {
    case applemail, googlemail, outlook, protonmail
}

A clear limitation of this approach is of course that I am limiting the user to very specific apps (in this case Google Mail, iOS "default" Mail, Microsoft Outlook and ProtonMail). So this approach does not really scale well.

But at least, you can cover a few favorite ones and go from there based on your users' feedback.

The main reason for jumping through these hoops of asking the first is that, at least at the moment, it seems impossible to get that information from iOS directly. I also could not find a URL scheme that would always open the chosen default Mail app without showing the compose new email view.

like image 119
Raphael Avatar answered Jan 03 '23 08:01

Raphael