I am learning how to communicate between swift and c++ for ios. As a first step I have looked on this example:
https://github.com/leetal/ios-cmake
There is an example-app that I have managed to compile and run. Took some time to get it to work. That is an objective-c project.
The next step is to create a new swift project and try and import the compiled library and use the headers in swift instead.
I have not managed to do that. I think the current problem is that I cannot include the header HelloWorldIOS.h.
import SwiftUI
import HelloWorldIOS.h <- No such module found
struct ContentView: View {
var body: some View {
Text(sayHello())
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have tried to create a bridging file example-Bridging-Header.h as suggested here: https://developer.apple.com/documentation/swift/importing-objective-c-into-swift
It looks like:
//
// example-Bridging-Header.h
// example-swift
//
#ifndef example_Bridging_Header_h
#define example_Bridging_Header_h
#import "HelloWorldIOS.h"
#endif /* example_Bridging_Header_h */
I have also added the path to the headers in Target - Build Settings - Header Search Paths
The Objective-C Bridging Header looks like example-swift/example-Bridging-Header.h.
Are there any good instructions for how to call c++ code from a compiled library? I hoped this example I found would be easy to get to work.
The comment below helped me plus that I had to link to libc++.tbd.
You don't import anything in your Swift code when Objective-C headers are imported in the bridging header.
All public interfaces available from the imported files get available in the entire Swift module by default after that.
Sample listing
TDWObject.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface TDWObject : NSObject
- (void)someCPPCode;
#end
NS_ASSUME_NONNULL_END
TDWObject.mm
#include <iostream>
#import "TDWObject.h"
#implementation TDWObject
- (void)someCPPCode {
std::cout << "Hello from CPP cout" << std::endl;
}
#end
Some-Bridging-Header.h
#import "TDWObject.h"
main.swift
TDWObject().someCPPCode()
Provided the main.swift file is the entry point of the program, it will print Hello from CPP cout.
You can use Scapix Language Bridge to automatically bridge C++ to Swift (among other languages). Bridge code automatically generated on the fly directly from C++ header files. Here is an example:
C++:
#include <scapix/bridge/object.h>
class contact : public scapix::bridge::object<contact>
{
public:
std::string name();
void send_message(const std::string& msg, std::shared_ptr<contact> from);
void add_tags(const std::vector<std::string>& tags);
void add_friends(std::vector<std::shared_ptr<contact>> friends);
void notify(std::function<bool(std::shared_ptr<contact>)> callback);
};
Swift:
class ViewController: UIViewController {
func send(friend: Contact) {
let contact = Contact()
contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
contact.notify() {
(c: Contact) in
//...
return true
}
}
}
Disclaimer: I am the author of Scapix Language Bridge.
Related
I've compiled a dylib in Qt/C++. I created a simple class method called test() that reads a string input and returns a string output with "-response" back. Now how do I load that inside Objective C in XCode 7 (a default Cocoa application) and make it emit test() via NSLog()?
This is what my build folder looks like in Qt/C++.
You need to use an Objective-C++ class, which is a hybrid of Objective-C and C++.
The greatest challenge using one or more Objective-C++ classes in a largely Objective-C project is avoiding exposing C++ classes to the Objective-C classes. Therefore you need to avoid C++ in the Objective-C++ header file and just include the C++ in the implementation file.
For example:
CppWrapper.h:
#import <Foundation/Foundation.h>
#interface CppWrapper : NSObject
- (BOOL)callCppMethodA:(int)param;
#end
CppWrapper.mm:
#import "CppWrapper.h"
#import "cppclass.h" // C++
#interface CppWrapper()
{
cppclass *_cppclass; // C++
}
#end
#implementation CppWrapper
- (id)init
{
self = [super init];
if (self) {
_cppclass = new cppclass(); // C++
}
return self;
}
- (void)dealloc
{
delete _cppclass; // C++
}
- (BOOL)callCppMethodA:(int)param
{
return _cppclass->methodA(param); // C++
}
#end
Use it like this:
CppWrapper *cppWrapper = [CppWrapper new];
[cppWrapper callCppMethodA:123];
Another approach would be to not use Qt/C++, but create C++ classes inside of Objective C and avoid Qt altogether, opting for these includes to make life a whole lot easier in C++:
#include <string>
#include <stdio.h>
#include <sqlite3.h>
#include <Foundation/Foundation.h>
string docs
stdio docs
stdio notes
sqlite3 docs
Apple Foundation Class docs
Also, one can (and actually must) mix a little Objective C in their C++ code in order to make life easier. Here's a sample .mm file, which is the file type that lets you mix C++ and Objective C:
#include <string>
#include <stdio.h>
#include <sqlite3.h>
#include <Foundation/Foundation.h>
class Testlib {
public:
std::string test(std::string sIn) {
sIn = sIn.append("-response");
return sIn;
}
NS_RETURNS_RETAINED NSString *test2(NSString *sIn) {
// note [[funky Objective C syntax]]
NSString *sOut = [[NSString alloc] init];
sOut = [NSString stringWithFormat:#"%#-response", sIn];
return sOut;
}
};
In order for me to call this from my main.m file, I had to rename it to main.mm and then do something like:
#import <Cocoa/Cocoa.h>
#import "testlib.mm"
int main(int argc, const char * argv[]) {
// demo Testlib out to the debug log
Testlib *o = new Testlib();
std::string s = "";
s = o->test("request");
NSLog(#"Result=%s",s.c_str());
NSLog(#"Result2=%#",o->test2(#"request"));
// load our GUI
return NSApplicationMain(argc, argv);
}
So, for the most part, it gives the ease of use of C++, but makes it powerful with the SQLite3 and Apple Foundation Class stuff to do pretty much what one would have used Qt for (without having to include very large Qt runtime framework libraries). However, for the GUI -- Cocoa is pretty sparse on options (dare I say fascist) compared to Qt, which is why I opt to use Mac native WebKit inside Cocoa, which opens up a vast array of GUI styling. Also, by using Mac native WebKit instead of Qt's embedded WebKit, you can decrease the .app size by about 30MB.
I'm writing an IOS application with swift that uses a c++ library that needs to load some text files.
I wrapped that library using C/ObjC so now I can call its methods from inside swift.
That library anyway accepts a relative/absolute char* pathname to a file or even a FILE* fd to a file.
What is a good strategy to obtain a path or a file descriptor that I can pass to underlying library from swift?
I guess that I have to bundle those files some way in the app project and then obtain a runtime an handler to those files and then get a pathname or a file descriptor.
I am new to iOS developing and arrived here looking for the same thing, as I wanted to read an array stored in a file and I was getting null when using fopen inside my C++ code. Solved it in the following way with XCode 6.3.2. Corrections or improvements are of course welcome.
Some context.
I dragged some_file.txt to the project navigator next to my files (ViewController.swift, AppDelegate.swift, etc.). I also added a ccode.cpp file with this:
#include "myccode.h"
/*Some other includes*/
#define MAX_AUT_SIZE (1 << 22)
typedef union {
/* some data */
} transition;
transition data_struct[MAX_SIZE];
unsigned data_size;
FILE *the_file;
void read_my_file(const char * path) {
if ((the_file = fopen(path, "rb")) == NULL){
fprintf(stderr, "Cannot open input file");
} else {
fprintf(stderr, "Succees reading file");
}
data_size = fread(data_struct, sizeof data_struct[0], MAX_SIZE, the_file);
fclose(the_file);
/* Other code*/
}
/* Some other C++ functions that use the data structure which
I just read from the file. */
Added myccode.h with:
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
void read_my_file(const char * path);
/* The other C++ functions */
#ifdef __cplusplus
}
#endif
In the My App Name > Supporting Files folder, I placed the file: My App Name-Bridging-Header.h. It only contains this:
#include "ccode.h"
The answer. To let C++ know the path to the file I used this in ViewController.swift.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var path = NSBundle.mainBundle().pathForResource("some_file", ofType: "txt")
var path2 = strdup(path!) /* Swift expects the argument to
have "UnsafePointer<Int8>" type. I don't really understand much of this */
read_my_file(path2)
/* Call some other C functions that use the data structure read from the file.
They work!! */
}
}
I only tested this in development. I have not yet submitted my first app to the store :P
I am writing a device driver for a Blackmagic Design AV device in XCode, and I'm having trouble including BMD's SyncController class from their abbreviated sample code (below) into my purely Objective-C project.
Their DecklinkAPI.h file is rich in C++ code, so when I try include this header file as-is in a an Objective-C class, the compiler chokes deep in the API include: Unknown type name 'class'; did you mean 'Class'?
I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success. I've never done any C++ programming (and have never used Obj-C class extensions), so this is new territory for me.
I'm not sure if I need to create an additional wrapper class for my SyncController object, or whether I can just do a class extension on this one and shuffle the C++ bits into the .mm file.
I would like to be able to do a #include "SyncController.h" (or its wrapper) in an Objective-C class without having the compiler choke.
Any assistance in doing so would be much appreciated.
First up, here is my current SyncController.h file:
#import <Cocoa/Cocoa.h>
#import "DeckLinkAPI.h" // this is rich in C++ code
class PlaybackDelegate;
#interface SyncController : NSObject {
PlaybackDelegate* playerDelegate;
IDeckLink* deckLink;
IDeckLinkOutput* deckLinkOutput;
}
- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;
#end
class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
SyncController* mController;
IDeckLinkOutput* mDeckLinkOutput;
public:
PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput);
// IUnknown needs only a dummy implementation
virtual HRESULT QueryInterface (REFIID iid, LPVOID *ppv) {return E_NOINTERFACE;}
virtual ULONG AddRef () {return 1;}
virtual ULONG Release () {return 1;}
virtual HRESULT ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
virtual HRESULT ScheduledPlaybackHasStopped ();
virtual HRESULT RenderAudioSamples (bool preroll);
};
void ScheduleNextVideoFrame (void);
Next up, here is my (simplified) SyncController.mm file:
#import <CoreFoundation/CFString.h>
#import "SyncController.h"
#implementation SyncController
- (instancetype)init
{
self = [super init];
return self;
}
- (void)dealloc
{
}
- (void)scheduleNextFrame:(BOOL)prerolling
{
}
- (void)writeNextAudioSamples
{
}
#end
PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}
HRESULT PlaybackDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
{
[mController scheduleNextFrame:NO];
return S_OK;
}
HRESULT PlaybackDelegate::ScheduledPlaybackHasStopped ()
{
return S_OK;
}
HRESULT PlaybackDelegate::RenderAudioSamples (bool preroll)
{
[mController writeNextAudioSamples];
if (preroll)
mDeckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
return S_OK;
}
I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success.
If you're targeting 64-bit, the class extension method should be fairly simple.
The following is equivalent to the code you've post, but moves all of the C++ declarations to a separate header:
SyncController.h:
#import <Cocoa/Cocoa.h>
#interface SyncController : NSObject
- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;
#end
SyncController_CPP.h
#import "SyncController.h"
#include "DeckLinkAPI.h"
class PlaybackDelegate;
#interface SyncController() {
PlaybackDelegate* playerDelegate;
IDeckLink* deckLink;
IDeckLinkOutput* deckLinkOutput;
}
#end
class PlaybackDelegate ...
{
...
}
SyncController.mm
#import "SyncController_CPP.h"
#implementation SyncController
...
#end
PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}
// etc..
Any other ObjC classes that need access to SyncController will import "SyncController.h". Any other ObjC++ classes can import either "SyncController.h" or "SyncController_CPP.h"
Not a complete answer, however errors like:
Unknown type name 'class'; did you mean 'Class'?
Is a classic issue with Objective-C++ where an Objective-C implementation file is seeing a C++ header file.
I can only provide advice about how to avoid it as you didn't post the complete build output.
Don't put C++ headers is the pre-compiled header.
Try to only include C++ headers within Objective-C++ implementation files and not in their counterpart header file which might, in turn, be included into an Objective-C file.
Hide the use of C++ from any header files, for example using private instance variables:
#import <vector>
#implementation MyObjCppClass {
std::vector<int> _stuff;
}
- (id)init {
...
}
#end
If you are mixing Objective-C and Objective-C++ then you might find you need to provide Objective-C wrappers to C++ classes (which look from the outside as Objective-C but are actually implemented in Objective-C++).
Rename your .m files (objective-c) to .mm (objective-c++). this should allow you to then mix objc and c++ by including c++ headers and referencing c++ code from your objc.
---EDIT---
Any header file you include from objective-c must contain only objective-c. Remove any c++ from the header in your wrapper class to get the other objc classes to build. In modern objc, you can split your ivars between the .h and .m files; keep all your methods in the .h for other objc classes to use, and declare your c++ ivars in the .mm. Stick your c++ delegate class in its own .h that is only included from the .mm wrapper.
Use #if __cplusplus.
For example,
#import <Cocoa/Cocoa.h>
#if __cplusplus
#import "DeckLinkAPI.h" // this is rich in C++ code
#endif // __cplusplus
#interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
#end
#if __cplusplus
class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
...
};
#endif // __cplusplus
The header file can be used with Objective-C and Objective-C++. But you can not use C++ class signature in SyncController Objective-C class declaration in the header. Use void * instead of PlaybackDelegate * with proper type cast.
Also using void * means that C++ stuff in the header is no longer needed.
#import <Cocoa/Cocoa.h>
#interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
#end
In Objective-C++ code,
// initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate;
initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate
Is it possible to use a native C/C++ library with dot42? If so are there examples of how to do this? If not is this a planned feature?
Given the JNI implementation a native method such as the following:
#include <string.h>
#include <jni.h>
jstring Java_dllImportTest_MainActivity_Foo( JNIEnv* env, jobject thiz)
{
return (*env)->NewStringUTF(env, "Hello from dot42 JNI !");
}
The JNI tooling will compile this to a native library and put this in e.g. libs\armeabi.
Copy the libs folder to the root of your dot42 project and include the .so in your project. Next set the build action to NativeCodeLibrary:
Next declare the native method in your C# code using the DllImport attribute and call it like this:
using System;
using System.Runtime.InteropServices;
using Android.App;
using Android.Os;
using Android.Widget;
using Dot42;
using Dot42.Manifest;
[assembly: Application("DllImportTest")]
namespace DllImportTest
{
[Activity(Label ="DllImport MainActivity")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
SetContentView(R.Layouts.MainActivityLayout);
var textView = FindViewById<TextView>(R.Ids.label);
textView.Text = Foo();
}
[DllImport("dllImportTest")]
public static extern string Foo();
}
}
The loadLibrary call is generated by the dot42 compiler.
I am using a c++ library using callback to inform about the progress of an operation.
Everything is working fine except this:
I need to have a function in my controller to be use as a c++ callback function.
I've tried a lot of things but none of them are working.
Do you know how we can manage this kind of thing?
Thanks :)
iPhone APIs like the Audio Queue Services use a void * parameter in their callbacks, into which you can stuff your Objective-C instance.
If your C++ library has a similar setup - your callback gives a void * "context" parameter - you could do this:
void interruptionListener(void *inClientData, UInt32 inInterruptionState) {
InterruptionMonitor *self = (InterruptionMonitor *)inClientData;
[self inInterruption: inInterruptionState == kAudioSessionBeginInterruption];
}
So you use the inClientData to store your instance, and can then call methods on that instance that do the actual processing.
You have to define a c++-class in your .h with your callback methods, implementing the c++-interface. This class also keeps a delegate of your objC Class.
in your .m File after #end you specify the c++ methods. You may then use the delegate to perform selectors of your objC class
in .h
#interface YourObjcClass {
#ifdef __cplusplus
class FooObserver : public YourNS::Interface {
public:
virtual ~FooObserver() {
}
YourObjcClass *delegate;
};
YourNS::YourCallbackClass *myCallbackClass;
#endif
in .m
#ifdef __cplusplus
void FooObserver::callback( args ) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[delegate performSelectorOnMainThread:#selector(performCallback)
withObject:nil
waitUntilDone:false];
[pool release];
}
#endif
There is a much better way of doing this. If you are already using boost in your iOS project you can use my objc_callback template: http://alex.tapmania.org/2012/04/c-to-obj-c-callbacks-part-2.html