Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Send email

In a normal UIViewController in Swift, I use this code to send a mail.

let mailComposeViewController = configuredMailComposeViewController()  mailComposeViewController.navigationItem.leftBarButtonItem?.style = .plain mailComposeViewController.navigationItem.rightBarButtonItem?.style = .plain mailComposeViewController.navigationBar.tintColor = UIColor.white  if MFMailComposeViewController.canSendMail() {     self.present(mailComposeViewController, animated: true, completion: nil) } else {     self.showSendMailErrorAlert() } 

How can I achieve the same in SwiftUI?

Do I need to use UIViewControllerRepresentable?

like image 530
Khant Thu Linn Avatar asked Jun 27 '19 05:06

Khant Thu Linn


People also ask

Is SwiftUI the same as Swift?

SwiftUI helps you build great-looking apps across all Apple platforms with the power of Swift — and surprisingly little code. You can bring even better experiences to everyone, on any Apple device, using just one set of tools and APIs.

How do I open an email in Swift iOS?

Show activity on this post. let email = "[email protected]" let url = URL(string: "mailto:\(email)") UIApplication. shared. openURL(url!)

Does SwiftUI replace cocoa?

iOS development never had a framework with “Cocoa” in the name but was still officially called “Cocoa Touch”. It's not as though the NeXTStep derived Objective-C classes are going to be wholly replaced any time soon. Even excluding Foundation, SwiftUI replaces only some Objective-C user-interface classes.


2 Answers

@Matteo's answer is good but it needs to use the presentation environment variable. I have updated it here and it addresses all of the concerns in the comments.

import SwiftUI import UIKit import MessageUI  struct MailView: UIViewControllerRepresentable {      @Environment(\.presentationMode) var presentation     @Binding var result: Result<MFMailComposeResult, Error>?      class Coordinator: NSObject, MFMailComposeViewControllerDelegate {          @Binding var presentation: PresentationMode         @Binding var result: Result<MFMailComposeResult, Error>?          init(presentation: Binding<PresentationMode>,              result: Binding<Result<MFMailComposeResult, Error>?>) {             _presentation = presentation             _result = result         }          func mailComposeController(_ controller: MFMailComposeViewController,                                    didFinishWith result: MFMailComposeResult,                                    error: Error?) {             defer {                 $presentation.wrappedValue.dismiss()             }             guard error == nil else {                 self.result = .failure(error!)                 return             }             self.result = .success(result)         }     }      func makeCoordinator() -> Coordinator {         return Coordinator(presentation: presentation,                            result: $result)     }      func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {         let vc = MFMailComposeViewController()         vc.mailComposeDelegate = context.coordinator         return vc     }      func updateUIViewController(_ uiViewController: MFMailComposeViewController,                                 context: UIViewControllerRepresentableContext<MailView>) {      } } 

Usage:

import SwiftUI import MessageUI  struct ContentView: View {     @State var result: Result<MFMailComposeResult, Error>? = nil    @State var isShowingMailView = false      var body: some View {         Button(action: {             self.isShowingMailView.toggle()         }) {             Text("Tap Me")         }         .disabled(!MFMailComposeViewController.canSendMail())         .sheet(isPresented: $isShowingMailView) {             MailView(result: self.$result)         }     } }  struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()     } } 
like image 162
Hobbes the Tige Avatar answered Sep 23 '22 22:09

Hobbes the Tige


As you mentioned, you need to port the component to SwiftUI via UIViewControllerRepresentable.

Here's a simple implementation:

struct MailView: UIViewControllerRepresentable {      @Binding var isShowing: Bool     @Binding var result: Result<MFMailComposeResult, Error>?      class Coordinator: NSObject, MFMailComposeViewControllerDelegate {          @Binding var isShowing: Bool         @Binding var result: Result<MFMailComposeResult, Error>?          init(isShowing: Binding<Bool>,              result: Binding<Result<MFMailComposeResult, Error>?>) {             _isShowing = isShowing             _result = result         }          func mailComposeController(_ controller: MFMailComposeViewController,                                    didFinishWith result: MFMailComposeResult,                                    error: Error?) {             defer {                 isShowing = false             }             guard error == nil else {                 self.result = .failure(error!)                 return             }             self.result = .success(result)         }     }      func makeCoordinator() -> Coordinator {         return Coordinator(isShowing: $isShowing,                            result: $result)     }      func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {         let vc = MFMailComposeViewController()         vc.mailComposeDelegate = context.coordinator         return vc     }      func updateUIViewController(_ uiViewController: MFMailComposeViewController,                                 context: UIViewControllerRepresentableContext<MailView>) {      } } 

Usage:

struct ContentView: View {      @State var result: Result<MFMailComposeResult, Error>? = nil     @State var isShowingMailView = false      var body: some View {          VStack {             if MFMailComposeViewController.canSendMail() {                 Button("Show mail view") {                     self.isShowingMailView.toggle()                 }             } else {                 Text("Can't send emails from this device")             }             if result != nil {                 Text("Result: \(String(describing: result))")                     .lineLimit(nil)             }         }         .sheet(isPresented: $isShowingMailView) {             MailView(isShowing: self.$isShowingMailView, result: self.$result)         }      }  } 

(Tested on iPhone 7 Plus running iOS 13 - works like a charm)

Updated for Xcode 11.4

like image 28
Matteo Pacini Avatar answered Sep 21 '22 22:09

Matteo Pacini