SWIFT - mailComposeDelegate not called : cancel of MFMailComposeViewController doesn't work - swift

On the same viewcontroller, we can send an email or a text message to send an information to a friend.
The text message in app fully works. But for the email, the email app opens inside my app with all the informations I asked to write but it's impossible to dismiss it by pushing cancel, nothing happens.
I tried mc.mailComposeDelegate = self or mc.delegate = self and MFMailComposeViewControllerDelegate is at the top too.
I looked everything on internet, I didn't find any explanation.
mailComposeController is never called!
Do you have any idea ?
class inviteAFriendViewController: UIViewController, MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate {
#IBAction func emailButtonDidTouch(sender: AnyObject) {
sendEmail()
}
func sendEmail() {
let emailTitle = "text"
let messageBody = "text"
let toRecipents = [""]
let mc = MFMailComposeViewController()
//mc.mailComposeDelegate = self
mc.delegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
presentViewController(mc, animated: true, completion: nil)
}
func mailComposeController(controller2: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Mail cancelled")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultSaved.rawValue:
print("Mail saved")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultSent.rawValue:
print("Mail sent")
controller2.dismissViewControllerAnimated(true, completion: nil)
case MFMailComposeResultFailed.rawValue:
print("Mail sent failure.")
controller2.dismissViewControllerAnimated(true, completion: nil)
default:
break
}
controller2.dismissViewControllerAnimated(true, completion: nil)
}

I got it working without any problems, but my delegate method looks a little bit different to yours:
func mailComposeController(controller:MFMailComposeViewController, didFinishWithResult result:MFMailComposeResult, error:NSError?) {
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Mail cancelled")
case MFMailComposeResultSaved.rawValue:
print("Mail saved")
case MFMailComposeResultSent.rawValue:
print("Mail sent")
case MFMailComposeResultFailed.rawValue:
print("Mail sent failure: %#", [error.localizedDescription])
default:
break
}
self.dismissViewControllerAnimated(true, completion: nil)
}
you may try this one.
And you need to set
mc.mailComposeDelegate = self
and not the
mc.delegate = self

First of all use
mc.mailComposeDelegate = self
instead of
mc.delegate = self
Moreover, in case of Swift 3, delegate method is somehow updated which is:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?){
controller.dismiss(animated: true, completion: nil)
}

For Swift 4:
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
print("Mail cancelled")
case MFMailComposeResult.saved.rawValue:
print("Mail saved")
case MFMailComposeResult.sent.rawValue:
print("Mail sent")
case MFMailComposeResult.failed.rawValue:
print("Mail sent failure: \(error?.localizedDescription)")
default:
break
}

Related

How do I get my app to print ''E-Mail send'' or ''E-Mail cancelled"?

I have an app which allows me to send E-Mails. I already got it as far, as that the Mail Controller is closed after type on the send or cancel button. But I would like to have an ''Popup'' or something inside of the app which tell the user whether they have send the mail or cancelled it.
The commented out code had been my try let the app say it, but it wouldn't work because of some warnings.
#IBAction func Senden(_ sender: Any) {
let toRecipients = ["Mail#adress.com"]
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(toRecipients)
mc.setSubject(FirmaFeld.text!)
mc.setMessageBody("Firma: \(FirmaFeld.text!) \n\n Kontaktperson: \(KontaktpersonFeld.text!) \n\n EMail: \(EMailFeld.text!) \n\n Anliegen: \(NachrichtFeld.text!)", isHTML: false)
self.present(mc, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Swift.Error?) {
controller.dismiss(animated: true, completion: nil)
}
/* switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
print("Mail cancelled")
case MFMailComposeResult.saved.rawValue:
print("Mail saved")
case MFMailComposeResult.sent.rawValue:
print("Mail sent")
case MFMailComposeResult.failed.rawValue:
print("Mail sent failure: %#", [error!.localizedDescription])
default:
break
}
// Dismiss the mail compose view controller.
self.dismiss(animated: true, completion: nil)
}*/
#IBAction func dismissKeyboard(_ sender: Any) {
self.resignFirstResponder()
}
Show the alertController inside the completionBlock.
controller.dismiss(animated: true, completion: {
if result == .cancelled {
let alertController = UIAlertController(title: "E-Mail not sent!", message: "E-Mail not sent.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { (action: UIAlertAction!) in
}))
present(alertController, animated: true, completion: nil)
}
})
Same way when mail send.

how can I perform segue with another view only when email was sent?

For some reason this code doesn't work it gives off the error:"Warning: Attempt to present * on * whose view is not in the window hierarchy". But i don't understand how to fix this. Here is my code.
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .cancelled:
print("Mail cancelled")
case .saved:
print("Mail saved")
case .sent:
print("Mail sent")
self.performSegue(withIdentifier: "AllList", sender: self)
AllListOfViolations.violationType.append("\(LocationAndTimeData.getSystemDate())")
case .failed:
print("Mail sent failure: \(error?.localizedDescription ?? "nil")")
}
controller.dismiss(animated: true, completion: nil)
}
Problem is before performing segue you need to dismiss the MFMailComposeViewController, so try to segue in the completion block of dismiss on basis of some condition. Also if you are accessing AllListOfViolations.violationType in prepareForSegue then append object in array before calling performSegue.
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
var needToSegue = false
switch result {
case .cancelled:
print("Mail cancelled")
case .saved:
print("Mail saved")
case .sent:
needToSegue = true
print("Mail sent")
case .failed:
print("Mail sent failure: \(error?.localizedDescription ?? "nil")")
}
controller.dismiss(animated: true) {
if needToSegue {
AllListOfViolations.violationType.append("\(LocationAndTimeData.getSystemDate())")
self.performSegue(withIdentifier: "AllList", sender: self)
}
}
}

MFMessageComposeViewController make the app reload after close?

i have table of contacts , the logic user will multi select contacts to send them sms invite to the app , my issue is after popup sms compose( when close or send ) the composer is closed and the app is been restarted, is it how its work? why it should reset the app i am just closing or canceling the sms compose controller !
here is what i did
//Send SMS
func sendSMS( to : [String] = [] , message : String = "" )
{
if (MFMessageComposeViewController.canSendText())
{
let controller = MFMessageComposeViewController()
controller.body = message
controller.recipients = to
if let topController = UIApplication.topViewController()
{
controller.messageComposeDelegate = topController as MFMessageComposeViewControllerDelegate
topController.present(controller, animated: true, completion: nil)
}
}
}
extension UIViewController: MFMessageComposeViewControllerDelegate {
public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
controller.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
controller.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
controller.dismiss(animated: true, completion: nil)
default:
break;
}
}
}
is there anyway to close sms composer without reloading the app ?

Swift 3 How to display a confirmation screen based on MFMailComposeResult email screen

I'm building an app that constructs an email and presents it in a MFMailComposeViewController for the user to send. When the user sends or cancels it I want the app to respond with an appropriate confirmation screen message.
I'm able to compose the email, dismiss the compose screen, and I have a named segue in IB from the pre-compose view to the confirmation view. But I cannot get that segue to execute.
So, how can I update text message in the segue destination and then segue to it.
Because I'm trying to learn Swift I'm very interested in understanding how the code works, not just getting code that works. So I really do appreciate any help.
The workflow starts with the user snapping a photo from the app:
func snapPhoto(){
if let cameraConnection = sessionOutput.connection(withMediaType: AVMediaTypeVideo) {
sessionOutput.captureStillImageAsynchronously(from: cameraConnection, completionHandler: { buffer, error in
let myMessage = self.buildEmail()
let myJpg = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
let mapSnap = (self.myMap == nil) ? nil : UIImagePNGRepresentation(self.myMap!)
let mail = self.setupMail(to: myMessage.to, subject: myMessage.subject, body: myMessage.body, myJpg: myJpg!, mapSnap: mapSnap)
self.presentMailComposer(mail: mail)
}) // close completionHandler
} // close if let cameraConnection
} // close func snapPhoto
Which assembles all of the email message content and passes it to:
func presentMailComposer(mail : MFMailComposeViewController) {
if MFMailComposeViewController.canSendMail() {
self.present(mail, animated: true, completion: nil)
} else {
let sendMailErrorAlert = UIAlertController.init(title: "Uh oh!", message: "Unable to send email.", preferredStyle: .alert)
self.present(sendMailErrorAlert, animated: true, completion: nil)
} // close if
} // close presentEmailComposer
And then this fires when the user taps "Send" of "Cancel"
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
self.performSegue(withIdentifier: "afterEmail", sender: self)
print("Mail cancelled")
case MFMailComposeResult.saved.rawValue:
print("Mail saved")
case MFMailComposeResult.sent.rawValue:
print("Mail sent")
case MFMailComposeResult.failed.rawValue:
print("Mail sent failure: %#", [error!.localizedDescription])
default:
break
}
controller.dismiss(animated: true, completion: nil)
} // close mailCompose
And this is where I find myself stumped. I can access MFMailComposeResult, and it is correct, but I cannot figure out how to present the confirmation view so it is available as the compose view slides away.
You need to make your view controller the MFMailComposeViewController delegate and override the method didFinishWith result and switch MFMailComposeResult value inside the completion handler of the dismiss method :
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true) {
// do whatever you need to do after dismissing the mail window
switch result {
case .cancelled: print("cancelled")
case .saved: print("saved")
case .sent:
let alert = UIAlertController(title: "Mail Composer", message: "Mail was successfully sent", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Done", style: .default, handler: nil))
self.present(alert, animated: true)
case .failed: print("failed")
}
}
}
If you want to present an alert before the controller is dismissed? Try this one:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .cancelled:
let alertController = UIAlertController.init(title: "Cancelled", message: "Some message", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: { (alertAction) in
controller.dismiss(animated: true, completion: nil)
}))
controller.present(alertController, animated: true, completion: nil)
case .sent:
let alertController = UIAlertController.init(title: "Sent", message: "Some message", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: { (alertAction) in
controller.dismiss(animated: true, completion: nil)
}))
controller.present(alertController, animated: true, completion: nil)
default:
break;
}
}

Send SMS in iOS

I am brand new in iOS developing and xCode.
Previously i developed android and i know i should work with Intent to make call and send SMS in android App.
Now i want to develop a simple App that when i press a Button it send a SMS to a specific number.
So i installed a Mac OS 10.11 VM on my Ubuntu.
And could connect connect my iPhone 5 to that and run my simple Hello World App in real iPhone Device and not simulator.
Now i created a Button in StoryBoard and make a funcion by following this article :
Send SMS Message Toturial
Also look at other links and was similar.
Here is my simple ViewController.swift.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendMessage(sender: AnyObject) {
print("Send button pressed") //display a text to make sure is calling
if MFMessageComposeViewController.canSendText() {
let controller = MFMessageComposeViewController()
controller.body = "TestMessage "
controller.recipients = ["xxxxxxxxx", "xxxxxxxxx"] // sending for two numbers
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
}
And also created a Simple Button and link that to sendMessage above.
When i run the app, displays the button and when i press it, it seems it is doing something, but the SMS is not sent.
Any Idea??
I really appreciate it.
Update : As our friend said i added print("Send button pressed") in sendMessage Button, so i find out the button call sendMessage when i press it.
I finally did it, I mixed up two solutions,
First of all,i changed the function of sendMessage and renamed it to sendText, in this way :
#IBAction func sendText(sender: AnyObject) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Text Body"
controller.recipients = ["xxxxxxxxxxx"]
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
And the protocol of MessageComposeViewController is :
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
And then click on the button in storyboards, and press control key and drag the button to ViewController and select the sendText function, now it works correctly.
Thx to my Friend #Kabiroberai for help and give some time.
Heres the bottom function converted to swift 4:
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
self.dismiss(animated: true, completion: nil)
default:
break;
}
}