MFMailComposeViewController doesn't read variable into message in Swift - ios

I have small problem. I'm using MFMailComposeViewController to send email after work. So I have var allRuns, and I've printed that var in few places, and it always show value = 3. But in my MailViewController into MessageBody it's equal 0/nil (I've set 0). If it is important- I'm taking var from Firebase, but earlier in the app. Where should I seek problem? That's some code below.
MailViewController:
import UIKit
import MessageUI
class MailViewController: UIViewController, MFMailComposeViewControllerDelegate {
let av = ActualValues()
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - Buttons
#IBAction func emailButtonTapped(_ sender: UIButton) {
showMailComposer()
}
#IBAction func backButtonTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
//MARK: -Func
func showMailComposer() {
if MFMailComposeViewController.canSendMail() {
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
mailComposer.setToRecipients(["paereson#gmail.com"])
mailComposer.setSubject("Work of \(av.currentDate())")
mailComposer.setMessageBody("All runs: \(av.allRuns)", isHTML: false)
present(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult, error: Error?) {
if let _ = error {
//show alert
controller.dismiss(animated: true, completion: nil)
return
}
switch result {
case .cancelled:
print("Cancelled")
case .failed:
print("Failed")
case .saved:
print("saved")
case .sent:
print("Email sent")
#unknown default:
print("default")
}
controller.dismiss(animated: true, completion: nil)
}
}
Few lines in av = ActualValuse:
var allRuns: Int = 0
var runs: Array? = []
//All runs
func allRunsFunc() {
let ref1 = ref.child("\(currentYear())/\(currentMonth())/\(currentDay())")
ref1.observeSingleEvent(of: .value) { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
self.allRuns = snapshots.count as Int
}
}
}
EDIT:
Should I use segue before MFMAilComposeViewController? If yes, how should I configure it?
import UIKit
import MessageUI
class MailViewController: UIViewController, MFMailComposeViewControllerDelegate {
let av = ActualValues()
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - Buttons
#IBAction func emailButtonTapped(_ sender: UIButton) {
showMailComposer()
}
#IBAction func backButtonTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
//MARK: -Func
func showMailComposer() {
if MFMailComposeViewController.canSendMail() {
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
mailComposer.setToRecipients(["paereson#gmail.com"])
mailComposer.setSubject("Work of \(av.currentDate())")
mailComposer.setMessageBody("\(av.exportText())", isHTML: true)
present(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult, error: Error?) {
if let _ = error {
//show alert
controller.dismiss(animated: true, completion: nil)
return
}
switch result {
case .cancelled:
print("Cancelled")
case .failed:
print("Failed")
case .saved:
print("saved")
case .sent:
print("Email sent")
#unknown default:
print("default")
}
controller.dismiss(animated: true, completion: nil)
}
func configureMail(model: ActualValues) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is MFMailComposeViewController {
let vc = segue.destination as? MFMailComposeViewController
vc?.configureMail(model: av)
}
}
}

You seem to be missing the execution of allRunsFunc function. Maybe call it in the ActualValue init method.

Related

My segue is not executed after taking a pic

I'm developing an app which uses a camera and then the photo is supposed to be sent to another ViewController, but i'm not sure if it maybe the segue is not be able to there or that it is just be executed, but when I confirm the photo my app crashed and no errors are showed on my log.
This is the camera View controller
import UIKit
import AVFoundation
class ViewController: UIViewController ,
UIImagePickerControllerDelegate, UINavigationControllerDelegate{
var img = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func photo(_ sender: Any) {
checkCameraPermissions()
}
private func checkCameraPermissions() {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
print("1")
presentPicker()
case .notDetermined:
print("2")
askPermision()
case .denied:
print("3")
// user denied access
self.permissionDenied()
case .restricted:
print("Error restricted")
}
}
private func presentPicker() {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerController.SourceType.camera
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage{
img.image = pickedImage
}
performSegue(withIdentifier: "captured", sender: nil)
}
private func askPermision(){
AVCaptureDevice.requestAccess(for: AVMediaType.video) {granted in
if granted {
self.presentPicker()
} else {
print("Denied")
}
}
}
private func permissionDenied() {
let alert = UIAlertController(title: "Access to camera is denied", message: "You have denied the access to the camera. Would you like to able it?", preferredStyle: .alert)
let actionOK = UIAlertAction(title: "Ok", style: .default) { (UIAlertAction) in
self.askPermision()
}
let cancel = UIAlertAction(title: "Cancel", style: .default) { (action) in
print("Cancel")
}
alert.addAction(actionOK)
alert.addAction(cancel)
present(alert, animated : true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "captured"){
let vc = segue.destination as! ImageReportedVC
vc.imgAux = img.image!
}
}
}
And then, this is the 2nd VC which doesn't appear. I believe the segue shouldn't be done there or something, but if you put it in another part of the code, it is executed before taking the photo.
import UIKit
class ImageReportedVC: UIViewController {
var imgAux = UIImage()
#IBOutlet weak var imgReported: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imgReported.image = imgAux
// Do any additional setup after loading the view.
}
}
Thanks
You need to dismiss the picker
picker.dismiss(animated:true) {
self.performSegue(withIdentifier: "captured", sender: nil)
}

UIImagePickerController what is the right approach to avoid error Code=13 "query cancelled"

I'm implementing a photopicker in my project and it works or not, with the same code, depending the way I implement it. In my first approach I was using a custom UIAlertAction class and doing all the stuff in there, to have my main controller lighter, but the picker delegate was never called, instead it prints an error message in the console ([discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled}) and I would like to discuss if the way I were implementing the picker was right or wrong and why? Or if it's a bug from apple. I've been googling for a while and checking all the related questions in stack overflow, and anything has worked for me except to put the picker code in my main controller.
Here is the code of my main controller, when it was not calling the delegate:
First approach that give me error and don't call delegates
import UIKit
import MobileCoreServices
import Photos
class ViewController: UIViewController {
#IBOutlet weak var lblMain: UILabel!
#IBOutlet weak var buttonsBottom: NSLayoutConstraint!
#IBOutlet weak var imgFromUser: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
hideButtons()
}
override func viewDidAppear(_ animated: Bool) {
showButtons()
checkPermission()
}
func checkPermission() {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized: print("Access is granted by user")
case .notDetermined: PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized { print("success") }
})
case .restricted: print("User do not have access to photo album.")
case .denied: print("User has denied the permission.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func hideButtons()
{
buttonsBottom.constant += self.view.frame.size.height * 0.15
}
func showButtons(){
UIView.animate(withDuration: 0.5) {
}
UIView.animate(withDuration: 0.5, animations: {
self.buttonsBottom.constant = 0
self.view.layoutIfNeeded()
}) { (completed) in
self.lblMain.text = firstController.lblMain
}
}
#IBAction func addImagePressed(_ sender: Any) {
let alertViewController : AlertAction = AlertAction.init(controller: self, type: .photoGallery)
self.present((alertViewController.setType(alert: .photoGallery)), animated: true, completion: nil)
}
}
And this is my custom AlertAction class, it implements the picker & navigation delegate, creates the UIImagePickerController, sets the delegate to it, creates the alert action and set it to present the main view controller, adds it to a UIAlertController and gets returned to the main view controller which present it:
import UIKit
import Photos
class AlertAction: UIAlertAction {
var destinationController : ViewController?
var imagePicker = UIImagePickerController()
convenience init(controller : ViewController, type : type) {
self.init()
destinationController = controller
//Init picker
imagePicker.delegate = self
imagePicker.sourceType = type == .photoGallery ? UIImagePickerControllerSourceType.photoLibrary : UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = false
}
enum type {
case camera
case photoGallery
}
var alertType : type = .camera
func setType(alert : type) -> UIAlertController {
alertType = alert
return alertType == .camera ? newAlert() : newAlert()
}
func newAlert() -> UIAlertController{
//set alert text
let alertText = alertType == .camera ? AlertText.typeCamera : AlertText.typeGalery
let myAlert = UIAlertController(title: AlertText.title, message: "", preferredStyle: .actionSheet)
if alertType == .camera {
myAlert.addAction(getCameraAction(alertText: alertText))
return myAlert
}
else{
myAlert.addAction(getGalleryAction(alertText: alertText))
return myAlert
}
}
func getCameraAction(alertText : String) -> UIAlertAction{
let cameraAction = UIAlertAction(title : alertText, style : .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
self.destinationController!.present(self.imagePicker, animated: true, completion: nil)
}
}
return cameraAction
}
func getGalleryAction(alertText : String) -> UIAlertAction{
let photoLibraryAction = UIAlertAction(title: alertText, style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
self.destinationController!.present(self.imagePicker, animated: true, completion: nil)
}
}
return photoLibraryAction
}
}
extension AlertAction : UIImagePickerControllerDelegate {
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType.isEqual(to: kCIAttributeTypeImage as String){
destinationController!.imgFromUser.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
destinationController!.dismiss(animated: true, completion: nil)
}
#objc func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
destinationController!.dismiss(animated: true, completion: nil)
}
}
extension AlertAction : UINavigationControllerDelegate {
}
After reading a while, I've been trying all the posible solutions in this custom class but nothing worked.
Then I tried to implement a picker creation method in my main view controller and it worked.
So my question is very simple, why the delegate methods get called only if I do all the coding stuff in the main view controller but don't work in a custom class?
Here is the code that I'm currently using and works:
Second approach that works, all the code in the same ViewController
import UIKit
import MobileCoreServices
import Photos
class ViewController: UIViewController {
#IBOutlet weak var lblMain: UILabel!
#IBOutlet weak var buttonsBottom: NSLayoutConstraint!
#IBOutlet weak var imgFromUser: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
hideButtons()
}
override func viewDidAppear(_ animated: Bool) {
showButtons()
checkPermission()
}
func checkPermission() {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized: print("Access is granted by user")
case .notDetermined: PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized { print("success") }
})
case .restricted: print("User do not have access to photo album.")
case .denied: print("User has denied the permission.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func hideButtons()
{
buttonsBottom.constant += self.view.frame.size.height * 0.15
}
func showButtons(){
UIView.animate(withDuration: 0.5) {
}
UIView.animate(withDuration: 0.5, animations: {
self.buttonsBottom.constant = 0
self.view.layoutIfNeeded()
}) { (completed) in
self.lblMain.text = firstController.lblMain
}
}
#IBAction func addImagePressed(_ sender: Any) {
self.present(addPicker(), animated: true, completion: nil)
}
//MARK: Picker methods
func addPicker()->UIAlertController{
let alertText = AlertText.typeGalery
let myAlert = UIAlertController(title: AlertText.title, message: "", preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title : alertText, style : .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
}
myAlert.addAction(cameraAction)
return myAlert
}
}
extension ViewController : UIImagePickerControllerDelegate {
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage{
self.imgFromUser.image = image
}
if mediaType.isEqual(to: kCIAttributeTypeImage as String){
self.imgFromUser.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
self.dismiss(animated: true, completion: nil)
}
#objc func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
}
extension ViewController : UINavigationControllerDelegate {
}
Update: the error [discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled} still appears in my second implementation, but only when I remove #objc before the picker delegate method and environement varibable OS_ACTIVITY_MODE = disable is not set, but the code still works anda the delegates are called correctly (in the second implementation) so basically this error is not related with the code functionality and don't describe anything useful

Can't get app to dismiss the mail compose view controller & return to first screen

I'm using the Swift book to try to learn to code. I added the delegate method to dismiss the view, but it's not working. What am I missing here?
#IBAction func emailButtonTapped(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Can not send mail")
return
}
guard MFMailComposeViewController.canSendMail() else { return }
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
mailComposer.setToRecipients(["example#example.com"])
mailComposer.setSubject("Look at this")
mailComposer.setMessageBody("Hello, this is an email from the app I made.", isHTML: false)
present(mailComposer, animated: true, completion: nil)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
dismiss(animated: true, completion: nil)
}
}
You just need to declare the delegate function outside of the IBAction:
#IBAction func emailButtonTapped(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Can not send mail")
return
}
guard MFMailComposeViewController.canSendMail() else { return }
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
mailComposer.setToRecipients(["example#example.com"])
mailComposer.setSubject("Look at this")
mailComposer.setMessageBody("Hello, this is an email from the app I made.", isHTML: false)
present(mailComposer, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
dismiss(animated: true, completion: nil)
}

Closing view when canceled email

I've been working on this app. I can't get the view of the email to close. What do I do?
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var Label: UITextField!
#IBAction func SendOrder(_ sender: Any) {
let recipients = ["EmailHere"]
let title = "My order is:"
let message = Label.text
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(recipients)
mc.setSubject(title)
mc.setMessageBody(message!, isHTML: false)
self.present(mc, animated: true, completion: nil)
I have tried the Delegate method but it comes up with this:
What do I do??
https://i.stack.imgur.com/xqgTW.png
TRY THiS
#IBAction func btnEmail(_ sender: AnyObject) {
self.openEmail()
}
func openEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setSubject("Your subject Name")
mail.setMessageBody("Contact Us", isHTML: false)
mail.setToRecipients(["test#gmail.com"])
present(mail, animated: true, completion: { _ in })
}
else {
print("This device cannot send email")
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .sent:
print("You sent the email.")
break
case .saved:
print("You saved a draft of this email")
break
case .cancelled:
print("You cancelled sending this email.")
break
case .failed:
print("Mail failed: An error occurred when trying to compose this email")
break
}
dismiss(animated: true, completion: { _ in })
}

There's a bug in CNContactPickerController

After spending all day (>12 hours) trying to isolate a bug in 13 lines of mind-bogglingly generic code, I have come to the dubious conclusion that there must be a bug in the current iteration of CNContactPickerViewController, in iOS 9.2.
Simply copy+paste this ViewController and link the invite action to a button.
The bug is that MFMessageComposeViewController dismisses itself immediately.
If anybody knows what to do with this, do share?
import UIKit
import MessageUI
import ContactsUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate, CNContactPickerDelegate {
let contactPickerVC = CNContactPickerViewController()
let messageVC = MFMessageComposeViewController()
override func viewDidLoad() {
super.viewDidLoad()
contactPickerVC.delegate = self
}
func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {
// Configure message ViewController
messageVC.messageComposeDelegate = self
messageVC.recipients = [phoneNumber]
messageVC.body = "Yoyoyo"
picker.presentViewController(messageVC, animated: true, completion: nil)
}
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func invite(sender: AnyObject) {
presentViewController(contactPickerVC, animated: true, completion: nil)
}
}
I got it working by dismissing the pickerVC and changing the controller which presents the messageVC!
Insert (before the messageVC config lines):
picker.dismissViewControllerAnimated(true, completion: nil)
Replace
picker.presentViewController(messageVC, animated: true, completion: nil)
with
presentViewController(messageVC, animated: true, completion: nil)

Resources