├── .gitignore
├── LICENSE
├── README.md
├── SwapiAndroidApp
├── .gitignore
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── jniLibs
│ │ ├── arm64
│ │ │ └── libswapi.so
│ │ ├── armeabi
│ │ │ └── libswapi.so
│ │ └── x86
│ │ │ └── libswapi.so
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── rust
│ │ │ └── app
│ │ │ └── swapiclient
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── rust
│ │ │ │ └── app
│ │ │ │ └── swapiclient
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── adapter
│ │ │ │ └── PeopleAdapter.kt
│ │ │ │ ├── nativeswapi
│ │ │ │ └── NativeSwapiClient.kt
│ │ │ │ └── swapi
│ │ │ │ ├── InternalPointerMarker.java
│ │ │ │ ├── JNIReachabilityFence.java
│ │ │ │ ├── Logger.kt
│ │ │ │ ├── People.java
│ │ │ │ ├── SwapiClient.java
│ │ │ │ └── SwapiPeopleLoadedListener.java
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ │ ├── layout
│ │ │ ├── activity_main.xml
│ │ │ └── item_people.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── rust
│ │ └── app
│ │ └── swapiclient
│ │ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── SwapiIOSApp
├── SwapiClientIOS.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── igor.steblii.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── igor.steblii.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── SwapiClientIOS
│ ├── .gitignore
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── Swapi-Bridging-Header.h
│ ├── model
│ ├── People.swift
│ └── PeopleResponse.swift
│ ├── swapi
│ ├── NativeSwapiClient.swift
│ └── SwapiLoader.swift
│ ├── swapi_core.h
│ ├── view
│ └── PeopleRow.swift
│ └── vm
│ └── PeopleViewModel.swift
├── build_release
├── clean
├── content
├── bridge_arch.png
├── swapi_adr.png
└── swapi_ios.png
├── swapi_adr
├── .gitignore
├── Cargo.toml
├── Readme.md
├── build.rs
├── scripts
│ ├── create_s_link
│ └── release_build
└── src
│ ├── android_logger.rs
│ ├── lib.rs
│ ├── main.rs
│ └── swig
│ ├── java_glue.rs
│ ├── java_glue.rs.in
│ └── mod.rs
├── swapi_core
├── .gitignore
├── Cargo.toml
├── Readme.md
├── build.rs
└── src
│ ├── lib.rs
│ ├── main.rs
│ ├── swapi.rs
│ └── swapi_core.h
└── swapi_ios
├── .gitignore
├── Cargo.toml
├── Readme.md
├── build.rs
├── scripts
└── release_build
└── src
├── ffi
├── callback.rs
├── ios_ffi.rs
├── mod.rs
├── native_model.rs
└── swapi_native.h
├── lib.rs
└── main.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/*
2 | # Local History for Visual Studio Code
3 | .history/
4 | *.project
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Igor Steblii
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # swapi-rust-mobile
2 | Crossplatform mobile solution based on Rust for IOS & Android.
3 |
4 | ***More details here:["Rust & cross-platform mobile development"](https://medium.com/@igor.stebliy/rust-cross-platform-mobile-development-9117a67ac9b7)***
5 |
6 | In this project you'll find Rust network client, that laods and serialize data from
7 | [star wars API](https://swapi.dev/people) and pass it to Android and IOS.
8 |
9 | Android | IOS
10 | :-------------------------:|:-------------------------:
11 |  | 
12 |
13 | # Architecture
14 |
15 | Project consist of sub-project:
16 | * Android applicaition
17 | * IOS application
18 | * Rust core module
19 | * Rust Android bridge
20 | * Rust IOS bridge
21 |
22 | Architecture |
23 | :-------------------------:|
24 |
|
25 |
26 | ## swapi_core
27 | module wtih shared logic between IOS& Android.
28 |
29 | More details: [Readme](/swapi_core/Readme.md)
30 |
31 | ## swapi_adr
32 | Android specific module, that depends on [swapi_core] for networking and provide custom build for JNI communicaiton.
33 |
34 | More details: [Readme](/swapi_adr/Readme.md)
35 |
36 | ## swapi_ios
37 | IOS specific module, that depends on [swapi_code] for networking and provide custom build for FFI communicaiton.
38 |
39 | More details: [Readme](/swapi_ios/Readme.md)
40 |
41 | # Build & Run
42 |
43 | ## Enviroment project
44 | To setup enviroment follow instuctions in :
45 | * swapi_adr/Readme.
46 | * swapi_ios/Readme.
47 | * swapi_core/Readme.
48 |
49 | ### Official guides
50 |
51 | * [Android](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html)
52 | * [IOS](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html)
53 |
54 | ## Build:
55 |
56 | Each project contain `scripts` folder to automate build.
57 |
58 | ### IOS:
59 | * ./swapi_ios/scripts/build_release
60 |
61 | ### Andorid:
62 | * ./swapi_adr/scripts/build_release
63 | * ./swapi_adr/scripts/create_s_link - create symb link between generated files .so and Android project `jniLibs`
64 |
65 | ## Build:
66 | * ./build_release (execute all 3 commands listed on top)
67 | * ./clean - clean all 3 Rust project
68 |
69 | # IDE
70 |
71 | Based on my experience
72 |
73 | ## Rust has very good support with:
74 | * InteliJ IDE CE
75 | * [Rust Plugin](https://intellij-rust.github.io/)
76 |
77 | ## Alternatvie:
78 | * VSCode
79 | * [Rust plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust)
80 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
87 | .gradle
88 | /local.properties
89 | .idea
90 | .DS_Store
91 | /build
92 | /captures
93 | .cxx
94 |
95 | #VSCode
96 | .vscode/*
97 | !.vscode/settings.json
98 | !.vscode/tasks.json
99 | !.vscode/launch.json
100 | !.vscode/extensions.json
101 | *.code-workspace
102 |
103 | # Local History for Visual Studio Code
104 | .history/
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlinx-serialization'
5 |
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.3"
9 |
10 | defaultConfig {
11 | applicationId "com.cvs.app.swapiclient"
12 | minSdkVersion 15
13 | targetSdkVersion 29
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 |
19 | sourceSets {
20 | main {
21 | jniLibs.srcDirs = ["jniLibs"]
22 | }
23 | }
24 |
25 | ndk.abiFilters 'armeabi','arm64','x86'
26 | }
27 |
28 | buildTypes {
29 | release {
30 | minifyEnabled false
31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
32 | }
33 | }
34 |
35 | }
36 |
37 | dependencies {
38 | implementation fileTree(dir: 'libs', include: ['*.jar'])
39 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
40 | implementation 'androidx.appcompat:appcompat:1.1.0'
41 | implementation 'androidx.core:core-ktx:1.2.0'
42 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
43 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
44 |
45 | //coroutines
46 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4'
47 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
48 | //serialization
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // or "kotlin-stdlib-jdk8"
50 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" // JVM dependency
51 |
52 | testImplementation 'junit:junit:4.12'
53 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
54 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
55 | }
56 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/jniLibs/arm64/libswapi.so:
--------------------------------------------------------------------------------
1 | /Users/igor.steblii/Project/Rust/swapi/swapi-rust-mobile/swapi_adr/target/aarch64-linux-android/release/libswapi.so
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/jniLibs/armeabi/libswapi.so:
--------------------------------------------------------------------------------
1 | /Users/igor.steblii/Project/Rust/swapi/swapi-rust-mobile/swapi_adr/target/armv7-linux-androideabi/release/libswapi.so
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/jniLibs/x86/libswapi.so:
--------------------------------------------------------------------------------
1 | /Users/igor.steblii/Project/Rust/swapi/swapi-rust-mobile/swapi_adr/target/i686-linux-android/release/libswapi.so
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/src/androidTest/java/com/rust/app/swapiclient/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rust.app.swapiclient
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.cvs.app.swapiclient", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/SwapiAndroidApp/app/src/main/java/com/rust/app/swapiclient/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rust.app.swapiclient
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.widget.Button
7 | import android.widget.Toast
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.recyclerview.widget.RecyclerView
10 | import com.rust.app.swapiclient.adapter.PeopleAdapter
11 | import com.rust.app.swapiclient.nativeswapi.NativeSwapiClient
12 | import com.rust.app.swapiclient.swapi.Logger
13 | import com.rust.app.swapiclient.swapi.People
14 | import com.rust.app.swapiclient.swapi.SwapiClient
15 | import com.rust.app.swapiclient.swapi.SwapiPeopleLoadedListener
16 |
17 | class MainActivity : AppCompatActivity() {
18 |
19 | companion object {
20 | const val TAG = "MainActivity"
21 |
22 | init {
23 | System.loadLibrary("swapi")
24 | }
25 | }
26 |
27 | private lateinit var recyclerView: RecyclerView
28 | private lateinit var viewAdapter: PeopleAdapter
29 | private lateinit var viewManager: RecyclerView.LayoutManager
30 |
31 | private val client by lazy(LazyThreadSafetyMode.NONE) { SwapiClient() }
32 |
33 | private val nativeClient by lazy(LazyThreadSafetyMode.NONE) { NativeSwapiClient() }
34 |
35 | override fun onCreate(savedInstanceState: Bundle?) {
36 | super.onCreate(savedInstanceState)
37 | setContentView(R.layout.activity_main)
38 | Logger.initLogger()
39 | setupRecycler()
40 | findViewById