Search code examples
swiftqr-codeuiimagepngrepresentation

Program to generate QRCode and email it as attachment not working because UIImagePNGRepresentation is giving back nil


Please find code below.

  1. I am creating a QRCode.
  2. Then I want to convert it to png and email as an attachment.

The line:

let imgData = UIImagePNGRepresentation(imageToEmail)

keeps giving back nil and causes the code to crash. Any help?

class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
    @IBOutlet weak var dataField: UITextField!
    @IBOutlet weak var displayCodeView: UIImageView!

    var filter:CIFilter!
    var qrImage = UIImage()

    @IBAction func generatePressed(_ sender: UIButton) {
        if let text = dataField.text {
            let data = text.data(using: .ascii, allowLossyConversion: false)

            filter = CIFilter(name: "CIQRCodeGenerator")
            filter.setValue(data, forKey: "inputMessage")

            let transform = CGAffineTransform(scaleX: 10, y: 10)

            qrImage = UIImage(ciImage: filter.outputImage!.transformed(by: transform))
            displayCodeView.image = qrImage

            let mailComposeViewController = configureMailController(imageToEmail: qrImage)
            if MFMailComposeViewController.canSendMail() {
                self.present(mailComposeViewController, animated: true, completion: nil)
            } else {
                showMailError()
            }
        }
    }

    func configureMailController(imageToEmail: UIImage) -> MFMailComposeViewController {
        let mailComposerVC = MFMailComposeViewController()
        mailComposerVC.mailComposeDelegate = self

        mailComposerVC.setToRecipients(["[email protected]"])
        mailComposerVC.setSubject("QRCode")
        let imgData = UIImagePNGRepresentation(imageToEmail)
        mailComposerVC.addAttachmentData(imgData!, mimeType: "image/png", fileName:  "qrCode.png")
        let emailBody = "<html><body><p>QR code is attached in image </p></body></html>"
        mailComposerVC.setMessageBody(emailBody, isHTML:true)

        return mailComposerVC
    }

    func showMailError() {
        let sendMailErrorAlert = UIAlertController(title: "Email error", message: "Your device could not send email", preferredStyle: .alert)
        let dismiss = UIAlertAction(title: "Ok", style: .default, handler: nil)
        sendMailErrorAlert.addAction(dismiss)
        self.present(sendMailErrorAlert, animated: true, completion: nil)
    }

    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        switch result {
        case .cancelled:
            print("Mail cancelled")
            break
        case .saved:
            print("Mail saved")
            break
        case .sent:
            print("Mail sent")
            break
        case .failed:
            print("Mail failed to send")
            break

        }
        controller.dismiss(animated: true, completion: nil)
    }
}

Solution

  • Add this extension to your code

    extension UIImage {
    
    /**
     Creates the UIImageJPEGRepresentation out of an UIImage
     @return Data
     */
    
    func generatePNGRepresentation() -> Data? {
    
        let newImage = self.copyOriginalImage()
        let newData = UIImagePNGRepresentation(newImage)
    
        return newData!
    }
    
    /**
     Copies Original Image which fixes the crash for extracting Data from UIImage
     @return UIImage
     */
    
    private func copyOriginalImage() -> UIImage {
        UIGraphicsBeginImageContext(self.size);
        self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext();
    
        return newImage!
        }
    }
    

    In your code,

    let imgData = imageToEmail.generatePNGRepresentation()