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,229