Search code examples
iosswiftclosuresmfmailcomposeviewcontroller

Swift - Simplifying or refactoring an escaping closure? Can a closure be reusable?


I'm curious if there is a way to simplify / make a closure reusable? I asked a much more in depth question here: Swift - Refactoring code. Closure and inout? and its the same code in question.

What I'm trying to do is make this closure a single line:

self.feedbackManager?.send(on: self) { [weak self] result in
    switch result {
    case .failure(let error):
        print("error: ", error.localizedDescription)
    case .success(_):
        print("Success")
    }
    self?.feedbackManager = nil
}

feedbackManager is an instance of a class, which presents a MFMailComposeViewController on the self.viewController. The result is the MFMailComposeResult. Which is the result in the closure.

So that ideally, it can be called something like this?

self.feedbackManager?.send(on: self) { switchResultClosure }

Edit: Result

Implementation:

let feedback = Feedback(subject: "subject", body: "body", footer: "footer")
sendEmail(with: feedback, on: self)

Class:

import UIKit
import MessageUI

struct Feedback {
    let recipients = [R.App.Contact.email]
    let subject: String
    let body: String
    let footer: String
}


enum SendStatus: Int {
    case sent, cancelled, failed, notSupported, saved
}

protocol FeedbackProtocol {
    func sendEmail(with feedback: Feedback, on viewController: UIViewController)
}

extension FeedbackProtocol {
    func sendEmail(with feedback: Feedback, on viewController: UIViewController) {
        print("protocol FeedbackController: sendEmail()")
        ShareController.shared.sendMailToRecipients(with: feedback, on: viewController) { (SendStatus) in
            print("SendStatus: ", SendStatus)
        }
    }
}

final class ShareController: NSObject {
    
    static var shared = ShareController()
    var completionBlock: ((SendStatus) -> Void)?
    
    private override init() { }
    
}

extension ShareController: MFMailComposeViewControllerDelegate {
    
    func sendMailToRecipients(with feedback: Feedback, on viewController: UIViewController, block: @escaping (SendStatus) -> Void) {
        print("sendMailToRecipients()")
        
        guard MFMailComposeViewController.canSendMail() else {
            block(.notSupported)
            return
        }
        
        var appVersion = ""
        if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            appVersion = version
        }
        
        completionBlock = block
        let mailController = MFMailComposeViewController()
        mailController.mailComposeDelegate = self
        mailController.mailComposeDelegate = self
        mailController.setToRecipients(feedback.recipients)
        mailController.setSubject(feedback.subject)
        mailController.setMessageBody((feedback.body), isHTML: true)
        
        DispatchQueue.main.async {
            viewController.present(mailController, animated: true, completion: nil)
        }
    }
    
    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        print("mailComposeController(): ShareVC")
        controller.dismiss(animated: true)
        switch result {
        case .sent: completionBlock?(.sent)
        case .cancelled: completionBlock?(.cancelled)
        case .failed: completionBlock?(.failed)
        case .saved: completionBlock?(.saved)
        default: break
        }
    }
}

Solution

  • Yes you can do by creating function with same parameter as your closure. Call below function. instead of writing closure in send just type switchResultClosure with out brackets. Syntax might not be accurate.

    func switchResultClosure(result: ResultDataType) {
        // switch code
        switch result {
        case let .failure(error):
            print("error: ", error.localizedDescription)
        case .success:
            print("Success")
        }
        self?.feedbackManager = nil
    }
    
    feedbackManager?.send(on: self, switchResultClosure)