Android with NDK has support to C/C++ code and iOS with Objective-C++ has support too, so how can I write applications with native C/C++ code shared between Android and iOS?

- 33,559
- 24
- 104
- 119

- 14,215
- 14
- 85
- 114
-
1try cocos2d-x framework – glo Aug 20 '13 at 13:04
-
@glo it seems good, but i'm looking for a more generic thing, using the c++ without frameworks, "excluded JNI, obviously". – ademar111190 Aug 20 '13 at 13:12
2 Answers
Update.
This answer is quite popular even four years after I write it, in this four years a lot of things has changed, so I decided to update my answer to fit better our current reality. The answer idea does not change; the implementation has changed a little. My English also has changed, it has improved a lot, so the answer is more understandable to everyone now.
Please take a look at the repo so you can download and run the code I'll show below.
The Answer
Before I show the code, please take a lot on the following diagram.
Each OS has its UI and peculiarities, so we intend to write specific code to each platform in this regard. In other hands, all logic code, business rules, and things that can be shared we intend to write using C++, so we can compile the same code to each platform.
In the diagram, you can see the C++ layer at the lowest level. All shared code is in this segment. The highest level is regular Obj-C / Java / Kotlin code, no news here, the hard part is the middle layer.
The middle layer to iOS side is simple; you only need to configure your project to build using a variant of Obj-c know as Objective-C++ and it is all, you have access to C++ code.
The thing became harder on the Android side, both languages, Java and Kotlin, on Android, run under a Java Virtual Machine. So the only way to access C++ code is using JNI, please take time to read the basics of JNI. Fortunately, today's Android Studio IDE has vast improvements on JNI side, and a lot of problems are shown to you while you edit your code.
The code by steps
Our sample is a simple app that you send a text to CPP, and it converts that text to something else and returns it. The idea is, iOS will send "Obj-C" and Android will send "Java" from their respective languages, and the CPP code will create a text as a follow "cpp says hello to << text received >>".
Shared CPP code
First of all, we are going to create the shared CPP code, doing it we have a simple header file with the method declaration that receives the desired text:
#include <iostream>
const char *concatenateMyStringWithCppString(const char *myString);
And the CPP implementation:
#include <string.h>
#include "Core.h"
const char *CPP_BASE_STRING = "cpp says hello to %s";
const char *concatenateMyStringWithCppString(const char *myString) {
char *concatenatedString = new char[strlen(CPP_BASE_STRING) + strlen(myString)];
sprintf(concatenatedString, CPP_BASE_STRING, myString);
return concatenatedString;
}
Unix
An interesting bonus is, we can also use the same code for Linux and Mac as well as other Unix systems. This possibility is especially useful because we can test our shared code faster, so we are going to create a Main.cpp as follow to execute it from our machine and see if the shared code is working.
#include <iostream>
#include <string>
#include "../CPP/Core.h"
int main() {
std::string textFromCppCore = concatenateMyStringWithCppString("Unix");
std::cout << textFromCppCore << '\n';
return 0;
}
To build the code, you need to execute:
$ g++ Main.cpp Core.cpp -o main
$ ./main
cpp says hello to Unix
iOS
It is time to implement on the mobile side. As far as iOS has a simple integration we are starting with it. Our iOS app is a typical Obj-c app with only one difference; the files are .mm
and not .m
. i.e. It is an Obj-C++ app, not an Obj-C app.
To a better organization, we create the CoreWrapper.mm as follow:
#import "CoreWrapper.h"
@implementation CoreWrapper
+ (NSString*) concatenateMyStringWithCppString:(NSString*)myString {
const char *utfString = [myString UTF8String];
const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
NSString *objcString = [NSString stringWithUTF8String:textFromCppCore];
return objcString;
}
@end
This class has the responsibility to convert CPP types and calls to Obj-C types and calls. It is not mandatory once you can call CPP code on any file you want on Obj-C, but it helps to keep the organisation, and outside your wrapper files you maintain a complete Obj-C styled code, only the wrappers file become CPP styled.
Once your wrapper is connected to the CPP code, you can use it as a standard Obj-C code, e.g. ViewController"
#import "ViewController.h"
#import "CoreWrapper.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString* textFromCppCore = [CoreWrapper concatenateMyStringWithCppString:@"Obj-C++"];
[_label setText:textFromCppCore];
}
@end
Take a look of how the app looks:
Android
Now it is time for Android integration. Android uses Gradle as the build system, and to C/C++ code it uses CMake. So the first thing we need to do is to configure the CMake on gradle file:
android {
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
...
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++14"
}
}
...
}
And the second step is to add the CMakeLists.txt file:
cmake_minimum_required(VERSION 3.4.1)
include_directories (
../../CPP/
)
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
../../CPP/Core.h
../../CPP/Core.cpp
)
find_library(
log-lib
log
)
target_link_libraries(
native-lib
${log-lib}
)
The CMake file is where you need to add the CPP files and header folders you will use on the project, on our example, we are adding the CPP
folder and the Core.h/.cpp files. To know more about C/C++ configuration please read it.
Now the core code is part of our app it is time to create the bridge, to make the things more simple and organized we create a specific class named CoreWrapper to be our wrapper between JVM and CPP:
public class CoreWrapper {
public native String concatenateMyStringWithCppString(String myString);
static {
System.loadLibrary("native-lib");
}
}
Note this class has a native
method and loads a native library named native-lib
. This library is the one we create, in the end, the CPP code will become a shared object .so
File embed in our APK, and the loadLibrary
will load it. Finally, when you call the native method, the JVM will delegate the call to the loaded library.
Now the most strange part of Android integration is the JNI; We need a cpp file as follow, in our case "native-lib.cpp":
extern "C" {
JNIEXPORT jstring JNICALL Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString(JNIEnv *env, jobject /* this */, jstring myString) {
const char *utfString = env->GetStringUTFChars(myString, 0);
const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
jstring javaString = env->NewStringUTF(textFromCppCore);
return javaString;
}
}
The first thing you will notice is the extern "C"
this part is necessary to JNI work correctly with our CPP code and method linkages. You will also see some symbols JNI uses to works with JVM as JNIEXPORT
and JNICALL
. To you understand the meaning of those things, it is necessary to take a time and read it, for this tutorial purposes just consider these things as boilerplate.
One significant thing and usually the root of a lot of problems is the name of the method; it needs to follow the pattern "Java_package_class_method". Currently, Android studio has excellent support for it so it can generate this boilerplate automatically and show to you when it is correct or not named. On our example our method is named "Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString" it is because "ademar.androidioscppexample" is our package, so we replace the "." by "_", CoreWrapper is the class where we are linking the native method and "concatenateMyStringWithCppString" is the method name itself.
As we have the method correctly declared it is time to analyze the arguments, the first parameter is a pointer of JNIEnv
it is the way we have access to JNI stuff, it is crucial to we make our conversions as you will see soon. The second is a jobject
it is the instance of the object you had used to call this method. You can think it as the java "this", on our example we do not need to use it, but we still need to declare it. After this jobject, we are going to receive the arguments of the method. Because our method has only one argument - a String "myString", we have only a "jstring" with the same name. Also notice that our return type is also a jstring. It is because our Java method returns a String, for more information about Java/JNI types please read it.
The final step is to convert the JNI types to the types we use on CPP side. On our example, we are transforming the jstring
to a const char *
sending it converted to the CPP, getting the result and converting back to jstring
. As all other steps on JNI, it is not hard; it is only boilerplated, all the work is done by the JNIEnv*
argument we receive when we call the GetStringUTFChars
and NewStringUTF
. After it our code is ready to run on Android devices, lets take a look.

- 15,854
- 5
- 53
- 88

- 14,215
- 14
- 85
- 114
-
11I don't get it - but +1 for one of the highest quality answers on SO – Michael Rodrigues Nov 18 '13 at 09:19
-
i am working with same senario and my code is in c/c++ files and .h files for definition. i try to create static library it build properly. when i try to add library in other project and just compile the project i got errors like : Undefined symbols for architecture armv7s: "_Net", referenced from: Netmodule::Module(Mem_t const*, void*) in lib.a(net_module.o) "_DestroyMem", referenced from: MemModule::DestroyModule(Mem_t const*) in lib.a(mem_module.o) thanks in advance – Anjan Jul 11 '14 at 13:39
-
@Anjan are you getting this error in android or iOS? I ask because the Android need be configured to Arm, x86 and Mips architectures. – ademar111190 Jul 11 '14 at 14:38
-
17@ademar111190 By far the most helpful post. This should not have been closed. – Jared Burrows Jul 19 '14 at 15:19
-
6
-
1@OmnipotentEntity, Thank you! I have +1'd everything and voted to re-open. I have actually tested/implemented this. Best tutorial so far. – Jared Burrows Jul 19 '14 at 15:44
-
1Thank you so much guys! some people do bad things on stackoverflow, but you are trying to correct it :) – ademar111190 Jul 19 '14 at 17:46
-
@KVISH I do not know, afaik swift works with objc, so probably it works with swift, but I never tried it and I not tried if it works with the new Apple bitcode. – ademar111190 Oct 16 '15 at 20:53
-
3@KVISH you have to implement the wrapper in Objective-C first, then you will access the Objective-C wrapper in swift by adding the wrapper header to your bridging header file. No way to directly access C++ in Swift as of now. For more information see http://stackoverflow.com/a/24042893/1853977 – Chris Mar 23 '16 at 12:49
-
4Btw, you are not forced to use Objective-C++ in the ViewController implementation, you can use standard Objective-C for the `ViewController.m` since you are importing a standard `#import "CoreWrapper.h"`, there is no c++ references in your ViewController implementation – dulaccc Jul 10 '16 at 09:18
-
is the c++ method name is supposed to be that long or are you really trying to make it horrible ? – Ahmed Adel Ismail Jul 28 '16 at 09:16
-
4@TereBentikh The JNI wrapper is to be so long because it uses the package+class+method, it is how JNI works I have no choice. On cpp I'm trying to make it explicitly once it is a how to and not a Beauty contest. – ademar111190 Jul 28 '16 at 13:28
-
1Added my own take based on this great answer. Uses modern Android Build System, CMake, and Swift on the iOS side. Check it out: https://github.com/sfunke/crossplattform-example/tree/master – Steffen Funke Apr 27 '17 at 10:51
-
ok, used the same c++ files for compilation it is possible, but what about this approach: Compile c++ files into a library (one file) and include this library into c++ of each platform? Is it reasonable? – walkmn Apr 05 '19 at 13:06
-
1@walkmn if this one file you are talking about is a binary it is not possible. You can have one unique binary per platform/architecture. e.g. (ios_x86 / ios_arm / linux_mips / etc...) – ademar111190 Apr 05 '19 at 16:07
-
1[Scapix Language Bridge](https://www.scapix.com/) does exactly what you describe completely automatically, generating wrapper code directly from C++ headers on the fly. – Boris Rasin Apr 06 '19 at 20:40
-
Great answer! Just remove the intro update section. It tarnishes the rest and brings no useful information. – Zingam Jul 30 '20 at 03:47
-
1We're now in 2020, and your quality answer is still valid. Thanks a lot @ademar111190 – Niko Aug 28 '20 at 13:40
-
It's confusing to talk about C++, when you're really using extern "C". https://stackoverflow.com/a/51360735/4959635 – mavavilj Dec 26 '22 at 11:12
Approach described in the excellent answer above can be completely automated by Scapix Language Bridge which generates wrapper code on the fly directly from C++ headers. Here is an example:
Define your class in 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);
};
And call it from Swift:
class ViewController: UIViewController {
func send(friend: Contact) {
let c = Contact()
contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
}
}
And from Java:
class View {
private contact = new Contact;
public void send(Contact friend) {
contact.sendMessage("Hello", friend);
contact.addTags({"a","b","c"});
contact.addFriends({friend});
}
}

- 448
- 4
- 12
-
-
1@Pixel Scapix generates Java bindings, which are very easy to use from Kotlin: https://kotlinlang.org/docs/java-interop.html – Boris Rasin Mar 30 '21 at 23:49