Freeze on dismiss view controller

I've just added an email capability to my app, so that the user can send me an email containing the app's log files and current defaults. This all works great UNTIL it's time to send the email and dismiss the MFMailComposeViewController, at which point my app freezes.


I use a button in a table cell to invoke the MFMailComposeViewController. Hitting the button calls my sendEmail function:

(Note that "logSB" resembles the print command but logs to a file using SwiftyBeaver logging.)


        func sendEmail() {
            if MFMailComposeViewController.canSendMail() {
                let mail = MFMailComposeViewController()
                mail.mailComposeDelegate = self
                mail.setToRecipients(["support@aircompare.us"])
                mail.setSubject("AirCompare user report")
                mail.setMessageBody("<p>Thanks for helping to improve AirCompare.  Your app logs and settings files are attached. Please include any details describing any problems you are having.</p>", isHTML: true)
                let filemgr = FileManager.default
                let dir: URL = filemgr.urls(for: .documentDirectory, in: .userDomainMask).last! as URL
               
                let files = ["swiftybeaver.log","swiftybeaverX.log", "defaultsFile.txt"]
                for file in files {
                    let url = dir.appendingPathComponent(file)
                    if filemgr.fileExists(atPath: url.path) {
                        self.logSB.verbose("Found the \(file) file")
                        if let fileData = NSData(contentsOfFile: url.path)
                        {
                            logSB.debug("Attachment File data loaded for \(file)")
                            mail.addAttachmentData(fileData as Data, mimeType: "text/plain", fileName: file)
                        }
                    
                    } else {
                        self.logSB.warning("\(file) file not found at \(url.absoluteString)")
                    }
                }
                present(mail, animated: true)
            } else {
                /
                logSB.error("App cannot send email")
            }
        }
    
        func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
            logSB.debug("Mail composer succeeded with result \(String(describing: result))")
            if error != nil {
            logSB.error("Mail composer returned error \(String(describing: error))")
            }
            controller.dismiss(animated: true)
        }


As I said, this is all working great and the MFMailComposeResult comes back without an error. But when the email controller is dismissed (line 38), the app freezes upon trying to redraw the table, I guess. And BTW, the email is not sent.


I know how I could probably "cheat" and navigate back to some view, but why doesn't dismiss work?


Debugger points me to:

   func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return ACsections[section]  // ACsections contains the names of each table section
    }

...and says the index is out of range.

Accepted Reply

My problem was due to calling to configure my table in the ViewWillAppear. After moving that to the ViewDidLoad, the email composer view dismisses as expected and all is well.


Now if I could just remember why I bothered in the first place to configure the table from the ViewWillAppear.... 😟

Replies

I had a similar function in an app.


My line 38 is different ; I dismiss from self, not from controller:

     self.dismiss(animated: true)

I think you're onto something, but when I add self and use this instead:


self.controller.dismiss(animated: true)


... I get an eror "Value of type 'ExpandingTableController' has no member 'controller'. My 'ExpandingTableController' is the one I want to return to, not the mail composer.


I've set my 'ExpandingTableController' as the MFMailComposeViewControllerDelegate so that it can receive the didFinishWith.


Oh, and I just added a log line to get the description of the current controller:

        func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
            logSB.debug("The controller is \(String(describing: controller.description))")
            logSB.debug("Mail composer succeeded with result \(String(describing: result))")
            if error != nil {
            logSB.error("Mail composer returned error \(String(describing: error))")
            }
            controller.dismiss(animated: true)
        }


This logs "The controller is <MFMailComposeViewController: 0x10384f400>". So I think I'm attempting to dismiss the right controller. It's the redrawing of the table controller that seems to be where the trouble is.

He said self.dismiss(), not self.controller.dismiss(). i.e. calling the method on the parent (presenting) view controller, not on the child.

Good catch, thanks. Unfortunately I get the same outcome either way.


I don't really see how using self could ever work, because "self" is my ExpandingTableController.


I'm going to try my whole email thing from a different view. This table view prepares the table in the viewWIllAppear. I think this may be causing the problem?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        test = defineCellProps()  //  loads the table cell definitions into the "test" database
        configureTableView()  // like it says
    }

In my case, self is a UIViewController, taht is also the MFMailComposeViewControllerDelegate.


Is ExpandingTableController a UIViewController ?

Yes, it is a UIViewController.


And I've just confirmed that my code works fine when I run it on another view. My problem is indeed specific to returning to the ExpandingTableController view, which is also a UITableViewDelegate and a UITableViewDataSource.

My problem was due to calling to configure my table in the ViewWillAppear. After moving that to the ViewDidLoad, the email composer view dismisses as expected and all is well.


Now if I could just remember why I bothered in the first place to configure the table from the ViewWillAppear.... 😟