Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- We migrated iOS to the UIKit scene-based life cycle.

## [v0.5.2] - 2026-07-23

- We addressed a scenario where, in iOS, an Auth dialogue could continuously popup on app startup due to access of either large keychain blob items or blocked items.
Expand Down
4 changes: 4 additions & 0 deletions example/ios/MendixNativeExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
1499ABBDB7935B67C3951198 /* libPods-MendixNativeExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24A5FB93E78C6A91516539F4 /* libPods-MendixNativeExample.a */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
761780EF2CA45674006654EE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EE2CA45674006654EE /* SceneDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
E7824D7967CD7EE7C8E76686 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */
Expand All @@ -22,6 +23,7 @@
24A5FB93E78C6A91516539F4 /* libPods-MendixNativeExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MendixNativeExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
503B3921D3702B3C7DF3D941 /* Pods-MendixNativeExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MendixNativeExample.release.xcconfig"; path = "Target Support Files/Pods-MendixNativeExample/Pods-MendixNativeExample.release.xcconfig"; sourceTree = "<group>"; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = MendixNativeExample/AppDelegate.swift; sourceTree = "<group>"; };
761780EE2CA45674006654EE /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SceneDelegate.swift; path = MendixNativeExample/SceneDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = MendixNativeExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
9EF6D8952E93D80B00BFE8AE /* MendixNativeExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MendixNativeExample-Bridging-Header.h"; sourceTree = "<group>"; };
DC6440C7A3C29FAC4633A591 /* Pods-MendixNativeExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MendixNativeExample.debug.xcconfig"; path = "Target Support Files/Pods-MendixNativeExample/Pods-MendixNativeExample.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -49,6 +51,7 @@
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
9EF6D8952E93D80B00BFE8AE /* MendixNativeExample-Bridging-Header.h */,
761780EE2CA45674006654EE /* SceneDelegate.swift */,
);
name = MendixNativeExample;
sourceTree = "<group>";
Expand Down Expand Up @@ -249,6 +252,7 @@
buildActionMask = 2147483647;
files = (
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */,
761780EF2CA45674006654EE /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
38 changes: 3 additions & 35 deletions example/ios/MendixNativeExample/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,41 +1,9 @@
import UIKit
import React
import MendixNative

@main
class AppDelegate: ReactAppProvider {
class AppDelegate: UIResponder, UIApplicationDelegate {

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
setUpProvider()
guard let bundleUrl = bundleURL() else {
let message = "No script URL provided. Make sure the metro packager is running or you have embedded a JS bundle in your application bundle."
fatalError(message)
}
let mendixApp = MendixApp.init(
identifier: nil,
bundleUrl: bundleUrl,
runtimeUrl: URL(string: "http://localhost:8081")!,
warningsFilter: .none,
isDeveloperApp: true,
clearDataAtLaunch: false,
splashScreenPresenter: nil,
reactLoading: nil,
enableThreeFingerGestures: false
)

AppPreferences.devModeEnabled = true
AppPreferences.remoteDebuggingEnabled = true

ReactNative.shared.setup(mendixApp, launchOptions: nil)
ReactNative.shared.start()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
return true
}
}
17 changes: 17 additions & 0 deletions example/ios/MendixNativeExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@
<string></string>
<key>RCTNewArchEnabled</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
Expand Down
39 changes: 39 additions & 0 deletions example/ios/MendixNativeExample/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import UIKit
import React
import MendixNative

class SceneDelegate: ReactAppProvider {

override func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
super.scene(scene, willConnectTo: session, options: connectionOptions)

setUpProvider()
guard let bundleUrl = bundleURL() else {
let message = "No script URL provided. Make sure the metro packager is running or you have embedded a JS bundle in your application bundle."
fatalError(message)
}
let mendixApp = MendixApp.init(
identifier: nil,
bundleUrl: bundleUrl,
runtimeUrl: URL(string: "http://localhost:8081")!,
warningsFilter: .none,
isDeveloperApp: true,
clearDataAtLaunch: false,
splashScreenPresenter: nil,
reactLoading: nil,
enableThreeFingerGestures: false
)
AppPreferences.devModeEnabled = true
AppPreferences.remoteDebuggingEnabled = true
ReactNative.shared.setup(mendixApp)
ReactNative.shared.start()
}

func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ PODS:
- hermes-engine (250829098.0.9):
- hermes-engine/Pre-built (= 250829098.0.9)
- hermes-engine/Pre-built (250829098.0.9)
- MendixNative (0.4.1):
- MendixNative (0.5.2):
- hermes-engine
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -2126,7 +2126,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da
hermes-engine: a7179a4cd45fa3f8143712e52bd3c2d20b5274a0
MendixNative: 0014d648c1ad67c7da144e99603ad636b017b424
MendixNative: b789c95cb7e776e332a0dabd4f39b91e20c1f31d
op-sqlite: e9ef65bcf95a97863874cee87841425bb71c8396
OpenSSL-Universal: 9110d21982bb7e8b22a962b6db56a8aa805afde7
RCTDeprecation: af44b104091a34482596cd9bd7e8d90c4e9b4bd7
Expand Down
38 changes: 31 additions & 7 deletions ios/Modules/Helper/ReactAppProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React_RCTAppDelegate
import ReactAppDependencyProvider

@objcMembers
open class ReactAppProvider: UIResponder, UIApplicationDelegate {
open class ReactAppProvider: UIResponder, UIWindowSceneDelegate {

public static let defaultName = "App"

Expand All @@ -15,26 +15,50 @@ open class ReactAppProvider: UIResponder, UIApplicationDelegate {

var reactRootViewName: String = defaultName

private static weak var currentProvider: ReactAppProvider?
private var reactRootViewController: UIViewController?

public var hasStartedReact: Bool {
return reactRootViewController != nil
}

public func setUpProvider(
moduleName: String = ReactAppProvider.defaultName,
reactRootViewName: String = ReactAppProvider.defaultName
) {
self.moduleName = moduleName
self.reactRootViewName = reactRootViewName
guard reactNativeFactory == nil else {
return
}
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory
window = UIWindow(frame: UIScreen.main.bounds)
}

open func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
return true

open func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else {
return
}
ReactAppProvider.currentProvider = self
window = UIWindow(windowScene: windowScene)

if let reactRootViewController = reactRootViewController {
changeRoot(to: reactRootViewController)
}
}

open func sceneDidDisconnect(_ scene: UIScene) {
if ReactAppProvider.currentProvider === self {
ReactAppProvider.currentProvider = nil
}
}

public func setReactViewController(_ controller: UIViewController) {
controller.view = reactAppView()
reactRootViewController = controller
changeRoot(to: controller)
}

Expand All @@ -43,7 +67,7 @@ open class ReactAppProvider: UIResponder, UIApplicationDelegate {
return nil
}
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.frame = window?.rootViewController?.view.frame ?? .zero
view.frame = window?.bounds ?? .zero
return view
}

Expand All @@ -55,7 +79,7 @@ open class ReactAppProvider: UIResponder, UIApplicationDelegate {
}

public static func shared() -> ReactAppProvider? {
return UIApplication.shared.delegate as? ReactAppProvider
return currentProvider
}

public func changeRoot(to controller: UIViewController) {
Expand Down
2 changes: 1 addition & 1 deletion ios/Modules/Helper/ReactHostHelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ - (nullable id) getModuleForClass: (Class) clazz {
}

- (RCTHost *) currentHost {
ReactAppProvider *reactAppProvider = (ReactAppProvider *) [[UIApplication sharedApplication] delegate];
ReactAppProvider *reactAppProvider = [ReactAppProvider shared];
RCTReactNativeFactory *reactNativeFactory = [reactAppProvider reactNativeFactory];
RCTRootViewFactory *rootViewFactory = [reactNativeFactory rootViewFactory];
RCTHost *reactHost = [rootViewFactory reactHost];
Expand Down
Loading