Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Native Objective-C calls from cpp/qt (IOS Email-call)


  • Moderators

    Hello everyone,

    I'm trying send E-Mails with attachments that are created by my application. Because this is only really possible with native IOS/Objective-C code, I created the following simple (pure x-code) example that upon a button press shows the Mail-App:

    - (IBAction)showEmail :(id)sender{
        //Email Subject
        NSString *emailTitle = @"Test Email";
        
        //Email Content
        NSString *messageBody = @"ios programming is so fun!";
        
        //To Address
        NSArray *toRecipents = [NSArray arrayWithObject:@"support@myCompany.com"];
        
        MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
        mc.mailComposeDelegate = self;
        
        [mc setSubject:emailTitle];
        [mc setMessageBody:messageBody isHTML:false];
        [mc setToRecipients:toRecipents];
        
        [self presentViewController:mc animated:YES completion:NULL];
    }
    
    - (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(nullable NSError *)error{
        switch (result) {
            case MFMailComposeResultCancelled:
                NSLog(@"Mail cancelled");
                break;
            case MFMailComposeResultSaved:
                NSLog(@"Mail saved");break;
            case MFMailComposeResultSent:
                NSLog(@"Mail sent");break;
            case MFMailComposeResultFailed:
                NSLog(@"Mail sent failure: %@", [error localizedDescription]);
                break;
            default:
                break;
        }
        
        [self dismissViewControllerAnimated:YES completion:NULL];
    }
    

    this works fine, EMail window appears and sends the E-Mail away without problem.

    To intereate this now in cpp/Qt, U create a native c-interface:

    //ObjectiveCInterface.h
    #ifndef __OBJECTIVECINTERFACE_H__
    #define __OBJECTIVECINTERFACE_H__
    
    class MyClassImpl
    {
    public:
        MyClassImpl (void);
        ~MyClassImpl (void);
    
        void init (void);
    
        void showEmail(char * aCstr);
    
    private:
        void * self;
    };
    
    #endif
    
    

    and a cpp/objective c - combi class

    //MyObject.h
    #import "ObjectCInterface.h"
    #import <Foundation/Foundation.h>
    
    @interface MyObject : NSObject
    {
    
    }
    
    - (void)showEmail:(char *) aCStr;
    
    @end
    
    //MyObject.mm
    #import "MyObject.h"
    #import <UIKit/UIKit.h>
    #import <MessageUI/MessageUI.h>
    
    @implementation MyObject
    
    MyClassImpl::MyClassImpl( void ) : self(NULL){
        init();
    }
    
    MyClassImpl::~MyClassImpl( void ){
        [(id)self dealloc];
    }
    
    void MyClassImpl::init( void ){
        self = [ [MyObject alloc] init];
    }
    
    
    void MyClassImpl::showEmail(char *aCstr){
        NSLog(@"logMyMessage");
        [(id)self showEmailPopup:aCstr];
    }
    
    
    - (void) showEmailPopup:(char *)aCStr{
        NSLog(@"LogMyMessage in Obj");
        NSLog([NSString stringWithUTF8String:aCStr]);
    
    /* Code From working X-Code Project
        //Email Subject
        NSString *emailTitle = @"Test Email";
        
        //Email Content
        NSString *messageBody = @"ios programming is so fun!";
        
        //To Address
        NSArray *toRecipents = [NSArray arrayWithObject:@"support@myCompany.com"];
        
        MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
        mc.mailComposeDelegate = self;
        
        [mc setSubject:emailTitle];
        [mc setMessageBody:messageBody isHTML:false];
        [mc setToRecipients:toRecipents];
        
        [self presentViewController:mc animated:YES completion:NULL];
    */
    }
    
    @end
    
    
    //Mainwindow
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include "ObjectCInterface.h"
    
    #include <QPushButton>
    #include <QVBoxLayout>
    #include <QDebug>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow), _impl( NULL )
    {
        ui->setupUi(this);
    
        QPushButton *btn = new QPushButton("Email");
        connect(btn, &QPushButton::clicked, this, &MainWindow::btnClicked);
        QVBoxLayout *lay = new QVBoxLayout(ui->centralWidget);
        lay->addWidget(btn);
    
        init();
    }
    
    void MainWindow::btnClicked(){
        qDebug() << "BtbClicked";
        showNativeEmail();
    }
    
    MainWindow::~MainWindow()
    {
        if(_impl)
        {
            delete _impl;
            _impl = NULL;
        }
        delete ui;
    }
    
    void MainWindow::init()
    {
        _impl = new MyClassImpl();
    }
    
    void MainWindow::showNativeEmail()
    {    
        _impl->showEmail((char*)("Path/to/Attachment.pdf"));
    }
    
    

    So far so good, when I press the Button in my QMainWindow I do get the NSLog entry "Path/To/Attachment.pdf"

    But when I now copy and paste the working code from the X-Code Project into my MyObject.mm class I do get a couple of warnings and when I run the program I get a sigbart-crash.
    in the Line

    [self presentViewController:mc animated:YES completion:NULL];
    

    Clearly I'm missing something. Anyone got an Idea what it could be ?


  • Moderators

    Never mind, @SGaist , I managed to make it work.

    //ObjectCInterface.h
    #ifndef __OBJECTIVECINTERFACE_H__
    #define __OBJECTIVECINTERFACE_H__
    
    class MyClassImpl
    {
    public:
        MyClassImpl (void);
        ~MyClassImpl (void);
    
        void init (void);
    
        void showEmail(char * aCstr);
    
    private:
        void * self;
    };
    
    #endif
    
    //MyObject.h
    #import "ObjectCInterface.h"
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import <MessageUI/MessageUI.h>
    
    @interface MyObject : UIViewController  <MFMailComposeViewControllerDelegate>
    {
    
    }
    
    - (void)showEmailPopup:(char *) aCStr;
    
    @end
    
    //MyObject.mm
    #import "MyObject.h"
    
    @implementation MyObject
    
    
    MyClassImpl::MyClassImpl( void ) : self(NULL){
        init();
    }
    
    MyClassImpl::~MyClassImpl( void ){
        [(id)self dealloc];
    }
    
    void MyClassImpl::init( void ){
        self = [ [MyObject alloc] init];
    }
    
    
    void MyClassImpl::showEmail(char *aCstr){
        NSLog(@"logMyMessage");
        [(id)self showEmailPopup:aCstr];
    }
    
    
    - (void) showEmailPopup:(char *)aCStr{
        NSLog(@"LogMyMessage in Obj");
        NSLog([NSString stringWithUTF8String:aCStr]);
    
        //Email Subject
        NSString *emailTitle = @"Test Email";
        
        //Email Content
        NSString *messageBody = @"ios programming is so fun!";
        
        //To Address
        NSArray *toRecipents = [NSArray arrayWithObject:@"support@MyCompany.com"];
        
        MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
        mc.mailComposeDelegate = self;
        
        [mc setSubject:emailTitle];
        [mc setMessageBody:messageBody isHTML:false];
        [mc setToRecipients:toRecipents];
        
        UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
        while (topController.presentedViewController){
            topController = topController.presentedViewController;
        }
        
        [topController presentViewController:mc animated:YES completion:NULL];
    }
    
    
    - (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(nullable NSError *)error{
        switch (result) {
            case MFMailComposeResultCancelled:
                NSLog(@"Mail cancelled");
                break;
            case MFMailComposeResultSaved:
                NSLog(@"Mail saved");break;
            case MFMailComposeResultSent:
                NSLog(@"Mail sent");break;
            case MFMailComposeResultFailed:
                NSLog(@"Mail sent failure: %@", [error localizedDescription]);
                break;
            default:
                break;
        }
        
        [controller dismissViewControllerAnimated:YES completion:NULL];
    }
    
    @end
    
    

    Now I'll have to find out how to add the MessageuiFramework to my *.pro file to compile it usccessfully in QtCreator . But thats an other topic I guess.


  • Lifetime Qt Champion

    Hi,

    On which line exactly do you have that failure ?
    Which version of Qt are you using ?
    Which version of Xcode ?
    Which iOS version are you targeting ?


  • Moderators

    Hi @SGaist ,
    thanks for your time, I'm really a novice with objetcive-c so I'm struggeling quite a bit.

    On which line exactly do you have that failure ?

    that would be in this line:

    - (void) showEmailPopup:(char *)aCStr{
        NSLog(@"LogMyMessage in Obj");
        NSLog([NSString stringWithUTF8String:aCStr]);
    
        //Email Subject
        NSString *emailTitle = @"Test Email";
        
        //Email Content
        NSString *messageBody = @"ios programming is so fun!";
        
        //To Address
        NSArray *toRecipents = [NSArray arrayWithObject:@"support@myCompany.com"];
        
        MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
        mc.mailComposeDelegate = self;
        
        [mc setSubject:emailTitle];
        [mc setMessageBody:messageBody isHTML:false];
        [mc setToRecipients:toRecipents];
        
    //!---------------------This Line------------------
        [self presentViewController:mc animated:YES completion:NULL];
    }
    

    the xcode error is:

    ***Terminating app due to uncaught exception 'NSInvalidArgumentException', reason '-[MyObject presentViewController:animated:completion:] unrecognized selector sent to instance 0x1c001e8f0'
    

    My guess would be I didn't initialize 'self' correctly ?

    Which version of Qt are you using ?

    I'm using Qt 5.10.1

    Which version of Xcode ?

    Version 9.2(9C40b)

    Which iOS version are you targeting ?

    Deployment Target is set to 10.0


  • Moderators

    Ok, I made some changes in my code. It sill doesn't show the E-Mail Popup but it no longer crashes.

    In the header file I changed

    @interface MyObject : NSObject
    {
    
    }
    
    //to
    @interface MyObject : UIViewController <MFMailComposeViewControllerDelegate>
    {
    
    }
    

    Aparently a NSOBject alone can not be the base of a ViewController?

    however now I run int this error or warning as nothing happens:

    Warning: Attempt to present <MFMailComposeViewController> on <MyObject> whose view is not in the window hierarchy!
    

    So, how would I make it a defined part of the window hierarchy, when the rest of the UI is made by Qt-Code?


  • Moderators

    Never mind, @SGaist , I managed to make it work.

    //ObjectCInterface.h
    #ifndef __OBJECTIVECINTERFACE_H__
    #define __OBJECTIVECINTERFACE_H__
    
    class MyClassImpl
    {
    public:
        MyClassImpl (void);
        ~MyClassImpl (void);
    
        void init (void);
    
        void showEmail(char * aCstr);
    
    private:
        void * self;
    };
    
    #endif
    
    //MyObject.h
    #import "ObjectCInterface.h"
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import <MessageUI/MessageUI.h>
    
    @interface MyObject : UIViewController  <MFMailComposeViewControllerDelegate>
    {
    
    }
    
    - (void)showEmailPopup:(char *) aCStr;
    
    @end
    
    //MyObject.mm
    #import "MyObject.h"
    
    @implementation MyObject
    
    
    MyClassImpl::MyClassImpl( void ) : self(NULL){
        init();
    }
    
    MyClassImpl::~MyClassImpl( void ){
        [(id)self dealloc];
    }
    
    void MyClassImpl::init( void ){
        self = [ [MyObject alloc] init];
    }
    
    
    void MyClassImpl::showEmail(char *aCstr){
        NSLog(@"logMyMessage");
        [(id)self showEmailPopup:aCstr];
    }
    
    
    - (void) showEmailPopup:(char *)aCStr{
        NSLog(@"LogMyMessage in Obj");
        NSLog([NSString stringWithUTF8String:aCStr]);
    
        //Email Subject
        NSString *emailTitle = @"Test Email";
        
        //Email Content
        NSString *messageBody = @"ios programming is so fun!";
        
        //To Address
        NSArray *toRecipents = [NSArray arrayWithObject:@"support@MyCompany.com"];
        
        MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
        mc.mailComposeDelegate = self;
        
        [mc setSubject:emailTitle];
        [mc setMessageBody:messageBody isHTML:false];
        [mc setToRecipients:toRecipents];
        
        UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
        while (topController.presentedViewController){
            topController = topController.presentedViewController;
        }
        
        [topController presentViewController:mc animated:YES completion:NULL];
    }
    
    
    - (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(nullable NSError *)error{
        switch (result) {
            case MFMailComposeResultCancelled:
                NSLog(@"Mail cancelled");
                break;
            case MFMailComposeResultSaved:
                NSLog(@"Mail saved");break;
            case MFMailComposeResultSent:
                NSLog(@"Mail sent");break;
            case MFMailComposeResultFailed:
                NSLog(@"Mail sent failure: %@", [error localizedDescription]);
                break;
            default:
                break;
        }
        
        [controller dismissViewControllerAnimated:YES completion:NULL];
    }
    
    @end
    
    

    Now I'll have to find out how to add the MessageuiFramework to my *.pro file to compile it usccessfully in QtCreator . But thats an other topic I guess.


  • Lifetime Qt Champion

    ios {
        LIBS += -framework MessageUI
    }
    

Log in to reply