Native Objective-C calls from cpp/qt (IOS Email-call)
-
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 ?
-
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.
-
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 ? -
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 ?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
-
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?
-
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.
-
ios { LIBS += -framework MessageUI }