Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
4 changes: 3 additions & 1 deletion .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ jobs:
# Run tests
- name: Run Harness E2E tests
working-directory: example
run: yarn harness:ios
run: |
export NODE_OPTIONS="--max-old-space-size=6144"
yarn harness:ios

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.mendix.mendixnative.react.splash.MendixSplashScreenPresenter;

import java.util.List;
import java.util.Map;

public interface MendixApplication extends ReactApplication {
boolean getUseDeveloperSupport();
Expand Down
22 changes: 20 additions & 2 deletions android/src/main/java/com/mendix/mendixnative/MendixInitializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.mendix.mendixnative

import android.app.Activity
import com.facebook.react.ReactHost
import com.facebook.react.devsupport.DevSupportManagerBase
import com.facebook.react.modules.network.OkHttpClientProvider
import com.mendix.mendixnative.config.AppPreferences
import com.mendix.mendixnative.react.*
Expand Down Expand Up @@ -34,12 +33,23 @@ class MendixInitializer(
MxConfiguration.runtimeUrl = runtimeUrl
MxConfiguration.warningsFilter = mendixApp.warningsFilter

// Must run before the first access to `reactHost` below (and before any reload).
// `PackagerConnectionSettings.debugServerHost` caches its value in memory for the
// lifetime of the process the first time it's read (e.g. by an eager dev-support
// settings reload triggered as soon as ReactHost/DevSupportManager is constructed).
// If that first read happens before we persist the real Metro host, RN falls back
// to its hardcoded emulator default (10.0.2.2:8081) and keeps using it for the rest
// of the process — even though the correct host is written to SharedPreferences —
// which is why a fresh app process (restart) "fixes" it but reload within the same
// process does not. Resolving/pushing the host here, before `reactHost` is touched,
// avoids that stale cache entirely.
if (hasRNDeveloperSupport) setupDeveloperApp(runtimeUrl, mendixApp)

// Reload only if there's already a running instance.
if (reactHost.currentReactContext != null) {
reactHost.reload("Clean start for new Mendix app")
}
if (clearData) clearData(context.application)
if (hasRNDeveloperSupport) setupDeveloperApp(runtimeUrl, mendixApp)
}

fun onDestroy() {
Expand All @@ -65,6 +75,14 @@ class MendixInitializer(
preferences.setDeltas(false)
preferences.setDevMode((mendixApp.showExtendedDevMenu))

// Explicitly push the freshly resolved host into the live ReactHost's
// PackagerConnectionSettings. Writing to SharedPreferences alone (above) isn't
// enough if RN already cached a stale/default debug server host in memory for
// this process — this overwrite takes effect immediately, regardless of when
// that first (possibly premature) read happened.
reactHost.devSupportManager?.devSettings?.packagerConnectionSettings?.debugServerHost =
preferences.getMetroBundlerHost()

clearCachedReactNativeDevBundle(context.application)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.mendix.mendixnative

import android.app.Application
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JSBundleLoader
import com.facebook.react.bridge.JSBundleLoaderDelegate
Expand All @@ -28,8 +27,6 @@ import com.mendixnative.MendixNativePackage
import java.util.*
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load

import com.facebook.react.defaults.DefaultReactNativeHost

abstract class MendixReactApplication : Application(), MendixApplication, ErrorHandlerFactory {
private val appSessionId = "" + Math.random() * 1000 + Date().time
override fun getAppSessionId(): String = appSessionId
Expand All @@ -44,33 +41,11 @@ abstract class MendixReactApplication : Application(), MendixApplication, ErrorH

private var jsBundleFileProvider: JSBundleFileProvider? = jsBundleProvider

override var reactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) {
override fun getUseDeveloperSupport(): Boolean = this@MendixReactApplication.useDeveloperSupport

override fun getPackages(): List<ReactPackage> {
val pkgs: MutableList<ReactPackage> = ArrayList()
// Use the packages provided by the concrete Application subclass.
pkgs.addAll(this@MendixReactApplication.packages)
// Inject splashScreenPresenter into any MendixNativePackage instances without creating duplicates.
applyInternalPackageAugmentations(pkgs)
return pkgs
}

override fun getJSBundleFile(): String? = this@MendixReactApplication.jsBundleFile
override fun getJSMainModuleName(): String = "index"
override fun getBundleAssetName(): String? = super.getBundleAssetName()
override fun getRedBoxHandler(): RedBoxHandler? = null

// Hermes & New Arch flags; Hermes executor will be picked automatically when isHermesEnabled is true.
override val isNewArchEnabled: Boolean = true
override val isHermesEnabled: Boolean = true
}

/**
* Build the [ReactHost] ourselves instead of using [DefaultReactHost.getDefaultReactHost],
* because that factory evaluates [ReactNativeHost.getJSBundleFile] once at creation time and
* bakes the result into a fixed [JSBundleLoader]. After an OTA update deploys a new bundle,
* a subsequent [ReactHost.reload] would still load the stale bundle.
* Build the [ReactHost] with a custom [JSBundleLoader] instead of using a static bundle path.
* The default approach evaluates the bundle file path once at creation time and bakes it into
* a fixed [JSBundleLoader]. After an OTA update deploys a new bundle, a subsequent
* [ReactHost.reload] would still load the stale bundle.
*
* By providing a **dynamic** [JSBundleLoader] whose [JSBundleLoader.loadScript] calls
* [getJSBundleFile] on every invocation, each reload picks up the latest bundle path —
Expand Down Expand Up @@ -133,10 +108,7 @@ abstract class MendixReactApplication : Application(), MendixApplication, ErrorH
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
// Only load the New Architecture entry point when enabled (always true here, but guarded for safety).
if (reactNativeHost is DefaultReactNativeHost) {
load()
}
load()
}

override fun getJSBundleFile(): String? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.mendix.mendixnative.activity

import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.bridge.ReactContext
import com.facebook.react.devsupport.interfaces.DevSupportManager
import com.mendix.mendixnative.DevAppMenuHandler
import com.mendix.mendixnative.MendixApplication
import com.mendix.mendixnative.MendixInitializer
import com.mendix.mendixnative.react.MendixApp
import com.mendix.mendixnative.react.splash.MendixSplashScreenPresenter
import com.mendix.mendixnative.util.MendixBackwardsCompatUtility
import java.io.Serializable

open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScreenHandler {

Expand All @@ -24,7 +24,7 @@ open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScree

override fun onCreate(savedInstanceState: Bundle?) {
mendixApp = mendixApp
?: intent.getSerializableExtra(MENDIX_APP_INTENT_KEY) as? MendixApp
?: getSerializableData(MENDIX_APP_INTENT_KEY, MendixApp::class.java)
?: throw IllegalStateException("MendixApp configuration can't be null")
val mendixApplication = application as? MendixApplication
?: throw ClassCastException("Application needs to implement MendixApplication")
Expand All @@ -36,6 +36,19 @@ open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScree
super.onCreate(null)
}

inline fun <reified T : Serializable> getSerializableData(key: String?, clazz: Class<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getSerializableExtra<T?>(key, clazz)
} else {
@Suppress("DEPRECATION")
val data = intent.getSerializableExtra(key)
if (data is T) {
return data
}
return null
}
}

override fun onDestroy() {
mendixInitializer.onDestroy()
super.onDestroy()
Expand All @@ -46,12 +59,9 @@ open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScree
}

override fun showDevAppMenu() {
currentDevSupportManager?.showDevOptionsDialog();
currentDevSupportManager?.showDevOptionsDialog()
}

private val currentReactContext: ReactContext?
get() = if (reactNativeHost.hasInstance()) reactInstanceManager.currentReactContext else null

val currentDevSupportManager: DevSupportManager?
get() = reactHost.devSupportManager

Expand All @@ -70,9 +80,7 @@ open class MendixReactActivity : ReactActivity(), DevAppMenuHandler, LaunchScree
}

override fun showLaunchScreen() {
if (!MendixBackwardsCompatUtility.getInstance().unsupportedFeatures.hideSplashScreenInClient && splashScreenPresenter != null) {
splashScreenPresenter?.show(this)
}
splashScreenPresenter?.show(this)
Comment thread
vadymv-mendix marked this conversation as resolved.
}

override fun hideLaunchScreen() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.mendix.mendixnative.config.AppUrl
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit

Expand All @@ -22,10 +23,7 @@ fun getRuntimeInfo(runtimeUrl: String, cb: (info: RuntimeInfoResponse) -> Unit)
client.newCall(
Request.Builder()
.post(
RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
"{\"action\":\"info\"}"
)
"{\"action\":\"info\"}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
)
.url(AppUrl.removeTrailingSlash(AppUrl.ensureProtocol(runtimeUrl)) + "/xas/")
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mendix.mendixnative.fragment

import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import com.facebook.react.devsupport.interfaces.DevSupportManager
Expand All @@ -9,6 +10,7 @@ import com.mendix.mendixnative.MendixInitializer
import com.mendix.mendixnative.activity.LaunchScreenHandler
import com.mendix.mendixnative.react.MendixApp
import com.mendix.mendixnative.util.MendixDoubleTapRecognizer
import java.io.Serializable

/**
* Class used for Sample apps
Expand Down Expand Up @@ -54,7 +56,7 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {
}

if (mendixApp == null) {
mendixApp = requireArguments().getSerializable(ARG_MENDIX_APP) as MendixApp?
mendixApp = getSerializableData(ARG_MENDIX_APP, MendixApp::class.java)
?: throw IllegalArgumentException("Mendix app is required")
}

Expand All @@ -73,9 +75,22 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {
super.onCreate(savedInstanceState)
}

inline fun <reified T : Serializable> getSerializableData(key: String?, clazz: Class<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getSerializable<T?>(key, clazz)
} else {
@Suppress("DEPRECATION")
val data = requireArguments().getSerializable(key)
if (data is T) {
return data
}
return null
}
}

fun onNewIntent(intent: Intent) {
if (reactNativeHost.hasInstance()) {
reactNativeHost.reactInstanceManager.onNewIntent(intent);
reactHost?.currentReactContext?.let {
it.onNewIntent(it.currentActivity, intent)
}
}

Expand All @@ -84,7 +99,7 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {
super.onDestroy()
}

override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_MENU || doubleTapReloadRecognizer.didDoubleTapBacktick(
keyCode,
view
Expand All @@ -100,7 +115,7 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {

override fun showDevAppMenu() {
activity?.let {
currentDevSupportManager?.showDevOptionsDialog();
currentDevSupportManager?.showDevOptionsDialog()
}
}

Expand All @@ -109,7 +124,7 @@ open class MendixReactFragment : ReactFragment(), MendixReactFragmentView {
}

interface MendixReactFragmentView : DevAppMenuHandler, BackButtonHandler {
fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean
fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean
}

interface BackButtonHandler {
Expand Down
Loading
Loading