Cross Platform Mobile Development in C++
Kittinun Vantasin
Well, not for fainted heart!
Agenda
Why ?
What ?!
How ?!?
Who ?!?!
Why?
iOS & Android (2 major mobile OSes)
Building feature twice ..
Fixing bugs at least twice .....
Writing tests twice ...
Why? - OSes
Why? - Features
Why? - Features
Why? - Features
Why? - Writing tests
Why? - Fixing bugs
Now what, I knew it!
There are always choices in life ...
1. Suck it up and move on as there is no way to solve this
2. This must be put to an END!, we must at least DO SOMETHING!
What if I told you, there is a better way!
Introducing Xamarin
Then what?
Our requirements are ...
1. Use latest and greatest toolchains from both platforms (using Xcode for iOS, Android studio for Android)
2. Want our apps to be natively looking (using NavigationBar for iOS, Toolbar for Android etc....)
3. Embrace latest languages/patterns/ideas each OS has to offer (using Swift for iOS, Kotlin for Android)
Our approach is
1. Find common sane language, .... candidates are C++, C, Assembly
2. Build a core library that share among iOS/Android apps
3. Focus to automation, generate tedious stuff by using tools
Rough Idea
1. Design portion of code that needs to be shared e.g. networking, database
4. Build core code into (.a) - static library (iOS) and (.so) shared library (Android)
2. Write that code in C++
3. Expose header file so application can call into
5. Write UI code then call into C++ code
In the nutshell
App Layer (Swift/Objc, Java/Kotlin)
Bridging Layer (ObjC++, Java - JNI)
Core Layer (C++)
Language Bridges
Objective-C++ = ObjC & C++ marriage
Unofficial overlay on both languages
Lots of power + Lots of headaches
Language Bridges
@interface Greeting : NSObject
- (NSString *)greet:(NSString *)name;
@end
@implementation Greeting : NSObject
- (NSString *)greet:(NSString *)name {
std::string nameInStr = [name UTF8String];
hello::greeting g(nameInStr);
return [NSString stringWithUTF8String:g.greet().c_str()];
}
@end
Language Bridges
JNI = Java Native Interface
Links Java to C or C++
Not only for Android
Language Bridges
extern "C"
JNIEXPORT jstring JNICALL Java_com_github_kittinunf_greeting_greet(
JNIEnv * env, jobject thiz, jstring arg) {
jclass clazz = env->GetObjectClass(thiz);
jmethodID method = env->GetMethodID(clazz, "getGreetCpp", “()J");
jlong cpp_long = env->CallLongMethod(thiz, method, arg);
JniDemo * cpp_obj = reinterpret_cast<hello::greeting*>(cpp_long);
const char * arg_cstr = env->GetStringUTFChars(arg, nullptr);
std::string result = cpp_obj->greet(arg_cstr);
env->ReleaseStringUTFChars(arg, arg_cstr);
jstring jresult = env->NewStringUTF(result.c_str());
return jresult;
}
Demo!
So, you get the idea
If you like what you see ...
Live well young man. This presentation is not for you!
I just don't want to do this anymore >_<
Then what?
Introducing Djinni!
Pronounced "genie"
Generates bridging code from idl file
Support Objective-C++ & Java
https://github.com/dropbox/djinni/
How Djinni helps?
demo = interface +c, +j, +o {
foo(arg: string): string;
}
Bridging
ObjC++
Java-JNI
C++
Example
demo = interface +c +j +o {
foo1(name: string);
foo2(): list<f32>;
foo3(i: i16);
}
Djinni IDL
class demo {
public:
virtual ~demo() {}
virtual void foo1(const std::string & name) = 0;
virtual std::vector<float> foo2() = 0;
virtual void foo3(int16_t i) = 0;
};
C++
@protocol XYZDemo
- (void)foo1:(nonnull NSString *)name;
- (nonnull NSArray<NSNumber *> *)foo2;
- (void)foo3:(int16_t)i;
@end
ObjC++
import java.util.ArrayList;
public abstract class Demo {
public abstract void foo1(String name);
public abstract ArrayList<Float> foo2();
public abstract void foo3(short i);
}
Java
Djinni supports?
Djinni IDL | ObjC++ | Java |
---|---|---|
bool | BOOL | boolean |
i8, i16, i32, i64 | int<8|16|32|64>_t | byte/short/int/long |
string | NSString | String |
binary | NSData | byte[] |
date | NSDate | Date |
list<T> | NSArray | ArrayList |
map<K,V> | NSDictionary | HashMap<K,V> |
optional<T> | __nullable T | T |
interface | class | class |
record | class | class |
More tool ... GYP
Pronounced "gip" stands for generate your project
Translate gyp file into project file e.g. .pbxproj etc.
Support Xcode, Visual Studio, IntelliJ
https://gyp.gsrc.io/
Why GYP?
GYP generates proj
{
"targets":
[
{
"target_name":"lib<target_name>",
"type":"static_library",
"sources":[
<source_files>
],
"include_dirs":[
<include_files>
],
"dependencies":[
<dependencies, e.g. other gyp project>
],
"libraries":[
<other_libraries>
],
"defines":[
<defines>
],
"cflags_cc":[
<compiler_flags>
]
}
]
}
In the nutshell .. again
C++
ObjC++
Java-JNI
Djinni
Gyp
Swift
Kotlin
iOS App
Static Library (.a)
Android App
Shared Library (.so)
Core
Xcode
Android Studio
What else I need to know
C++ is not bad at all!
Cpp มันสวยงามมากเลยนะครับ
"
Bjarne Stroustrup
"
ไม่ได้กล่าวไว้
C++ Crash Course 1
Swift | C++ | |
---|---|---|
Variable declaration | let i = 5 var ii = 10 |
const auto i = 5 auto ii = 10 |
Loop | for i in arr { } | for (auto i : arr) { } |
Ownership | let f = Foo() | Foo f / auto f = make_shared<foo>() |
Ownership | weak var f: Foo | weak_ptr<foo> f = ... |
Block | let f = { print() } | auto f = []() { printf() } |
Optional | var f: Foo? | optional<foo> f |
Concurrent | dispatch_async | std::async() |
Initialization | let arr = [1,2,3] | vector<int> v{ 1,2,3 } |
TLLS; Lean C++ in 2 slides?
C++ Crash Course 2
Swift | C++ | |
---|---|---|
map | a.map { $0 * $0 } | transform(a.begin(), a.end(), a.begin(), [](int i) { return i * i } |
filter | a.filter { $0 % 2 == 0 } | remove_if(a.begin(), a.end(), [](int i) { return i % 2 == 0 } |
reduce | a.reduce(0) { ag, i in ag += i } | accumulate(a.begin(), a.end(), 0, plus<int>()) |
Call wrapper forwarding | -* | auto plus10 = bind(plus<int>(), _1, 10) plus10(50) |
* Unless doing it manually
Now, you are ready!
Let's talk about ARCHITECTURE
MVVM - FTW
C++
ObjC++
Java-JNI
Djinni
Swift
Kotlin
VM
VM
VIEW
VIEW
MODEL
How ? Let's apply it!
Let's build Flickr client App
Explore
Able to explore pictures
Search
Able to search pictures by keyword
Map
Able to see where pictures
have been taken with geo tag
How ?
Components could be shared (C++)
Networking : libcurl (curl/curl)
Json : json11 (dropbox/json11)
Database : lmdb (LMDB/lmdb)
Components shouldn't be shared (Swift)
Image : Kingfisher (onevcat/Kingfisher)
Reactive Programming : RxSwift (ReactiveX/RxSwift)
How ?
imageUrl: String
title: String
id: String
explore_detail_view_data = record {
id: string;
image_url: string;
title: string;
} deriving (eq)
How ?
[ExploreDetailViewData]
explore_view_data = record {
error: bool;
message: string;
explores: list<explore_detail_view_data>;
}
How data is flow ?
One class to represent logic of your page (Controller)
One class(conforms objc protocol) to receive your data (Observer)
One class to represent your view (ViewController)
Djinni declaration
Base class (C++)
Base protocol (ObjC++)
Concrete class (C++)
Class (Swift)
Wrapper(ObjC++)
View Controller
(Swift)
owns
owns
generates
generates
generates
How data is flow ?
VM
VIEW
own
Update reactively
With help of RxSwift
More details
class explore_controller_observer;
class explore_controller {
public:
virtual ~explore_controller() {}
static std::shared_ptr<explore_controller> create();
virtual void subscribe(const std::shared_ptr
<explore_controller_observer> & observer) = 0;
virtual void unsubscribe() = 0;
virtual void reset() = 0;
virtual void request(int8_t page) = 0;
};
explore_controller_observer = interface +o +j {
on_begin_update();
on_update(view_data: explore_view_data);
on_end_update();
}
explore_controller = interface +c {
static create(): explore_controller;
subscribe(observer: explore_controller_observer);
unsubscribe();
reset();
request(page: i8);
}
@protocol CPExploreControllerObserver
- (void)onBeginUpdate;
- (void)onUpdate:(nonnull CPExploreViewData *)viewData;
- (void)onEndUpdate;
@end
public abstract class ExploreControllerObserver {
public abstract void onBeginUpdate();
public abstract void onUpdate(ExploreViewData viewData);
public abstract void onEndUpdate();
}
Demo
Who ?
Mobile C++ Slack Channel
What we've learned?
C++ can be a practical way of mobile development
Shared codebase is possible with no compromisation
Good organization/architecture of code is vital
The success of this strategy depends on good understanding of both platforms
Alternatives
Drawbacks
C++ is a beasts, so powerful but unforgiving
Logging on Android is not smooth, debugging onto C++ code on Android is horrendous
Communication between teams is key
Revamp repo/branch strategy is needed
Things we've shared
kittinunf/Cookpit
taskworld/flowcpp
Q&A
Cross Platform Mobile Development in C++
By Kittinun Vantasin
Cross Platform Mobile Development in C++
iOS Dev TH #5
- 2,333