
Всем привет! С вами Анна Жаркова, руководитель практики мобильной разработки ГК Юзтех, автор книги «Kotlin Multiplatform на практике» и человек, который уже который год следит за тем, как две мобильные экосистемы (Apple и Android) медленно, но верно ползут друг к другу. Сегодня мы с вами обсудим наработки от Apple. Какую технологию они представили еще в 2025 году, что изменилось со времени, как Apple выпустили официальный релиз Swift SDK для Android. А также мы посмотрим, как написать настоящее приложение, где вся бизнес-логика живёт на Swift, а UI на Compose.
Но давайте по порядку.
Июнь 2025 года, WWDC. По традиции Apple проводят большую презентацию и, среди прочего, громко анонсируют инструмент SwiftJava, который был призван не столько навести мост между двумя языками, сколько сделать портирование с одной технологии на другую абсолютно лёгким и бесшовным. С помпой рассказали, как портировали пару Java-библиотек под Swift и как хорошо это работает.
В пакет решений вошли 2 библиотеки:
JavaKit — набор Swift-макросов и инструментов, которые генерируют исходники Swift для вызова Java-кода. То есть берём Java-библиотеку и подключаем её к коду на Swift.
SwiftKit — обратная библиотека, которая работает под Java и позволяет вызывать Swift-типы из Java. То есть берём Swift-библиотеку и используем её на стороне Java (и, как следствие, на Android).
По заверению Apple, всё это позволяет буквально за пару действий превратить Java-библиотеку в библиотеку на Swift, и обратно.
Ниже представлен пример, что получается при генерации Swift из Java класса:
package com.example; public class Greeting { public static String greet(String name) { return "Hello, " + name + "!"; } }
Кстати, для демонстрации инженеры Apple брали самый простой код.
Собственно результат работы Java-to-Swift генератора:
// Auto-generated by Java-to-Swift wrapper generator. import CSwiftJavaJNI import SwiftJava @JavaClass("com.example.Greeting") open class Greeting: JavaObject { @JavaMethod @_nonoverride public convenience init(environment: JNIEnvironment? = nil) } extension JavaClass<Greeting> { @JavaStaticMethod public func greet(_ arg0: String) -> String }
Java-класс com.example.Greeting будет теперь доступен из Swift как обычный Swift-тип, и работать с ним можно так же: инициализируем его инстанс, вызываем методы, а под капотом всё уходит в JNI. Это и есть та самая «магия WWDC».
Впечатляет. Но остается вопрос, а для чего все это нужно?
Официальная версия компании Apple : миграция приложения Password Monitoring (мониторинг утечек паролей)с Java на Swift. Независимо от статистики использования данной утилиты выглядит несколько неубедительно. В отличие от версии распространения влияния Swift за пределы экосистемы Apple и превращения из языка мультиплатформы в кроссплатформу.

Для чего же нужна данная технология:
создание общего код без другого кроссплатформенного слоя;
развитие сближения Kotlin и Swift;
интеграция Swift-библиотек в Java/Android;
интеграция Java-библиотек в Swift (как вариант).
Прежде чем браться за новый инструмент, давайте вспомним, как вообще связываются языки в существующих кроссплатформенных решениях. Без этого будет сложно понять, где Apple пошли своим путём, а где повторили чужие ошибки. Сделаем мы это, конечно же, на примере Kotlin Multiplatform.
Как мы помним, общий модуль кода не монолитный: он состоит из общей части и платформенных частей, для каждой из которых используется своя версия Kotlin (Kotlin/JVM для Java и Android и Kotlin/Native для iOS и десктопов). Для корректной связки кода под капотом, есть механизм expect/actual, который позволяет переопределить реализацию конструкций кода под разные таргеты.
Если говорить про Kotlin/JVM, всё просто: Kotlin вышел из Java, это JVM-язык, и интероп Kotlin↔Java практически полный. А вот с Kotlin/Native интереснее. Связь между Kotlin/Native и Swift идёт через Objective-C: ваш код преобразуется в заголовочные файлы Objective-C, которые затем связываются со Swift через специальные bridge

Kotlin взаимодействует с C через JNI. Кто работал с JNI, знает: это способ подключить низкоуровневую логику, то есть готовые библиотеки на C/C++ (криптография, OpenGL и т.д.). Для интеграции в Android приложения используется Android NDK. Ниже представлена структура модуля приложения с подключением JNI кода:
my-app/ ├── cpp/ │ └── native-lib.cpp # наша логика на C++ ├── CMakeLists.txt # где искать cpp-шники └── java/com/example/app/ └── NativeBridge.java # class с native-методами
public class NativeBridge { static { System.loadLibrary("native-lib"); } public native String stringFromJNI(); }
val bridge = NativeBridge() println(bridge.stringFromJNI())
Со стороны C/C++ для каждого native-метода нужна декларация имен типов вида Java_пакет_класс_метод. Как мы понимаем, руками такое писать будет сложно и даже неприятно:
// nativeutils.h #include <jni.h> #ifndef _Included_com_example_jni_NativeUtils #define _Included_com_example_jni_NativeUtils #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_jni_NativeUtils * Method: sayHello * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_jni_NativeUtils_sayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif

Apple решили взять JNI-подход на вооружение. Их официальный рекламный лозунг гласит: «Generates hard-to-get-right code», то есть генерирует код, который сложно написать правильно это не преувеличение: корректный JNI-код действительно сложно писать вручную. Вопрос в другом: что в итоге генерируется и насколько это пригодно к использованию. Посмотрим в репозиторий проекта: github.com/swiftlang/swift-java.
Инструмент активно развивается. Баги и проблемы, а также тикеты на доработку и поддержку, постоянно заводятся, правятся, заливаются и сменяются новыми. На данный момент SwiftJava довольно рискованно использованном в настоящей прод-разработке.
Внутри SwiftJava есть утилита jextract, она же основной рабочий инструмент, которая и запускает генерацию hard-to-get-right code.
У него два направления работы:
JavaKit (Java → Swift): генерация Swift-обёртки из Java-кода, чтобы вызывать Java из Swift.
SwiftKit (Swift → Java): генерация Java-биндингов из Swift, чтобы вызывать Swift из Java и из Android.
Также есть два режима поддержки взаимодействия Java с нативным кодом:
swift-java jextract --mode=ffm (режим по умолчанию) swift-java jextract --mode=jni
FFM (Foreign Function & Memory API) требует Swift 6.2+ и JDK 25 (по факту работает начиная с JDK 22).
JNI требует Swift 6.2+ и JDK 7+. Это старый добрый JNI.
В общих чертах алгоритм выглядит так: вы запускаете jextract по вашему Swift-коду, и он генерирует Java-биндинги — Java-классы, которые зеркалируют Swift-типы и объявляют native-методы. На стороне Swift генерируются thunks: функции с атрибутом @_cdecl, которые становятся точками входа для JNI. Дополнительно создаются linker export lists: скрипты, которые экспортируют из итоговой shared library только нужные JNI-символы.
# ручной режим: указываем режим API и путь к исходникам swift run jextract --mode jni path/to/Sources/MyLibrary
Посмотрим на небольшом примере. Возьмем простой Swift-класс:
public class MySwiftClass { public let x: Int64 public init(x: Int64) { self.x = x } public func printMe() { print("\(self.x)"); } }
После запуска jextract получаем Java-класс, который реализует JNISwiftInstance: публичные методы представляют собой тонкие обёртки, а внизу объявлены native-методы $init, $getX, $printMe, которые и есть точки входа в среду исполнения Swift:
public final class MySwiftClass implements JNISwiftInstance { public static MySwiftClass init(long x, long y, SwiftArena swiftArena$) { return MySwiftClass.wrapMemoryAddressUnsafe(MySwiftClass.$init(x, y), swiftArena$); } public long getX() { return MySwiftClass.$getX(this.$memoryAddress()); } public void printMe() { MySwiftClass.$printMe(this.$memoryAddress()); } private static native long $init(long x, long y); private static native long $getX(long self); private static native void $printMe(long self); }
Возвращаясь к режиму взаимодействия с нативным кодом, зачем нужны JNI и FFM? FFM представляет собой новый стандартJava Foreign Function & Memory API (JEP 454), который пришёл на смену JNI и позволяет вызывать нативный код без JNI-прослойки и без классического копирования данных.
JNI |
FFM |
|
|---|---|---|
Java |
11+ (для jextract: JDK 7+) |
22+ (заявлено 25) |
Android |
Совместим |
Не поддерживается |
Копирование данных |
Больше копий |
Лучше поддержка нативной памяти |
Набор возможностей |
Шире |
«Базовый» набор |
Но если мы работаем с Android, то нам FFM не подойдет из-за отсутствия поддержки версии SDK. Будем работать с JNI.
Также стоит упомянуть, что как и в случае взаимодействия Swift и Kotlin, при конвертации Swift <-> Java не все конструкции языка и SDK поддерживаются. Например, исключения, коллекции, перечисления (enum), дженерики, структурированная многопоточность Swift (async/await работал только с JNI, акторы не поддерживались). Сейчас большинство этих проблем решены. Например, async/await конвертируется в CompletableFuture, а для управления памятью используется механизм SwiftArena. Но об этом позже. В следующей части мы рассмотрим, как можно написать приложение на Swift для Android.
Полезные статьи
Swift SDK for Android — Getting Started: https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-started.html
Nightly Swift SDK for Android: https://www.swift.org/blog/nightly-swift-sdk-for-android/
SwiftJava (репозиторий): https://github.com/swiftlang/swift-java
DeepWiki по swift-java: https://deepwiki.com/swiftlang/swift-java/
FFM API (JEP 454): https://docs.oracle.com/en/java/javase/21/core/foreign-function-and-memory-api.html
Примеры для Android: https://github.com/swiftlang/swift-android-examples
Семпл из статьи: https://gitverse.ru/azharkova/swift-droid-news-sample
Skip.Tools: https://skip.tools/
Комментарии (2)

ArtyomOchkin
19.10.2026 06:58В интересные времена живём... Недавно была новость о портировании самой iOS (16) на Андроид со снапом - Xiaomi redmi Note Pro 10 и Note 12 4g, к открытому ядру XNU авторами были применены значительные патчи и уже работает тач и экран, и вроде как физические кнопки громкости. Пока скорее очень любопытный рабочий PoC, но всё равно интересно.
Android1983
Производительность данного монстра в студию.
На моём чуде-юде с 4 Гб ОЗУ запуститься?