Home > database >  Unable to access variable values in view
Unable to access variable values in view

Time:05-01

Trying to send email from my iOS app. It have it set up and it's good to go, but I can't seem to be able to get the text passed to the view presented when sending the email. When I pass the text to be sent, it's always empty.

I know it might be related to the view not having access to it, but I'm scratching my head what to change, or what to add in order to make it work. I have tried with @binding and ObservableObject, but I'm still new with Swift and SwiftUI, so I'm making a mess.

Here's the code, how can I pass the text from the list item to the new view presented?

struct ContentView: View {

    @FetchRequest(entity: Jot.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Jot.date, ascending: false)])
    var jots: FetchedResults<Jot>
    @State var result: Result<MFMailComposeResult, Error>? = nil
    @State var isShowingMailView = false

    // added this to try to force the text to go, since passing jot.text was giving me always
    // the first item in the list
    @State private var emailText: String = "" 

    var body: some View {
        NavigationView {
            List(jots) { jot in
                Text(jot.text!)
                .contextMenu {
                    if MFMailComposeViewController.canSendMail() {
                        Button(action: {
                            emailText = jot.text! // try to force the text to be passed
                            self.isShowingMailView.toggle()
                        }) {
                            Text("Email jot")
                            Image(systemName: "envelope")
                        }
                    }
                }
                .sheet(isPresented: $isShowingMailView) {
                    MailView(result: $result) { composer in
                        composer.setSubject("Jot!")
                        // in here, if I pass jot.text! then it's always the first item in the list
                        // if I pass emailText then it's always empty
                        composer.setMessageBody(emailText, isHTML: false)
                    }
                }
            }
            .listStyle(.plain)
        }
    }

}

And the supporting code to send email:

import SwiftUI
import UIKit
import MessageUI


public struct MailView: UIViewControllerRepresentable {

    @Environment(\.presentationMode) var presentation
    @Binding var result: Result<MFMailComposeResult, Error>?
    public var configure: ((MFMailComposeViewController) -> Void)?

    public 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
        }

        public 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)
        }
    }

    public func makeCoordinator() -> Coordinator {
        return Coordinator(presentation: presentation,
                           result: $result)
    }

    public func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
        let vc = MFMailComposeViewController()
        vc.mailComposeDelegate = context.coordinator
        configure?(vc)
        return vc
    }

    public func updateUIViewController(
        _ uiViewController: MFMailComposeViewController,
        context: UIViewControllerRepresentableContext<MailView>) {

    }
}

CodePudding user response:

We don't have a full Minimal Reproducible Example (MRE), but I think what you want is to use the sheet(item:onDismiss:content:) initializer. Instead of using a Bool to trigger the sheet showing, it triggers when an optional value of whatever data you wish to pass in becomes non-nil. This way, you can pass the data to the .sheet and only need one variable to do it. This is untested, but try:

struct ContentView: View {

    @FetchRequest(entity: Jot.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Jot.date, ascending: false)])
    var jots: FetchedResults<Jot>
    @State var result: Result<MFMailComposeResult, Error>? = nil
    @State var isShowingMailView = false
    // this is your optional selection variable
    @State private var selectedJot: Jot?

    var body: some View {
        NavigationView {
            List(jots) { jot in
                Text(jot.text!)
                .contextMenu {
                    if MFMailComposeViewController.canSendMail() {
                        Button(action: {
                            // this gives selectedJot a value making it non-nil
                            selectedJot = jot
                        }) {
                            Text("Email jot")
                            Image(systemName: "envelope")
                        }
                    }
                }
            }
            .listStyle(.plain)
            // When selectedJot becomes non-nil, this initializer will trigger the sheet.
            .sheet(item: $selectedJot) { jot in
                MailView(result: $result) { composer in
                    composer.setSubject("Jot!")
                    composer.setMessageBody(jot.text, isHTML: false)
                }
            }
        }
    }
}
  • Related