Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a07a3e6
chore: revamp
YogendraShelke Apr 2, 2026
ce52a85
test: add new tests
YogendraShelke Apr 16, 2026
083cfeb
chore: update RN to v084
vadymv-mendix May 8, 2026
5b6ca03
refactor: streamline type definitions and remove unused code
YogendraShelke May 12, 2026
2b582de
chore: update hermes-engine and React-Core-prebuilt checksums in Podf…
YogendraShelke May 12, 2026
8eab392
feat: add RN_HARNESS configuration for app registry and view flattening
YogendraShelke May 12, 2026
4886110
chore: replace deprecated apis with modern
YogendraShelke May 13, 2026
18a417f
feat: integrate ReactAppDependencyProvider and refactor ReactNativeDe…
YogendraShelke May 13, 2026
c1b1b8a
fix: handle file deletion on download failure and improve error handling
YogendraShelke May 13, 2026
1ec9b64
feat: enhance development experience with improved debug settings and…
YogendraShelke May 13, 2026
3a7b84b
fix: downgrade react version from 19.2.4 to 19.2.3 in package.json an…
YogendraShelke May 13, 2026
568be58
fix: update emulator configurations for Android and iOS in rn-harness…
YogendraShelke May 13, 2026
20d4ece
fix: downgrade React version to 19.2.3 and update React Native Harnes…
YogendraShelke May 13, 2026
73951b4
refactor: clean up code by removing unnecessary comments and improvin…
YogendraShelke May 14, 2026
15919a8
refactor: update AppDelegate to use bundleURL method and clean up Rea…
YogendraShelke May 14, 2026
621c85f
refactor: remove outdated DevSettings tests
YogendraShelke May 14, 2026
ff3d757
refactor: revert unnecessary changes
YogendraShelke May 14, 2026
3697e3d
refactor: fix indentation in getDevInternalSettings function
YogendraShelke May 14, 2026
e2f48cc
refactor: remove outdated code
YogendraShelke May 14, 2026
7335246
refactor: update DevHelper and ReactHostHelper for improved module ac…
YogendraShelke May 14, 2026
9a8114c
refactor: remove unused imports and clean up touch event handling in …
YogendraShelke May 14, 2026
e4a0d8d
refactor: improve conditional checks for showing the development menu
YogendraShelke May 14, 2026
f548a96
refactor: rename MxSplashScreen to MendixSplashScreen for consistency
YogendraShelke May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/CI_DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ All workflows use standardized tooling versions to ensure consistency:
| ---------------- | --------- | ----------------------------- |
| **Node.js** | `24` | `.nvmrc` |
| **Yarn** | `4.12.0` | `package.json#packageManager` |
| **React Native** | `0.83.4` | `package.json` |
| **React** | `19.2.14` | `package.json` |
| **React Native** | `0.84.1` | `package.json` |
| **React** | `19.2.3` | `package.json` |
| **TypeScript** | `5.9.3` | `package.json` |

### Android Tooling
Expand Down Expand Up @@ -55,7 +55,7 @@ All workflows use standardized tooling versions to ensure consistency:

| Tool | Version | Defined In |
| ------------------------ | --------------------- | ----------------------- |
| **React Native Harness** | `1.0.0-alpha.21` | `package.json` |
| **React Native Harness** | `1.1.0` | `package.json` |
| **Android Emulator** | Pixel_API_35 (API 35) | `rn-harness.config.mjs` |
| **iOS Simulator** | iPhone 17 (iOS 26.2) | `rn-harness.config.mjs` |

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@ lib/

# Fastlane.swift runner binary
**/fastlane/FastlaneRunner

.claude/
5 changes: 3 additions & 2 deletions MendixNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/mendix/mendix-native.git", :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
s.public_header_files = "ios/**/*.h"
s.private_header_files = "ios/**/*.h"
s.public_header_files = "ios/Modules/Helper/ReactHostHelper.h"
s.private_header_files = "ios/TurboModules/**/*.h"

s.dependency "SSZipArchive"
s.dependency "RNCAsyncStorage"
s.dependency "ReactAppDependencyProvider"

install_modules_dependencies(s)
end
4 changes: 1 addition & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ android {
}

buildFeatures {
dataBinding true
viewBinding true
buildConfig true
}

Expand Down Expand Up @@ -88,7 +86,7 @@ dependencies {
kapt 'com.github.bumptech.glide:compiler:4.12.0'


api "com.facebook.react:react-android:0.83.4"
api "com.facebook.react:react-android:0.84.1"
api project(':op-engineering_op-sqlite')
api project(':react-native-async-storage_async-storage')
api project(':react-native-gesture-handler')
Expand Down

This file was deleted.

This file was deleted.

56 changes: 5 additions & 51 deletions android/src/main/java/com/mendix/mendixnative/MendixInitializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package com.mendix.mendixnative
import android.app.Activity
import android.view.MotionEvent
import com.facebook.react.ReactHost
import com.facebook.react.ReactInstanceEventListener
import com.facebook.react.ReactNativeHost
import com.facebook.react.bridge.ReactContext
import com.facebook.react.common.ShakeDetector
import com.facebook.react.devsupport.DevSupportManagerBase
import com.facebook.react.devsupport.attachMendixSupportManagerShakeDetector
Expand All @@ -20,25 +17,16 @@ import com.mendix.mendixnative.react.MxConfiguration
import com.mendix.mendixnative.react.clearCachedReactNativeDevBundle
import com.mendix.mendixnative.react.clearData
import com.mendix.mendixnative.react.closeSqlDatabaseConnection
import com.mendix.mendixnative.react.toggleElementInspector
import com.mendixnative.MendixNativeModule

class MendixInitializer(
private val context: Activity,
private val reactHost: ReactHost,
private val reactNativeHost: ReactNativeHost,
private val hasRNDeveloperSupport: Boolean = false,
) : ReactInstanceEventListener {
) {
private var shakeDetector: ShakeDetector? = null
private var devMenuTouchEventHandler: DevMenuTouchEventHandler? = null

fun onCreate(
mendixApp: MendixApp,
devAppMenuHandler: DevAppMenuHandler = object : DevAppMenuHandler {
override fun showDevAppMenu() {}
},
clearData: Boolean,
) {
fun onCreate(mendixApp: MendixApp, clearData: Boolean) {
// Assign mendix xas id interceptor to okhttp
CookieEncryption.init(this.context)
if (CookieEncryption.isCookieEncryptionEnabled()) {
Expand All @@ -54,31 +42,9 @@ class MendixInitializer(
MxConfiguration.warningsFilter = mendixApp.warningsFilter

// This is here to make sure that a clean host instance is initialised.
restartReactInstanceManager()
reactHost.invalidate()
if (clearData) clearData(context.application)
if (hasRNDeveloperSupport) setupDeveloperApp(runtimeUrl, mendixApp)
if (mendixApp.attachCustomDeveloperMenu) attachCustomDeveloperMenu(devAppMenuHandler)
}

private fun restartReactInstanceManager() {
if (reactNativeHost.hasInstance()) reactNativeHost.clear()
// Pre-initialize reactInstanceManager to be available for other methods
if (reactNativeHost.hasInstance()) reactNativeHost.reactInstanceManager
}

private fun attachCustomDeveloperMenu(devAppMenuHandler: DevAppMenuHandler) {
devMenuTouchEventHandler =
DevMenuTouchEventHandler(object : DevMenuTouchEventHandler.DevMenuTouchListener {
override fun onTap() {
reactNativeHost.typeSafeNativeModule<MendixNativeModule>()?.reloadClientWithState()
}

override fun onLongPress() {
devAppMenuHandler.showDevAppMenu()
}
})

attachShakeDetector(devAppMenuHandler)
}

fun onDestroy() {
Expand All @@ -87,29 +53,18 @@ class MendixInitializer(

if (hasRNDeveloperSupport) {
AppPreferences(context.applicationContext).setElementInspector(false)
reactHost.removeReactInstanceEventListener(this)
}

// We need to clear the host to allow for reinitialization of the Native Modules
// Especially for when switching between apps
reactNativeHost.clear()
reactHost.invalidate()

// We need to close all databases separately to avoid hitting a read only state exception
// Databases need to close after we are done closing the react native host to avoid db locks
closeSqlDatabaseConnection(reactNativeHost.reactApplicationContext())
closeSqlDatabaseConnection(reactHost.currentReactContext)
}

fun stopShakeDetector() {
shakeDetector?.stop()
}

override fun onReactContextInitialized(context: ReactContext) {
val preferences = AppPreferences(context)
if (preferences.isElementInspectorEnabled) {
toggleElementInspector(context)
}
}

fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
return devMenuTouchEventHandler?.handle(ev) ?: false
}
Expand Down Expand Up @@ -137,7 +92,6 @@ class MendixInitializer(
preferences.setDevMode((mendixApp.showExtendedDevMenu))

clearCachedReactNativeDevBundle(context.application)
reactHost.addReactInstanceEventListener(this)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScree
?: throw ClassCastException("Application needs to implement MendixApplication")

mendixInitializer =
MendixInitializer(this, reactHost, reactNativeHost, mendixApplication.useDeveloperSupport)
mendixInitializer.onCreate(mendixApp!!, this, intent.getBooleanExtra(CLEAR_DATA, false))
MendixInitializer(this, reactHost, mendixApplication.useDeveloperSupport)
mendixInitializer.onCreate(mendixApp!!, intent.getBooleanExtra(CLEAR_DATA, false))

super.onCreate(null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {
val hasRNDeveloperSupport = requireArguments().getBoolean(ARG_USE_DEVELOPER_SUPPORT, false)

mendixInitializer =
MendixInitializer(requireActivity(), reactHost!!, reactNativeHost, hasRNDeveloperSupport).also {
it.onCreate(mendixApp!!, this, clearData)
MendixInitializer(requireActivity(), reactHost!!, hasRNDeveloperSupport).also {
it.onCreate(mendixApp!!, clearData)
}

super.onCreate(savedInstanceState)
Expand Down
71 changes: 52 additions & 19 deletions android/src/main/java/com/mendix/mendixnative/react/ClearData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.content.Context
import android.util.Log
import android.webkit.CookieManager
import android.widget.Toast
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactHost
import com.facebook.react.bridge.ReactContext
import com.facebook.react.modules.network.NetworkingModule
import com.mendix.mendixnative.encryption.MendixEncryptedStorage
Expand Down Expand Up @@ -34,9 +34,9 @@ fun clearData(applicationContext: Application) = clearCookies().also {
fileBackend.deleteDirectory(applicationContext.filesDir)
}

fun clearDataWithReactContext(applicationContext: Application, reactNativeHost: ReactNativeHost, cb: (success: Boolean) -> Unit) {
fun clearDataWithReactContext(applicationContext: Application, reactHost: ReactHost, cb: (success: Boolean) -> Unit) {
clearCachedReactNativeDevBundle(applicationContext)
val reactContext = reactNativeHost.reactContext()
val reactContext = reactHost.currentReactContext
val fileBackend = FileBackend(applicationContext)
fileBackend.deleteDirectory(applicationContext.filesDir)
val errorString = "Clearing %s failed. Please clear your data from the launch screen."
Expand All @@ -57,7 +57,7 @@ fun clearDataWithReactContext(applicationContext: Application, reactNativeHost:
reportError("database")
}

if (!clearAsyncStorage(reactNativeHost)) {
if (!clearAsyncStorage(reactHost)) {
reportError("async storage")
}

Expand Down Expand Up @@ -88,30 +88,63 @@ fun clearDataWithReactContext(applicationContext: Application, reactNativeHost:
}

fun deleteAppDatabaseAsync(reactContext: ReactContext?, cb: BooleanCallback) {
reactContext?.typeSafeNativeModule<OPSQLiteModule>()?.let {
it.deleteAllDBs()
cb(true)
} ?: cb(false)
val opSQLiteModule = reactContext?.nativeModule<OPSQLiteModule>(OPSQLiteModule.NAME)
if (opSQLiteModule != null) {
try {
opSQLiteModule.deleteAllDBs()
cb(true)
} catch (e: Exception) {
Log.e("ClearData", "Failed to delete databases: ${e.message}")
cb(false)
}
} else {
cb(false)
}
}

fun clearAsyncStorage(reactNativeHost: ReactNativeHost): Boolean {
val module = reactNativeHost.typeSafeNativeModule<AsyncStorageModule>()
if (module != null) {
return true
} else {
return false
/**
* Clears all AsyncStorage data.
*
* Note: Previous implementation only checked module availability without clearing.
* This now actually clears the storage using AsyncStorageModule.clear().
*/
fun clearAsyncStorage(reactHost: ReactHost): Boolean {
val asyncStorageModule = reactHost.nativeModule<AsyncStorageModule>(AsyncStorageModule.NAME)
if (asyncStorageModule != null) {
try {
// Clear AsyncStorage synchronously - clear() expects a callback but we're using fire-and-forget
asyncStorageModule.clear { error ->
if (error != null) {
Log.e("ClearData", "AsyncStorage clear error: $error")
}
}
return true
} catch (e: Exception) {
Log.e("ClearData", "Failed to clear AsyncStorage: ${e.message}")
return false
}
}
return false
}

fun clearSecureStorage(context: Context?): Boolean =
context?.let { MendixEncryptedStorage.getMendixEncryptedStorage(it).clear() } ?: false

fun clearCookiesAsync(reactContext: ReactContext?, cb: (success: Boolean) -> Unit) =
reactContext?.typeSafeNativeModule<NetworkingModule>()?.let { module ->
module.clearCookies {
cb(it[0] as Boolean)
fun clearCookiesAsync(reactContext: ReactContext?, cb: (success: Boolean) -> Unit) {
val networkingModule = reactContext?.nativeModule<NetworkingModule>(NetworkingModule.NAME)
if (networkingModule != null) {
try {
networkingModule.clearCookies { result ->
cb(result[0] as Boolean)
}
} catch (e: Exception) {
Log.e("ClearData", "Failed to clear cookies: ${e.message}")
cb(false)
}
} ?: cb(false)
} else {
cb(false)
}
}

fun clearCachedReactNativeDevBundle(applicationContext: Application) {
try {
Expand Down
15 changes: 14 additions & 1 deletion android/src/main/java/com/mendix/mendixnative/react/CloseApp.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
package com.mendix.mendixnative.react

import android.util.Log
import com.facebook.react.bridge.ReactContext
import com.op.sqlite.OPSQLiteModule

/**
* Closes all SQLite database connections.
*
* This is called during app shutdown to gracefully close database connections.
*/
fun closeSqlDatabaseConnection(reactContext: ReactContext?) {
reactContext?.typeSafeNativeModule<OPSQLiteModule>()?.closeAllConnections()
val opSQLiteModule = reactContext?.nativeModule<OPSQLiteModule>(OPSQLiteModule.NAME)
if (opSQLiteModule != null) {
try {
opSQLiteModule.closeAllConnections()
} catch (e: Exception) {
Log.e("CloseApp", "Failed to close database connections: ${e.message}")
}
}
}
Loading
Loading