From 8d135147eb8fc42dee7b2e9b2bfce27834d64ba5 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:50:32 +0300 Subject: [PATCH 01/18] Remove Locale dependency from native language validator --- .../com/codename1/system/package-info.java | 8 ++--- .../src/com/codename1/system/package.html | 10 +++---- .../com/codename1/builders/IPhoneBuilder.java | 23 ++++++++++---- .../hellocodenameone/SwiftKotlinNativeImpl.kt | 11 +++++++ .../NativeInterfaceLanguageValidator.java | 30 +++++++++++++++++++ .../hellocodenameone/SwiftKotlinNative.java | 7 +++++ .../hellocodenameone/HelloCodenameOne.kt | 1 + ...llocodenameone_SwiftKotlinNativeImpl.swift | 12 ++++++++ 8 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift diff --git a/CodenameOne/src/com/codename1/system/package-info.java b/CodenameOne/src/com/codename1/system/package-info.java index 2b5b12fdb7..66468b03b3 100644 --- a/CodenameOne/src/com/codename1/system/package-info.java +++ b/CodenameOne/src/com/codename1/system/package-info.java @@ -2,7 +2,7 @@ /// [support for making platform native API calls](https://www.codenameone.com/how-do-i---access-native-device-functionality-invoke-native-interfaces.html). Notice /// that when we say "native" we do not mean C/C++ always but rather the platforms "native" environment. So in the /// case of Android the Java code will be invoked with full access to the Android API's, in case of iOS an Objective-C -/// message would be sent and so forth. +/// or Swift message would be sent and so forth. /// /// Native interfaces are designed to only allow primitive types, Strings, arrays (single dimension only!) of primitives /// and PeerComponent values. Any other type of parameter/return type is prohibited. However, once in the native layer @@ -59,9 +59,9 @@ /// These sources should be placed under the appropriate folder in the native directory and are sent to the /// server for compilation. /// -/// For Objective-C, one would need to define a class matching the name of the package and the class name -/// combined where the "." elements are replaced by underscores. One would need to provide both a header and -/// an "m" file following this convention e.g.: +/// For iOS, one would need to define a class matching the name of the package and the class name +/// combined where the "." elements are replaced by underscores. This class can be implemented in Objective-C +/// (by providing both a header and an "m" file) or in Swift. Objective-C classes follow this convention e.g.: /// /// ```java /// @interface com_my_code_MyNative : NSObject { diff --git a/CodenameOne/src/com/codename1/system/package.html b/CodenameOne/src/com/codename1/system/package.html index 590dd50809..400f9a20ef 100644 --- a/CodenameOne/src/com/codename1/system/package.html +++ b/CodenameOne/src/com/codename1/system/package.html @@ -8,8 +8,8 @@ support for making platform native API calls. Notice that when we say "native" we do not mean C/C++ always but rather the platforms "native" environment. So in the - case of Android the Java code will be invoked with full access to the Android API's, in case of iOS an Objective-C - message would be sent and so forth. + case of Android the Java code will be invoked with full access to the Android API's, in case of iOS an Objective-C + or Swift message would be sent and so forth.

Native interfaces are designed to only allow primitive types, Strings, arrays (single dimension only!) of primitives @@ -66,9 +66,9 @@ server for compilation.

- For Objective-C, one would need to define a class matching the name of the package and the class name - combined where the "." elements are replaced by underscores. One would need to provide both a header and - an "m" file following this convention e.g.: + For iOS, one would need to define a class matching the name of the package and the class name + combined where the "." elements are replaced by underscores. This class can be implemented in Objective-C + (by providing both a header and an "m" file) or in Swift. Objective-C classes follow this convention e.g.:

 @interface com_my_code_MyNative : NSObject {
diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
index e635fb255d..97ce2b91fe 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
@@ -1099,14 +1099,28 @@ public void usesClassMethod(String cls, String method) {
                         + "#include \"java_lang_String.h\"\n"
                         + "#import \"CodenameOne_GLViewController.h\"\n"
                         + "#import \n"
-                        + "#import \"" + classNameWithUnderscores + "Impl.h\"\n" + newVMInclude
+                        + newVMInclude
                         + "#include \"" + classNameWithUnderscores + "ImplCodenameOne.h\"\n\n"
+                        + "static id cn1_createNativeInterfacePeer(NSString* className) {\n"
+                        + "    Class cls = NSClassFromString(className);\n"
+                        + "    if(cls == Nil) {\n"
+                        + "        NSString* mainBundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleName\"];\n"
+                        + "        if(mainBundleName != nil) {\n"
+                        + "            NSString* prefixedName = [mainBundleName stringByAppendingFormat:@\".%@\", className];\n"
+                        + "            cls = NSClassFromString(prefixedName);\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "    if(cls == Nil) {\n"
+                        + "        return nil;\n"
+                        + "    }\n"
+                        + "    return [[cls alloc] init];\n"
+                        + "}\n\n"
                         + "JAVA_LONG " + classNameWithUnderscores + "ImplCodenameOne_initializeNativePeer__" + postfixForNewVM + "(" + prefixForNewVM + ") {\n"
-                        + "    " + classNameWithUnderscores + "Impl* i = [[" + classNameWithUnderscores + "Impl alloc] init];\n"
+                        + "    id i = cn1_createNativeInterfacePeer(@\"" + classNameWithUnderscores + "Impl\");\n"
                         + "    return i;\n"
                         + "}\n\n"
                         + "void " + classNameWithUnderscores + "ImplCodenameOne_releaseNativePeerInstance___long(" + prefix2ForNewVM + "JAVA_LONG l) {\n"
-                        + "    " + classNameWithUnderscores + "Impl* i = (" + classNameWithUnderscores + "Impl*)l;\n"
+                        + "    id i = (id)l;\n"
                         + "    [i release];\n"
                         + "}\n\n"
                         + "extern NSData* arrayToData(JAVA_OBJECT arr);\n"
@@ -1135,8 +1149,7 @@ public void usesClassMethod(String cls, String method) {
                     String mFileBody;
 
                     mFileArgs = "(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT me";
-                    mFileBody = "    " + classNameWithUnderscores + "Impl* ptr = (" + classNameWithUnderscores +
-                        "Impl*)get_field_" + classNameWithUnderscores + "ImplCodenameOne_nativePeer(me);\n";
+                    mFileBody = "    id ptr = (id)get_field_" + classNameWithUnderscores + "ImplCodenameOne_nativePeer(me);\n";
 
                     
                     if(!(returnType.equals(Void.class) || returnType.equals(Void.TYPE))) {
diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
new file mode 100644
index 0000000000..67f5bbf4cc
--- /dev/null
+++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
@@ -0,0 +1,11 @@
+package com.codenameone.examples.hellocodenameone
+
+class SwiftKotlinNativeImpl {
+    fun implementationLanguage(): String {
+        return "kotlin"
+    }
+
+    fun isSupported(): Boolean {
+        return true
+    }
+}
diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
new file mode 100644
index 0000000000..9490258ed5
--- /dev/null
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
@@ -0,0 +1,30 @@
+package com.codenameone.examples.hellocodenameone;
+
+import com.codename1.system.NativeLookup;
+import com.codename1.ui.CN;
+
+public final class NativeInterfaceLanguageValidator {
+    private NativeInterfaceLanguageValidator() {
+    }
+
+    public static void validate() {
+        String platformName = CN.getPlatformName();
+        String normalizedPlatform = platformName == null ? "" : platformName.toLowerCase();
+        boolean isAndroid = normalizedPlatform.contains("android");
+        boolean isIos = normalizedPlatform.contains("ios") || normalizedPlatform.contains("iphone");
+        if (!isAndroid && !isIos) {
+            return;
+        }
+
+        SwiftKotlinNative nativeImpl = NativeLookup.create(SwiftKotlinNative.class);
+        if (nativeImpl == null || !nativeImpl.isSupported()) {
+            throw new IllegalStateException("SwiftKotlinNative is not available on " + platformName);
+        }
+
+        String expected = isAndroid ? "kotlin" : "swift";
+        String actual = nativeImpl.implementationLanguage();
+        if (!expected.equalsIgnoreCase(actual)) {
+            throw new IllegalStateException("Expected " + expected + " implementation on " + platformName + " but got " + actual);
+        }
+    }
+}
diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java
new file mode 100644
index 0000000000..2faf787457
--- /dev/null
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java
@@ -0,0 +1,7 @@
+package com.codenameone.examples.hellocodenameone;
+
+import com.codename1.system.NativeInterface;
+
+public interface SwiftKotlinNative extends NativeInterface {
+    String implementationLanguage();
+}
diff --git a/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt b/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
index 2214782035..5de71abd27 100644
--- a/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
+++ b/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
@@ -14,6 +14,7 @@ open class HelloCodenameOne : Lifecycle() {
             "Jailbroken device detected by Display.isJailbrokenDevice()."
         }
         DefaultMethodDemo.validate()
+        NativeInterfaceLanguageValidator.validate()
         Cn1ssDeviceRunner.addTest(KotlinUiTest())
         TestReporting.setInstance(Cn1ssDeviceRunnerReporter())
     }
diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
new file mode 100644
index 0000000000..328133d274
--- /dev/null
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
@@ -0,0 +1,12 @@
+import Foundation
+
+@objc(com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl)
+class com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl: NSObject {
+    @objc func implementationLanguage() -> String {
+        return "swift"
+    }
+
+    @objc func isSupported() -> Bool {
+        return true
+    }
+}

From cb238b5e7387f3181d3effec923d37256ed53be0 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 14:08:07 +0300
Subject: [PATCH 02/18] Fix Android native stub by using Java implementation
 class

---
 .../hellocodenameone/SwiftKotlinNativeImpl.java     | 13 +++++++++++++
 .../hellocodenameone/SwiftKotlinNativeImpl.kt       | 11 -----------
 2 files changed, 13 insertions(+), 11 deletions(-)
 create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
 delete mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt

diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
new file mode 100644
index 0000000000..5ee7a9ab77
--- /dev/null
+++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
@@ -0,0 +1,13 @@
+package com.codenameone.examples.hellocodenameone;
+
+public class SwiftKotlinNativeImpl {
+    public String implementationLanguage() {
+        // Android native stubs are compiled with javac in the current pipeline.
+        // Keep this implementation in Java even though it validates "kotlin" mode.
+        return "kotlin";
+    }
+
+    public boolean isSupported() {
+        return true;
+    }
+}
diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
deleted file mode 100644
index 67f5bbf4cc..0000000000
--- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.codenameone.examples.hellocodenameone
-
-class SwiftKotlinNativeImpl {
-    fun implementationLanguage(): String {
-        return "kotlin"
-    }
-
-    fun isSupported(): Boolean {
-        return true
-    }
-}

From 90d961847e51670772493385a766b919447fb2a1 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 14:08:11 +0300
Subject: [PATCH 03/18] Support Kotlin native impls in generated Android native
 stubs

---
 .../java/com/codename1/builders/Executor.java | 49 +++++++++++++++++--
 .../SwiftKotlinNativeImpl.java                | 13 -----
 .../hellocodenameone/SwiftKotlinNativeImpl.kt | 11 +++++
 3 files changed, 56 insertions(+), 17 deletions(-)
 delete mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
 create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
index e3524630c6..e78b60f8a0 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
@@ -681,7 +681,27 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC
                 String javaImplSourceFile = "package " + currentNative.getPackage().getName() + ";\n\n"
                         + "import com.codename1.ui.PeerComponent;\n\n"
                         + "public class " + currentNative.getSimpleName() + "Stub implements " + currentNative.getSimpleName() + "{\n"
-                        + "    private " + currentNative.getSimpleName() + getImplSuffix() + " impl = new " + currentNative.getSimpleName() + getImplSuffix() + "();\n\n";
+                        + "    private final Object impl = createImpl();\n\n"
+                        + "    private static Object createImpl() {\n"
+                        + "        try {\n"
+                        + "            return Class.forName(\"" + currentNative.getName() + getImplSuffix() + "\").newInstance();\n"
+                        + "        } catch (Throwable t) {\n"
+                        + "            throw new RuntimeException(\"Failed to instantiate native implementation for " + currentNative.getName() + "\", t);\n"
+                        + "        }\n"
+                        + "    }\n\n"
+                        + "    private Object __cn1Invoke(String methodName, Object[] args) {\n"
+                        + "        try {\n"
+                        + "            java.lang.reflect.Method[] methods = impl.getClass().getMethods();\n"
+                        + "            for (java.lang.reflect.Method method : methods) {\n"
+                        + "                if (method.getName().equals(methodName) && method.getParameterTypes().length == args.length) {\n"
+                        + "                    return method.invoke(impl, args);\n"
+                        + "                }\n"
+                        + "            }\n"
+                        + "            throw new NoSuchMethodException(methodName + \" with \" + args.length + \" args\");\n"
+                        + "        } catch (Throwable t) {\n"
+                        + "            throw new RuntimeException(\"Failed to invoke native method \" + methodName, t);\n"
+                        + "        }\n"
+                        + "    }\n\n";
 
                 for (Method m : currentNative.getMethods()) {
                     String name = m.getName();
@@ -709,13 +729,34 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC
                         }
                     }
                     javaImplSourceFile += ") {\n";
+                    String invocationExpression = "__cn1Invoke(\"" + name + "\", new Object[]{" + args + "})";
                     if (Void.class == returnType || Void.TYPE == returnType) {
-                        javaImplSourceFile += "        impl." + name + "(" + args + ");\n    }\n\n";
+                        javaImplSourceFile += "        " + invocationExpression + ";\n    }\n\n";
                     } else {
                         if (returnType.getName().equals("com.codename1.ui.PeerComponent")) {
-                            javaImplSourceFile += "        return " + generatePeerComponentCreationCode("impl." + name + "(" + args + ")") + ";\n    }\n\n";
+                            javaImplSourceFile += "        return " + generatePeerComponentCreationCode(invocationExpression) + ";\n    }\n\n";
+                        } else if (returnType.isPrimitive()) {
+                            if (returnType == Boolean.TYPE) {
+                                javaImplSourceFile += "        return ((Boolean)" + invocationExpression + ").booleanValue();\n    }\n\n";
+                            } else if (returnType == Integer.TYPE) {
+                                javaImplSourceFile += "        return ((Integer)" + invocationExpression + ").intValue();\n    }\n\n";
+                            } else if (returnType == Long.TYPE) {
+                                javaImplSourceFile += "        return ((Long)" + invocationExpression + ").longValue();\n    }\n\n";
+                            } else if (returnType == Byte.TYPE) {
+                                javaImplSourceFile += "        return ((Byte)" + invocationExpression + ").byteValue();\n    }\n\n";
+                            } else if (returnType == Short.TYPE) {
+                                javaImplSourceFile += "        return ((Short)" + invocationExpression + ").shortValue();\n    }\n\n";
+                            } else if (returnType == Character.TYPE) {
+                                javaImplSourceFile += "        return ((Character)" + invocationExpression + ").charValue();\n    }\n\n";
+                            } else if (returnType == Float.TYPE) {
+                                javaImplSourceFile += "        return ((Float)" + invocationExpression + ").floatValue();\n    }\n\n";
+                            } else if (returnType == Double.TYPE) {
+                                javaImplSourceFile += "        return ((Double)" + invocationExpression + ").doubleValue();\n    }\n\n";
+                            } else {
+                                javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
+                            }
                         } else {
-                            javaImplSourceFile += "        return impl." + name + "(" + args + ");\n    }\n\n";
+                            javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
                         }
                     }
                 }
diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
deleted file mode 100644
index 5ee7a9ab77..0000000000
--- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.codenameone.examples.hellocodenameone;
-
-public class SwiftKotlinNativeImpl {
-    public String implementationLanguage() {
-        // Android native stubs are compiled with javac in the current pipeline.
-        // Keep this implementation in Java even though it validates "kotlin" mode.
-        return "kotlin";
-    }
-
-    public boolean isSupported() {
-        return true;
-    }
-}
diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
new file mode 100644
index 0000000000..67f5bbf4cc
--- /dev/null
+++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
@@ -0,0 +1,11 @@
+package com.codenameone.examples.hellocodenameone
+
+class SwiftKotlinNativeImpl {
+    fun implementationLanguage(): String {
+        return "kotlin"
+    }
+
+    fun isSupported(): Boolean {
+        return true
+    }
+}

From d42de76ceaec0395083f82811e9816ca286a59ab Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 14:55:37 +0300
Subject: [PATCH 04/18] Avoid NoSuchMethodException in generated native stubs

---
 .../src/main/java/com/codename1/builders/Executor.java          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
index e78b60f8a0..e35187df6b 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
@@ -697,7 +697,7 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC
                         + "                    return method.invoke(impl, args);\n"
                         + "                }\n"
                         + "            }\n"
-                        + "            throw new NoSuchMethodException(methodName + \" with \" + args.length + \" args\");\n"
+                        + "            throw new RuntimeException(methodName + \" with \" + args.length + \" args\");\n"
                         + "        } catch (Throwable t) {\n"
                         + "            throw new RuntimeException(\"Failed to invoke native method \" + methodName, t);\n"
                         + "        }\n"

From 3162274c89735eaee6a98ffb56a1f2399258789c Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 17:59:27 +0300
Subject: [PATCH 05/18] Scope reflective native stubs to Android builder only

---
 .../builders/AndroidGradleBuilder.java        | 111 ++++++++++++++++++
 .../java/com/codename1/builders/Executor.java |  49 +-------
 2 files changed, 115 insertions(+), 45 deletions(-)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
index 4377ebab93..0da9531668 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
@@ -3972,6 +3972,117 @@ static String xmlize(String s) {
     }
 
 
+    @Override
+    protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentClassLoader, File stubDir, File... classesDirectory) throws MalformedURLException, IOException {
+        nativeInterfaces = findNativeInterfaces(parentClassLoader, classesDirectory);
+        String registerNativeFunctions = "";
+        if (nativeInterfaces != null && nativeInterfaces.length > 0) {
+            for (Class n : nativeInterfaces) {
+                registerNativeFunctions += "        NativeLookup.register(" + n.getName() + ".class, "
+                        + n.getName() + "Stub.class" + ");\n";
+            }
+        }
+
+        if (nativeInterfaces != null && nativeInterfaces.length > 0) {
+            for (Class currentNative : nativeInterfaces) {
+                File folder = new File(stubDir, currentNative.getPackage().getName().replace('.', File.separatorChar));
+                folder.mkdirs();
+                File javaFile = new File(folder, currentNative.getSimpleName() + "Stub.java");
+
+                String javaImplSourceFile = "package " + currentNative.getPackage().getName() + ";\n\n"
+                        + "import com.codename1.ui.PeerComponent;\n\n"
+                        + "public class " + currentNative.getSimpleName() + "Stub implements " + currentNative.getSimpleName() + "{\n"
+                        + "    private final Object impl = createImpl();\n\n"
+                        + "    private static Object createImpl() {\n"
+                        + "        try {\n"
+                        + "            return Class.forName(\"" + currentNative.getName() + getImplSuffix() + "\").newInstance();\n"
+                        + "        } catch (Throwable t) {\n"
+                        + "            throw new RuntimeException(\"Failed to instantiate native implementation for " + currentNative.getName() + "\", t);\n"
+                        + "        }\n"
+                        + "    }\n\n"
+                        + "    private Object __cn1Invoke(String methodName, Object[] args) {\n"
+                        + "        try {\n"
+                        + "            java.lang.reflect.Method[] methods = impl.getClass().getMethods();\n"
+                        + "            for (java.lang.reflect.Method method : methods) {\n"
+                        + "                if (method.getName().equals(methodName) && method.getParameterTypes().length == args.length) {\n"
+                        + "                    return method.invoke(impl, args);\n"
+                        + "                }\n"
+                        + "            }\n"
+                        + "            throw new RuntimeException(methodName + \" with \" + args.length + \" args\");\n"
+                        + "        } catch (Throwable t) {\n"
+                        + "            throw new RuntimeException(\"Failed to invoke native method \" + methodName, t);\n"
+                        + "        }\n"
+                        + "    }\n\n";
+
+                for (Method m : currentNative.getMethods()) {
+                    String name = m.getName();
+                    if (name.equals("hashCode") || name.equals("equals") || name.equals("toString")) {
+                        continue;
+                    }
+
+                    Class returnType = m.getReturnType();
+
+                    javaImplSourceFile += "    public " + returnType.getSimpleName() + " " + name + "(";
+                    Class[] params = m.getParameterTypes();
+                    String args = "";
+                    if (params != null && params.length > 0) {
+                        for (int iter = 0; iter < params.length; iter++) {
+                            if (iter > 0) {
+                                javaImplSourceFile += ", ";
+                                args += ", ";
+                            }
+                            javaImplSourceFile += params[iter].getSimpleName() + " param" + iter;
+                            if (params[iter].getName().equals("com.codename1.ui.PeerComponent")) {
+                                args += convertPeerComponentToNative("param" + iter);
+                            } else {
+                                args += "param" + iter;
+                            }
+                        }
+                    }
+                    javaImplSourceFile += ") {\n";
+                    String invocationExpression = "__cn1Invoke(\"" + name + "\", new Object[]{" + args + "})";
+                    if (Void.class == returnType || Void.TYPE == returnType) {
+                        javaImplSourceFile += "        " + invocationExpression + ";\n    }\n\n";
+                    } else {
+                        if (returnType.getName().equals("com.codename1.ui.PeerComponent")) {
+                            javaImplSourceFile += "        return " + generatePeerComponentCreationCode(invocationExpression) + ";\n    }\n\n";
+                        } else if (returnType.isPrimitive()) {
+                            if (returnType == Boolean.TYPE) {
+                                javaImplSourceFile += "        return ((Boolean)" + invocationExpression + ").booleanValue();\n    }\n\n";
+                            } else if (returnType == Integer.TYPE) {
+                                javaImplSourceFile += "        return ((Integer)" + invocationExpression + ").intValue();\n    }\n\n";
+                            } else if (returnType == Long.TYPE) {
+                                javaImplSourceFile += "        return ((Long)" + invocationExpression + ").longValue();\n    }\n\n";
+                            } else if (returnType == Byte.TYPE) {
+                                javaImplSourceFile += "        return ((Byte)" + invocationExpression + ").byteValue();\n    }\n\n";
+                            } else if (returnType == Short.TYPE) {
+                                javaImplSourceFile += "        return ((Short)" + invocationExpression + ").shortValue();\n    }\n\n";
+                            } else if (returnType == Character.TYPE) {
+                                javaImplSourceFile += "        return ((Character)" + invocationExpression + ").charValue();\n    }\n\n";
+                            } else if (returnType == Float.TYPE) {
+                                javaImplSourceFile += "        return ((Float)" + invocationExpression + ").floatValue();\n    }\n\n";
+                            } else if (returnType == Double.TYPE) {
+                                javaImplSourceFile += "        return ((Double)" + invocationExpression + ").doubleValue();\n    }\n\n";
+                            } else {
+                                javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
+                            }
+                        } else {
+                            javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
+                        }
+                    }
+                }
+
+                javaImplSourceFile += "}\n";
+
+                try (FileOutputStream out = new FileOutputStream(javaFile)) {
+                    out.write(javaImplSourceFile.getBytes(StandardCharsets.UTF_8));
+                }
+            }
+        }
+
+        return registerNativeFunctions;
+    }
+
     @Override
     protected String generatePeerComponentCreationCode(String methodCallString) {
         return "PeerComponent.create(" + methodCallString + ")";
diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
index e35187df6b..e3524630c6 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/Executor.java
@@ -681,27 +681,7 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC
                 String javaImplSourceFile = "package " + currentNative.getPackage().getName() + ";\n\n"
                         + "import com.codename1.ui.PeerComponent;\n\n"
                         + "public class " + currentNative.getSimpleName() + "Stub implements " + currentNative.getSimpleName() + "{\n"
-                        + "    private final Object impl = createImpl();\n\n"
-                        + "    private static Object createImpl() {\n"
-                        + "        try {\n"
-                        + "            return Class.forName(\"" + currentNative.getName() + getImplSuffix() + "\").newInstance();\n"
-                        + "        } catch (Throwable t) {\n"
-                        + "            throw new RuntimeException(\"Failed to instantiate native implementation for " + currentNative.getName() + "\", t);\n"
-                        + "        }\n"
-                        + "    }\n\n"
-                        + "    private Object __cn1Invoke(String methodName, Object[] args) {\n"
-                        + "        try {\n"
-                        + "            java.lang.reflect.Method[] methods = impl.getClass().getMethods();\n"
-                        + "            for (java.lang.reflect.Method method : methods) {\n"
-                        + "                if (method.getName().equals(methodName) && method.getParameterTypes().length == args.length) {\n"
-                        + "                    return method.invoke(impl, args);\n"
-                        + "                }\n"
-                        + "            }\n"
-                        + "            throw new RuntimeException(methodName + \" with \" + args.length + \" args\");\n"
-                        + "        } catch (Throwable t) {\n"
-                        + "            throw new RuntimeException(\"Failed to invoke native method \" + methodName, t);\n"
-                        + "        }\n"
-                        + "    }\n\n";
+                        + "    private " + currentNative.getSimpleName() + getImplSuffix() + " impl = new " + currentNative.getSimpleName() + getImplSuffix() + "();\n\n";
 
                 for (Method m : currentNative.getMethods()) {
                     String name = m.getName();
@@ -729,34 +709,13 @@ protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentC
                         }
                     }
                     javaImplSourceFile += ") {\n";
-                    String invocationExpression = "__cn1Invoke(\"" + name + "\", new Object[]{" + args + "})";
                     if (Void.class == returnType || Void.TYPE == returnType) {
-                        javaImplSourceFile += "        " + invocationExpression + ";\n    }\n\n";
+                        javaImplSourceFile += "        impl." + name + "(" + args + ");\n    }\n\n";
                     } else {
                         if (returnType.getName().equals("com.codename1.ui.PeerComponent")) {
-                            javaImplSourceFile += "        return " + generatePeerComponentCreationCode(invocationExpression) + ";\n    }\n\n";
-                        } else if (returnType.isPrimitive()) {
-                            if (returnType == Boolean.TYPE) {
-                                javaImplSourceFile += "        return ((Boolean)" + invocationExpression + ").booleanValue();\n    }\n\n";
-                            } else if (returnType == Integer.TYPE) {
-                                javaImplSourceFile += "        return ((Integer)" + invocationExpression + ").intValue();\n    }\n\n";
-                            } else if (returnType == Long.TYPE) {
-                                javaImplSourceFile += "        return ((Long)" + invocationExpression + ").longValue();\n    }\n\n";
-                            } else if (returnType == Byte.TYPE) {
-                                javaImplSourceFile += "        return ((Byte)" + invocationExpression + ").byteValue();\n    }\n\n";
-                            } else if (returnType == Short.TYPE) {
-                                javaImplSourceFile += "        return ((Short)" + invocationExpression + ").shortValue();\n    }\n\n";
-                            } else if (returnType == Character.TYPE) {
-                                javaImplSourceFile += "        return ((Character)" + invocationExpression + ").charValue();\n    }\n\n";
-                            } else if (returnType == Float.TYPE) {
-                                javaImplSourceFile += "        return ((Float)" + invocationExpression + ").floatValue();\n    }\n\n";
-                            } else if (returnType == Double.TYPE) {
-                                javaImplSourceFile += "        return ((Double)" + invocationExpression + ").doubleValue();\n    }\n\n";
-                            } else {
-                                javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
-                            }
+                            javaImplSourceFile += "        return " + generatePeerComponentCreationCode("impl." + name + "(" + args + ")") + ";\n    }\n\n";
                         } else {
-                            javaImplSourceFile += "        return (" + returnType.getSimpleName() + ")" + invocationExpression + ";\n    }\n\n";
+                            javaImplSourceFile += "        return impl." + name + "(" + args + ");\n    }\n\n";
                         }
                     }
                 }

From 8497d18a4f0310831d229c508b286f3b5793de45 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 19:45:59 +0300
Subject: [PATCH 06/18] Add missing MalformedURLException import in
 AndroidGradleBuilder

---
 .../main/java/com/codename1/builders/AndroidGradleBuilder.java   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
index 0da9531668..14ad4d1719 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
@@ -42,6 +42,7 @@
 import java.awt.image.RGBImageFilter;
 import java.io.*;
 
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.channels.FileChannel;

From 09bb8994f08d2af4364c9e66f130d517903cac31 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 20:02:02 +0300
Subject: [PATCH 07/18] Fix AndroidGradleBuilder override compile issues

---
 .../com/codename1/builders/AndroidGradleBuilder.java  | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
index 14ad4d1719..3f83d699cf 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java
@@ -41,6 +41,7 @@
 import java.awt.image.ImageProducer;
 import java.awt.image.RGBImageFilter;
 import java.io.*;
+import java.lang.reflect.Method;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -3975,17 +3976,17 @@ static String xmlize(String s) {
 
     @Override
     protected String registerNativeImplementationsAndCreateStubs(ClassLoader parentClassLoader, File stubDir, File... classesDirectory) throws MalformedURLException, IOException {
-        nativeInterfaces = findNativeInterfaces(parentClassLoader, classesDirectory);
+        Class[] discoveredNativeInterfaces = findNativeInterfaces(parentClassLoader, classesDirectory);
         String registerNativeFunctions = "";
-        if (nativeInterfaces != null && nativeInterfaces.length > 0) {
-            for (Class n : nativeInterfaces) {
+        if (discoveredNativeInterfaces != null && discoveredNativeInterfaces.length > 0) {
+            for (Class n : discoveredNativeInterfaces) {
                 registerNativeFunctions += "        NativeLookup.register(" + n.getName() + ".class, "
                         + n.getName() + "Stub.class" + ");\n";
             }
         }
 
-        if (nativeInterfaces != null && nativeInterfaces.length > 0) {
-            for (Class currentNative : nativeInterfaces) {
+        if (discoveredNativeInterfaces != null && discoveredNativeInterfaces.length > 0) {
+            for (Class currentNative : discoveredNativeInterfaces) {
                 File folder = new File(stubDir, currentNative.getPackage().getName().replace('.', File.separatorChar));
                 folder.mkdirs();
                 File javaFile = new File(folder, currentNative.getSimpleName() + "Stub.java");

From 2bce5fd91cbea7650fd667f99db08107848c4f13 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Wed, 1 Apr 2026 22:39:19 +0300
Subject: [PATCH 08/18] Harden iOS native class lookup for Swift module naming

---
 .../com/codename1/builders/IPhoneBuilder.java | 26 ++++++++++++++-----
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
index 97ce2b91fe..f3fed6879b 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
@@ -1102,15 +1102,29 @@ public void usesClassMethod(String cls, String method) {
                         + newVMInclude
                         + "#include \"" + classNameWithUnderscores + "ImplCodenameOne.h\"\n\n"
                         + "static id cn1_createNativeInterfacePeer(NSString* className) {\n"
-                        + "    Class cls = NSClassFromString(className);\n"
-                        + "    if(cls == Nil) {\n"
-                        + "        NSString* mainBundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleName\"];\n"
-                        + "        if(mainBundleName != nil) {\n"
-                        + "            NSString* prefixedName = [mainBundleName stringByAppendingFormat:@\".%@\", className];\n"
-                        + "            cls = NSClassFromString(prefixedName);\n"
+                        + "    NSMutableArray* candidates = [NSMutableArray arrayWithObject:className];\n"
+                        + "    NSString* executableName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleExecutable\"];\n"
+                        + "    NSString* bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@\"CFBundleName\"];\n"
+                        + "    NSArray* moduleNames = @[executableName ?: @\"\", bundleName ?: @\"\"];\n"
+                        + "    for(NSString* moduleName in moduleNames) {\n"
+                        + "        if(moduleName.length == 0) {\n"
+                        + "            continue;\n"
+                        + "        }\n"
+                        + "        NSString* sanitized = [[moduleName stringByReplacingOccurrencesOfString:@\"-\" withString:@\"_\"] stringByReplacingOccurrencesOfString:@\" \" withString:@\"_\"];\n"
+                        + "        [candidates addObject:[sanitized stringByAppendingFormat:@\".%@\", className]];\n"
+                        + "        if(![sanitized isEqualToString:moduleName]) {\n"
+                        + "            [candidates addObject:[moduleName stringByAppendingFormat:@\".%@\", className]];\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "    Class cls = Nil;\n"
+                        + "    for(NSString* candidate in candidates) {\n"
+                        + "        cls = NSClassFromString(candidate);\n"
+                        + "        if(cls != Nil) {\n"
+                        + "            break;\n"
                         + "        }\n"
                         + "    }\n"
                         + "    if(cls == Nil) {\n"
+                        + "        NSLog(@\"[CN1] Failed to find native interface class %@. Tried: %@\", className, candidates);\n"
                         + "        return nil;\n"
                         + "    }\n"
                         + "    return [[cls alloc] init];\n"

From 1d8b85cad0c0bf7d9a89d15ab4a369fa8a6b8632 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 07:03:44 +0300
Subject: [PATCH 09/18] Add CN1SS diagnostics for Swift/Kotlin native interface
 validation

---
 .../examples/hellocodenameone/SwiftKotlinNativeImpl.kt     | 4 ++++
 .../hellocodenameone/NativeInterfaceLanguageValidator.java | 7 ++++++-
 .../examples/hellocodenameone/SwiftKotlinNative.java       | 1 +
 .../examples/hellocodenameone/HelloCodenameOne.kt          | 7 ++++++-
 ...e_examples_hellocodenameone_SwiftKotlinNativeImpl.swift | 4 ++++
 5 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
index 67f5bbf4cc..29a6938b88 100644
--- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
+++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNativeImpl.kt
@@ -5,6 +5,10 @@ class SwiftKotlinNativeImpl {
         return "kotlin"
     }
 
+    fun diagnostics(): String {
+        return "android-kotlin-native-impl"
+    }
+
     fun isSupported(): Boolean {
         return true
     }
diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
index 9490258ed5..c8d29c074f 100644
--- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
@@ -10,21 +10,26 @@ private NativeInterfaceLanguageValidator() {
     public static void validate() {
         String platformName = CN.getPlatformName();
         String normalizedPlatform = platformName == null ? "" : platformName.toLowerCase();
+        System.out.println("CN1SS:SWIFT_DIAG:START platform=" + platformName);
         boolean isAndroid = normalizedPlatform.contains("android");
         boolean isIos = normalizedPlatform.contains("ios") || normalizedPlatform.contains("iphone");
         if (!isAndroid && !isIos) {
+            System.out.println("CN1SS:SWIFT_DIAG:SKIP platform=" + platformName);
             return;
         }
 
         SwiftKotlinNative nativeImpl = NativeLookup.create(SwiftKotlinNative.class);
+        System.out.println("CN1SS:SWIFT_DIAG:NATIVE_LOOKUP result=" + (nativeImpl == null ? "null" : nativeImpl.getClass().getName()));
         if (nativeImpl == null || !nativeImpl.isSupported()) {
             throw new IllegalStateException("SwiftKotlinNative is not available on " + platformName);
         }
 
         String expected = isAndroid ? "kotlin" : "swift";
         String actual = nativeImpl.implementationLanguage();
+        String diagnostics = nativeImpl.diagnostics();
+        System.out.println("CN1SS:SWIFT_DIAG:RESULT expected=" + expected + " actual=" + actual + " diagnostics=" + diagnostics);
         if (!expected.equalsIgnoreCase(actual)) {
-            throw new IllegalStateException("Expected " + expected + " implementation on " + platformName + " but got " + actual);
+            throw new IllegalStateException("Expected " + expected + " implementation on " + platformName + " but got " + actual + ". diagnostics=" + diagnostics);
         }
     }
 }
diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java
index 2faf787457..cc495d7b4a 100644
--- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/SwiftKotlinNative.java
@@ -4,4 +4,5 @@
 
 public interface SwiftKotlinNative extends NativeInterface {
     String implementationLanguage();
+    String diagnostics();
 }
diff --git a/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt b/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
index 5de71abd27..a768e6f3f9 100644
--- a/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
+++ b/scripts/hellocodenameone/common/src/main/kotlin/com/codenameone/examples/hellocodenameone/HelloCodenameOne.kt
@@ -14,7 +14,12 @@ open class HelloCodenameOne : Lifecycle() {
             "Jailbroken device detected by Display.isJailbrokenDevice()."
         }
         DefaultMethodDemo.validate()
-        NativeInterfaceLanguageValidator.validate()
+        try {
+            NativeInterfaceLanguageValidator.validate()
+        } catch (t: Throwable) {
+            System.out.println("CN1SS:SWIFT_DIAG:VALIDATION_EXCEPTION " + t.javaClass.name + ": " + t.message)
+            t.printStackTrace()
+        }
         Cn1ssDeviceRunner.addTest(KotlinUiTest())
         TestReporting.setInstance(Cn1ssDeviceRunnerReporter())
     }
diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
index 328133d274..27c7209d84 100644
--- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
@@ -6,6 +6,10 @@ class com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl: NSObject
         return "swift"
     }
 
+    @objc func diagnostics() -> String {
+        return "ios-swift-native-impl"
+    }
+
     @objc func isSupported() -> Bool {
         return true
     }

From fb13946314617e742e11fbf351f76c8e42696b46 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 07:45:49 +0300
Subject: [PATCH 10/18] Clarify null handling in native language validator

---
 .../hellocodenameone/NativeInterfaceLanguageValidator.java   | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
index c8d29c074f..eb4812c780 100644
--- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
@@ -20,7 +20,10 @@ public static void validate() {
 
         SwiftKotlinNative nativeImpl = NativeLookup.create(SwiftKotlinNative.class);
         System.out.println("CN1SS:SWIFT_DIAG:NATIVE_LOOKUP result=" + (nativeImpl == null ? "null" : nativeImpl.getClass().getName()));
-        if (nativeImpl == null || !nativeImpl.isSupported()) {
+        if (nativeImpl == null) {
+            throw new IllegalStateException("SwiftKotlinNative lookup returned null on " + platformName);
+        }
+        if (!nativeImpl.isSupported()) {
             throw new IllegalStateException("SwiftKotlinNative is not available on " + platformName);
         }
 

From 2cff073cf7d63fa19da4c710c3f815d8340cb8f9 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 10:02:55 +0300
Subject: [PATCH 11/18] Emit compact swift diagnostic summary in CN1SS suite
 output

---
 .../NativeInterfaceLanguageValidator.java            | 12 ++++++++++++
 .../hellocodenameone/tests/Cn1ssDeviceRunner.java    |  2 ++
 2 files changed, 14 insertions(+)

diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
index eb4812c780..f1b0a08dec 100644
--- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeInterfaceLanguageValidator.java
@@ -4,26 +4,36 @@
 import com.codename1.ui.CN;
 
 public final class NativeInterfaceLanguageValidator {
+    private static String lastStatus = "UNINITIALIZED";
+
     private NativeInterfaceLanguageValidator() {
     }
 
+    public static String getLastStatus() {
+        return lastStatus;
+    }
+
     public static void validate() {
         String platformName = CN.getPlatformName();
         String normalizedPlatform = platformName == null ? "" : platformName.toLowerCase();
         System.out.println("CN1SS:SWIFT_DIAG:START platform=" + platformName);
+        lastStatus = "START platform=" + platformName;
         boolean isAndroid = normalizedPlatform.contains("android");
         boolean isIos = normalizedPlatform.contains("ios") || normalizedPlatform.contains("iphone");
         if (!isAndroid && !isIos) {
             System.out.println("CN1SS:SWIFT_DIAG:SKIP platform=" + platformName);
+            lastStatus = "SKIP platform=" + platformName;
             return;
         }
 
         SwiftKotlinNative nativeImpl = NativeLookup.create(SwiftKotlinNative.class);
         System.out.println("CN1SS:SWIFT_DIAG:NATIVE_LOOKUP result=" + (nativeImpl == null ? "null" : nativeImpl.getClass().getName()));
         if (nativeImpl == null) {
+            lastStatus = "LOOKUP_NULL platform=" + platformName;
             throw new IllegalStateException("SwiftKotlinNative lookup returned null on " + platformName);
         }
         if (!nativeImpl.isSupported()) {
+            lastStatus = "NOT_SUPPORTED platform=" + platformName;
             throw new IllegalStateException("SwiftKotlinNative is not available on " + platformName);
         }
 
@@ -32,7 +42,9 @@ public static void validate() {
         String diagnostics = nativeImpl.diagnostics();
         System.out.println("CN1SS:SWIFT_DIAG:RESULT expected=" + expected + " actual=" + actual + " diagnostics=" + diagnostics);
         if (!expected.equalsIgnoreCase(actual)) {
+            lastStatus = "MISMATCH expected=" + expected + " actual=" + actual + " diagnostics=" + diagnostics;
             throw new IllegalStateException("Expected " + expected + " implementation on " + platformName + " but got " + actual + ". diagnostics=" + diagnostics);
         }
+        lastStatus = "OK expected=" + expected + " actual=" + actual + " diagnostics=" + diagnostics;
     }
 }
diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java
index 29d31a3a1f..8bf85f419f 100644
--- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java
+++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java
@@ -7,6 +7,7 @@
 import com.codename1.ui.Display;
 import com.codename1.ui.Form;
 import com.codename1.util.StringUtil;
+import com.codenameone.examples.hellocodenameone.NativeInterfaceLanguageValidator;
 import com.codenameone.examples.hellocodenameone.tests.graphics.AffineScale;
 import com.codenameone.examples.hellocodenameone.tests.graphics.Clip;
 import com.codenameone.examples.hellocodenameone.tests.graphics.DrawArc;
@@ -131,6 +132,7 @@ public void runSuite() {
             }
             log("CN1SS:INFO:suite finished test=" + testName);
         }
+        log("CN1SS:INFO:swift_diag_status=" + NativeInterfaceLanguageValidator.getLastStatus());
         log("CN1SS:SUITE:FINISHED");
         TestReporting.getInstance().testExecutionFinished(getClass().getName());
         if (CN.isSimulator()) {

From 968fb7dd7f967a38296ff2c4b7b9843d1025e782 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 11:08:48 +0300
Subject: [PATCH 12/18] Add Objective-C runtime scan fallback for Swift native
 classes

---
 .../com/codename1/builders/IPhoneBuilder.java  | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
index f3fed6879b..3ce2ed7f16 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
@@ -1097,8 +1097,10 @@ public void usesClassMethod(String cls, String method) {
                 String classNameWithUnderscores = currentNative.getName().replace('.', '_');
                 String mSourceFile = "#include \"xmlvm.h\"\n"
                         + "#include \"java_lang_String.h\"\n"
+                        + "#include \n"
                         + "#import \"CodenameOne_GLViewController.h\"\n"
                         + "#import \n"
+                        + "#import \n"
                         + newVMInclude
                         + "#include \"" + classNameWithUnderscores + "ImplCodenameOne.h\"\n\n"
                         + "static id cn1_createNativeInterfacePeer(NSString* className) {\n"
@@ -1124,6 +1126,22 @@ public void usesClassMethod(String cls, String method) {
                         + "        }\n"
                         + "    }\n"
                         + "    if(cls == Nil) {\n"
+                        + "        unsigned int classCount = 0;\n"
+                        + "        Class *classList = objc_copyClassList(&classCount);\n"
+                        + "        NSString* dottedSuffix = [@\".\" stringByAppendingString:className];\n"
+                        + "        for(unsigned int i = 0; i < classCount; i++) {\n"
+                        + "            NSString* runtimeName = [NSString stringWithUTF8String:class_getName(classList[i])];\n"
+                        + "            if([runtimeName isEqualToString:className] || [runtimeName hasSuffix:dottedSuffix] || [runtimeName hasSuffix:className]) {\n"
+                        + "                cls = classList[i];\n"
+                        + "                NSLog(@\"[CN1] Resolved native interface class %@ via runtime scan as %@\", className, runtimeName);\n"
+                        + "                break;\n"
+                        + "            }\n"
+                        + "        }\n"
+                        + "        if(classList != NULL) {\n"
+                        + "            free(classList);\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "    if(cls == Nil) {\n"
                         + "        NSLog(@\"[CN1] Failed to find native interface class %@. Tried: %@\", className, candidates);\n"
                         + "        return nil;\n"
                         + "    }\n"

From a316764f5f11ea2b004a41929af7c0495535bb2d Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 13:14:14 +0300
Subject: [PATCH 13/18] Fail iOS UI test script when swift diag status is not
 OK

---
 scripts/run-ios-ui-tests.sh | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/scripts/run-ios-ui-tests.sh b/scripts/run-ios-ui-tests.sh
index a6328ceb2e..cd41b858d3 100755
--- a/scripts/run-ios-ui-tests.sh
+++ b/scripts/run-ios-ui-tests.sh
@@ -667,6 +667,17 @@ xcrun simctl spawn "$SIM_DEVICE_ID" \
   --predicate '(composedMessage CONTAINS "CN1SS") OR (eventMessage CONTAINS "CN1SS")' \
   > "$FALLBACK_LOG" 2>/dev/null || true
 
+SWIFT_DIAG_LINE="$( (grep -h "CN1SS:INFO:swift_diag_status=" "$TEST_LOG" "$FALLBACK_LOG" || true) | tail -n 1 )"
+if [ -n "$SWIFT_DIAG_LINE" ]; then
+  ri_log "Detected swift diagnostic status line: $SWIFT_DIAG_LINE"
+  if ! echo "$SWIFT_DIAG_LINE" | grep -q "swift_diag_status=OK "; then
+    ri_log "STAGE:SWIFT_DIAG_FAILED -> $SWIFT_DIAG_LINE"
+    exit 13
+  fi
+else
+  ri_log "STAGE:SWIFT_DIAG_MISSING -> No swift_diag_status marker found"
+fi
+
 if [ -n "$SIM_DEVICE_ID" ]; then
   xcrun simctl terminate "$SIM_DEVICE_ID" "$BUNDLE_IDENTIFIER" >/dev/null 2>&1 || true
 fi

From acea4ec3358c5080af942c7da7f12786b5072481 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 13:23:54 +0300
Subject: [PATCH 14/18] Add Objective-C shim delegating Swift native interface
 implementation

---
 ...s_hellocodenameone_SwiftKotlinNativeImpl.h |  9 +++++
 ...s_hellocodenameone_SwiftKotlinNativeImpl.m | 37 +++++++++++++++++++
 ...llocodenameone_SwiftKotlinNativeImpl.swift |  5 ++-
 3 files changed, 49 insertions(+), 2 deletions(-)
 create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h
 create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m

diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h
new file mode 100644
index 0000000000..208e61f7c1
--- /dev/null
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h
@@ -0,0 +1,9 @@
+#import 
+
+@interface com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl : NSObject
+
+-(NSString*)implementationLanguage;
+-(NSString*)diagnostics;
+-(BOOL)isSupported;
+
+@end
diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
new file mode 100644
index 0000000000..c202b4a69a
--- /dev/null
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
@@ -0,0 +1,37 @@
+#import "com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h"
+
+@implementation com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl
+
+-(id)getBridgeInstance {
+    Class bridgeClass = NSClassFromString(@"CN1SwiftKotlinNativeBridge");
+    if (bridgeClass == Nil) {
+        return nil;
+    }
+    return [[bridgeClass alloc] init];
+}
+
+-(NSString*)implementationLanguage {
+    id bridge = [self getBridgeInstance];
+    if (bridge != nil && [bridge respondsToSelector:@selector(implementationLanguage)]) {
+        return [bridge implementationLanguage];
+    }
+    return @"swift-bridge-missing";
+}
+
+-(NSString*)diagnostics {
+    id bridge = [self getBridgeInstance];
+    if (bridge != nil && [bridge respondsToSelector:@selector(diagnostics)]) {
+        return [bridge diagnostics];
+    }
+    return @"ios-swift-bridge-missing";
+}
+
+-(BOOL)isSupported {
+    id bridge = [self getBridgeInstance];
+    if (bridge != nil && [bridge respondsToSelector:@selector(isSupported)]) {
+        return [bridge isSupported];
+    }
+    return NO;
+}
+
+@end
diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
index 27c7209d84..667fd23d12 100644
--- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.swift
@@ -1,7 +1,8 @@
 import Foundation
 
-@objc(com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl)
-class com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl: NSObject {
+@objc(CN1SwiftKotlinNativeBridge)
+@objcMembers
+public class CN1SwiftKotlinNativeBridge: NSObject {
     @objc func implementationLanguage() -> String {
         return "swift"
     }

From 97cc0c6cbcd5f469d3910fa66a82cf925855e621 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 17:19:56 +0300
Subject: [PATCH 15/18] Scan runtime class list for Swift bridge in iOS shim

---
 ...s_hellocodenameone_SwiftKotlinNativeImpl.m | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
index c202b4a69a..7a27226bb9 100644
--- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
@@ -1,10 +1,30 @@
 #import "com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h"
+#import 
+#include 
 
 @implementation com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl
 
 -(id)getBridgeInstance {
     Class bridgeClass = NSClassFromString(@"CN1SwiftKotlinNativeBridge");
     if (bridgeClass == Nil) {
+        unsigned int classCount = 0;
+        Class *classList = objc_copyClassList(&classCount);
+        NSString *targetName = @"CN1SwiftKotlinNativeBridge";
+        NSString *dottedSuffix = [@".CN1SwiftKotlinNativeBridge" copy];
+        for (unsigned int i = 0; i < classCount; i++) {
+            NSString *runtimeName = [NSString stringWithUTF8String:class_getName(classList[i])];
+            if ([runtimeName isEqualToString:targetName] || [runtimeName hasSuffix:dottedSuffix] || [runtimeName hasSuffix:targetName]) {
+                bridgeClass = classList[i];
+                NSLog(@"[CN1] Found Swift bridge class as %@", runtimeName);
+                break;
+            }
+        }
+        if (classList != NULL) {
+            free(classList);
+        }
+    }
+    if (bridgeClass == Nil) {
+        NSLog(@"[CN1] Swift bridge class CN1SwiftKotlinNativeBridge was not found");
         return nil;
     }
     return [[bridgeClass alloc] init];

From e5204709d75f9a94961a6ee2fa77d7460b085152 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 18:08:21 +0300
Subject: [PATCH 16/18] Use generated Swift header for direct bridge binding on
 iOS

---
 ...s_hellocodenameone_SwiftKotlinNativeImpl.m | 27 ++-----------------
 1 file changed, 2 insertions(+), 25 deletions(-)

diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
index 7a27226bb9..e3730b82d5 100644
--- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
@@ -1,33 +1,10 @@
 #import "com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h"
-#import 
-#include 
+#import "HelloCodenameOne-Swift.h"
 
 @implementation com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl
 
 -(id)getBridgeInstance {
-    Class bridgeClass = NSClassFromString(@"CN1SwiftKotlinNativeBridge");
-    if (bridgeClass == Nil) {
-        unsigned int classCount = 0;
-        Class *classList = objc_copyClassList(&classCount);
-        NSString *targetName = @"CN1SwiftKotlinNativeBridge";
-        NSString *dottedSuffix = [@".CN1SwiftKotlinNativeBridge" copy];
-        for (unsigned int i = 0; i < classCount; i++) {
-            NSString *runtimeName = [NSString stringWithUTF8String:class_getName(classList[i])];
-            if ([runtimeName isEqualToString:targetName] || [runtimeName hasSuffix:dottedSuffix] || [runtimeName hasSuffix:targetName]) {
-                bridgeClass = classList[i];
-                NSLog(@"[CN1] Found Swift bridge class as %@", runtimeName);
-                break;
-            }
-        }
-        if (classList != NULL) {
-            free(classList);
-        }
-    }
-    if (bridgeClass == Nil) {
-        NSLog(@"[CN1] Swift bridge class CN1SwiftKotlinNativeBridge was not found");
-        return nil;
-    }
-    return [[bridgeClass alloc] init];
+    return [[CN1SwiftKotlinNativeBridge alloc] init];
 }
 
 -(NSString*)implementationLanguage {

From 31c300bd5bd8dd0b5fdc6bf73c0efabbc6fb1da3 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 19:33:46 +0300
Subject: [PATCH 17/18] Ensure Swift files are in compile phase for iOS main
 target

---
 .../com/codename1/builders/IPhoneBuilder.java | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
index 3ce2ed7f16..16ba2a76e7 100644
--- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
+++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java
@@ -1911,6 +1911,25 @@ public void usesClassMethod(String cls, String method) {
                             + "  puts \"Backtrace:\\n\\t#{e.backtrace.join(\"\\n\\t\")}\"\n"
                             + "  puts 'An error occurred recreating schemes, but the build still might work...'\n"
                             + "end\n"
+                            + "begin\n"
+                            + "  main_target = xcproj.targets.find{|e| e.name==main_class_name}\n"
+                            + "  if main_target\n"
+                            + "    swift_refs = xcproj.files.select{|f| f.path && f.path.end_with?('.swift')}\n"
+                            + "    swift_refs.each do |ref|\n"
+                            + "      unless main_target.source_build_phase.files_references.include?(ref)\n"
+                            + "        main_target.source_build_phase.add_file_reference(ref, true)\n"
+                            + "      end\n"
+                            + "      main_target.resources_build_phase.files.each do |bf|\n"
+                            + "        if bf.file_ref == ref\n"
+                            + "          main_target.resources_build_phase.remove_build_file(bf)\n"
+                            + "        end\n"
+                            + "      end\n"
+                            + "    end\n"
+                            + "  end\n"
+                            + "rescue => e\n"
+                            + "  puts \"Error while correcting Swift build phases: #{$!}\"\n"
+                            + "  puts \"Backtrace:\\n\\t#{e.backtrace.join(\"\\n\\t\")}\"\n"
+                            + "end\n"
                             + deploymentTargetStr
                             + appExtensionsBuilder.toString();
                     File hooksDir = new File(tmpFile, "hooks");

From d561375511cd99fb438df24144878adc56dda0c7 Mon Sep 17 00:00:00 2001
From: liannacasper <67953602+liannacasper@users.noreply.github.com>
Date: Thu, 2 Apr 2026 21:01:37 +0300
Subject: [PATCH 18/18] Remove hardcoded Swift header include from iOS native
 shim

---
 ...s_hellocodenameone_SwiftKotlinNativeImpl.m | 27 +++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
index e3730b82d5..7a27226bb9 100644
--- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
+++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.m
@@ -1,10 +1,33 @@
 #import "com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl.h"
-#import "HelloCodenameOne-Swift.h"
+#import 
+#include 
 
 @implementation com_codenameone_examples_hellocodenameone_SwiftKotlinNativeImpl
 
 -(id)getBridgeInstance {
-    return [[CN1SwiftKotlinNativeBridge alloc] init];
+    Class bridgeClass = NSClassFromString(@"CN1SwiftKotlinNativeBridge");
+    if (bridgeClass == Nil) {
+        unsigned int classCount = 0;
+        Class *classList = objc_copyClassList(&classCount);
+        NSString *targetName = @"CN1SwiftKotlinNativeBridge";
+        NSString *dottedSuffix = [@".CN1SwiftKotlinNativeBridge" copy];
+        for (unsigned int i = 0; i < classCount; i++) {
+            NSString *runtimeName = [NSString stringWithUTF8String:class_getName(classList[i])];
+            if ([runtimeName isEqualToString:targetName] || [runtimeName hasSuffix:dottedSuffix] || [runtimeName hasSuffix:targetName]) {
+                bridgeClass = classList[i];
+                NSLog(@"[CN1] Found Swift bridge class as %@", runtimeName);
+                break;
+            }
+        }
+        if (classList != NULL) {
+            free(classList);
+        }
+    }
+    if (bridgeClass == Nil) {
+        NSLog(@"[CN1] Swift bridge class CN1SwiftKotlinNativeBridge was not found");
+        return nil;
+    }
+    return [[bridgeClass alloc] init];
 }
 
 -(NSString*)implementationLanguage {