Kotlin/Native в виде фреймворка Apple — руководство
Kotlin/Native обеспечивает двустороннюю совместимость со Swift/Objective-C. Вы можете использовать фреймворки и библиотеки Objective-C в коде Kotlin, а модули Kotlin — в коде Swift/Objective-C.
Kotlin/Native поставляется с набором предварительно импортированных системных фреймворков; также можно импортировать существующий фреймворк и использовать его из Kotlin. В этом руководстве вы узнаете, как создать собственный фреймворк и использовать код Kotlin/Native в приложениях Swift/Objective-C для macOS и iOS.
В этом руководстве вы научитесь:
Для создания фреймворка Kotlin можно использовать командную строку напрямую или с помощью файла сценария (например, файла .sh или .bat). Однако такой подход плохо подходит для крупных проектов с сотнями файлов и библиотек. Система сборки упрощает процесс, загружая и кэшируя двоичные файлы компилятора Kotlin/Native и библиотеки с транзитивными зависимостями, а также запуская компилятор и тесты. Kotlin/Native может использовать систему сборки Gradle с помощью плагина Kotlin Multiplatform.
Создание библиотеки Kotlin
Компилятор Kotlin/Native может создать из кода Kotlin фреймворк для macOS и iOS. Созданный фреймворк содержит все объявления и двоичные файлы, необходимые для его использования со Swift/Objective-C.
Сначала создадим библиотеку Kotlin:
-
В каталоге
src/nativeMain/kotlinсоздайте файлlib.ktс содержимым библиотеки:package example object Object { val field = "A" } interface Interface { fun iMember() {} } class Clazz : Interface { fun member(p: Int): ULong? = 42UL } fun forIntegers(b: Byte, s: UShort, i: Int, l: ULong?) { } fun forFloats(f: Float, d: Double?) { } fun strings(str: String?) : String { return "That is '$str' from C" } fun acceptFun(f: (String) -> String?) = f("Kotlin/Native rocks!") fun supplyFun() : (String) -> String? = { "$it is cool!" } -
Обновите файл сборки Gradle
build.gradle(.kts)следующим образом:import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget plugins { kotlin("multiplatform") version "2.4.20" } repositories { mavenCentral() } kotlin { iosArm64() // macosArm64() // iosSimulatorArm64() targets.withType<KotlinNativeTarget>().configureEach { binaries { framework { baseName = "Demo" } } } } tasks.wrapper { gradleVersion = "9.7.0" distributionType = Wrapper.DistributionType.ALL }import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget plugins { id 'org.jetbrains.kotlin.multiplatform' version '2.4.20' } repositories { mavenCentral() } kotlin { iosArm64() // macosArm64() // iosSimulatorArm64() targets.withType(KotlinNativeTarget).configureEach { binaries { framework { baseName = "Demo" } } } } wrapper { gradleVersion = "9.7.0" distributionType = "ALL" }Блок
binaries {}настраивает проект для создания динамической или общей библиотеки.Kotlin/Native поддерживает цели
iosArm64иiosSimulatorArm64для iOS, а также цельmacosArm64для macOS. Поэтому вместоiosArm64()можно использовать соответствующую функцию Gradle для целевой платформы:Целевая платформа/устройство
Функция Gradle
macOS ARM64
macosArm64()iOS ARM64
iosArm64()Симулятор iOS (ARM64)
iosSimulatorArm64()Сведения о других поддерживаемых целевых платформах Apple см. в разделе Поддержка целевых платформ Kotlin/Native.
-
Чтобы собрать фреймворк, запустите задачу Gradle
linkDebugFramework<YourTargetName>в IDE или выполните в терминале команду, например:./gradlew linkDebugFrameworkIosArm64
В результате сборки фреймворк будет создан в каталоге build/bin/<yourTargetName>/debugFramework.
Заголовки сгенерированного фреймворка
Каждый вариант фреймворка содержит файл заголовка. Заголовки не зависят от целевой платформы. Файлы заголовков содержат определения вашего кода Kotlin и несколько общих объявлений Kotlin. Посмотрим, что в них находится.
Объявления среды выполнения Kotlin/Native
Откройте файл заголовка Demo.h в каталоге build/bin/<yourTargetName>/debugFramework/Demo.framework/Headers. Ознакомьтесь с объявлениями среды выполнения Kotlin:
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((swift_name("KotlinBase")))
@interface DemoBase : NSObject
- (instancetype)init __attribute__((unavailable));
+ (instancetype)new __attribute__((unavailable));
+ (void)initialize __attribute__((objc_requires_super));
@end
@interface DemoBase (DemoBaseCopying) <NSCopying>
@end
__attribute__((swift_name("KotlinMutableSet")))
@interface DemoMutableSet<ObjectType> : NSMutableSet<ObjectType>
@end
__attribute__((swift_name("KotlinMutableDictionary")))
@interface DemoMutableDictionary<KeyType, ObjectType> : NSMutableDictionary<KeyType, ObjectType>
@end
@interface NSError (NSErrorDemoKotlinException)
@property (readonly) id _Nullable kotlinException;
@end
В Swift/Objective-C у классов Kotlin есть базовый класс KotlinBase, который наследуется от класса NSObject. Также имеются обертки для коллекций и исключений. Большинство типов коллекций сопоставляются с похожими типами коллекций в Swift/Objective-C:
Kotlin |
Swift |
Objective-C |
|---|---|---|
List |
Array |
NSArray |
MutableList |
NSMutableArray |
NSMutableArray |
Set |
Set |
NSSet |
MutableSet |
NSMutableSet |
NSMutableSet |
Map |
Dictionary |
NSDictionary |
MutableMap |
NSMutableDictionary |
NSMutableDictionary |
Числа Kotlin и NSNumber
Следующая часть файла Demo.h содержит сопоставления типов между числовыми типами Kotlin/Native и NSNumber. В Objective-C базовый класс называется DemoNumber, а в Swift — KotlinNumber. Он наследуется от NSNumber.
Для каждого числового типа Kotlin предусмотрен соответствующий предварительно объявленный дочерний класс:
Kotlin |
Swift |
Objective-C |
Простой тип |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Для каждого числового типа предусмотрен метод класса, создающий новый экземпляр из соответствующего простого типа. Также есть метод экземпляра, позволяющий извлечь обратно простое значение. Схематически все такие объявления выглядят так:
__attribute__((swift_name("Kotlin__TYPE__")))
@interface Demo__TYPE__ : DemoNumber
- (instancetype)initWith__TYPE__:(__CTYPE__)value;
+ (instancetype)numberWith__TYPE__:(__CTYPE__)value;
@end;
Здесь __TYPE__ — одно из названий простых типов, а __CTYPE__ — соответствующий тип Objective-C, например initWithChar(char).
Эти типы используются для сопоставления упакованных числовых типов Kotlin со Swift/Objective-C. В Swift можно вызвать конструктор, чтобы создать экземпляр, например KotlinLong(value: 42).
Классы и объекты из Kotlin
Посмотрим, как class и object сопоставляются со Swift/Objective-C. Сгенерированный файл Demo.h содержит точные определения для Class, Interface и Object:
__attribute__((swift_name("Interface")))
@protocol DemoInterface
@required
- (void)iMember __attribute__((swift_name("iMember()")));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("Clazz")))
@interface DemoClazz : DemoBase <DemoInterface>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (DemoULong * _Nullable)memberP:(int32_t)p __attribute__((swift_name("member(p:)")));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("Object")))
@interface DemoObject : DemoBase
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
+ (instancetype)object __attribute__((swift_name("init()")));
@property (class, readonly, getter=shared) DemoObject *shared __attribute__((swift_name("shared")));
@property (readonly) NSString *field __attribute__((swift_name("field")));
@end
Атрибуты Objective-C в этом коде позволяют использовать фреймворк как из Swift, так и из Objective-C. Для Interface, Clazz и Object создаются соответственно DemoInterface, DemoClazz и DemoObject.
Interface преобразуется в @protocol, а class и object представлены как @interface. Префикс Demo образован от имени фреймворка. Тип возвращаемого значения с возможностью null ULong? преобразуется в DemoULong в Objective-C.
Глобальные объявления из Kotlin
Все глобальные функции Kotlin преобразуются в DemoLibKt в Objective-C и в LibKt в Swift, где Demo — это имя фреймворка, заданное параметром -output функции kotlinc-native:
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LibKt")))
@interface DemoLibKt : DemoBase
+ (NSString * _Nullable)acceptFunF:(NSString * _Nullable (^)(NSString *))f __attribute__((swift_name("acceptFun(f:)")));
+ (void)forFloatsF:(float)f d:(DemoDouble * _Nullable)d __attribute__((swift_name("forFloats(f:d:)")));
+ (void)forIntegersB:(int8_t)b s:(uint16_t)s i:(int32_t)i l:(DemoULong * _Nullable)l __attribute__((swift_name("forIntegers(b:s:i:l:)")));
+ (NSString *)stringsStr:(NSString * _Nullable)str __attribute__((swift_name("strings(str:)")));
+ (NSString * _Nullable (^)(NSString *))supplyFun __attribute__((swift_name("supplyFun()")));
@end
Тип Kotlin String и тип Objective-C NSString* сопоставляются напрямую. Аналогично, тип Unit из Kotlin сопоставляется с void. Примитивные типы сопоставляются напрямую. Типы-примитивы, не допускающие null, сопоставляются напрямую. Типы-примитивы, допускающие null, сопоставляются с типами Kotlin<TYPE>*, как показано в таблице. Включены обе функции высшего порядка — acceptFunF и supplyFun; они принимают блоки Objective-C.
Дополнительные сведения о сопоставлении типов см. в разделе Совместимость со Swift/Objective-C.
Сборка мусора и подсчет ссылок
В Swift и Objective-C используется автоматический подсчет ссылок (ARC). У Kotlin/Native есть собственный сборщик мусора, который также интегрирован с ARC Swift/Objective-C.
Неиспользуемые объекты Kotlin удаляются автоматически. Вам не нужно предпринимать дополнительные действия для управления временем жизни экземпляров Kotlin/Native из Swift или Objective-C.
Использование кода из Objective-C
Вызовем фреймворк из Objective-C. В каталоге фреймворка создайте файл main.m со следующим кодом:
#import <Foundation/Foundation.h>
#import <Demo/Demo.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
[DemoObject.shared field];
DemoClazz* clazz = [[ DemoClazz alloc] init];
[clazz memberP:42];
[DemoLibKt forIntegersB:1 s:1 i:3 l:[DemoULong numberWithUnsignedLongLong:4]];
[DemoLibKt forIntegersB:1 s:1 i:3 l:nil];
[DemoLibKt forFloatsF:2.71 d:[DemoDouble numberWithDouble:2.71]];
[DemoLibKt forFloatsF:2.71 d:nil];
NSString* ret = [DemoLibKt acceptFunF:^NSString * _Nullable(NSString * it) {
return [it stringByAppendingString:@" Kotlin is fun"];
}];
NSLog(@"%@", ret);
return 0;
}
}
Здесь вы вызываете классы Kotlin непосредственно из кода Objective-C. Для доступа к объекту Kotlin используется свойство класса <object name>.shared, которое позволяет получить единственный экземпляр объекта и вызвать его методы.
Для создания экземпляра класса Clazz используется распространенный шаблон. В Objective-C вызывается [[ DemoClazz alloc] init]. Для конструкторов без параметров также можно использовать [DemoClazz new].
Глобальные объявления из исходного кода Kotlin в Objective-C находятся в области видимости класса DemoLibKt. Все функции Kotlin преобразуются в методы класса этого класса.
Функция strings преобразуется в функцию Objective-C DemoLibKt.stringsStr, поэтому вы можете передать ей непосредственно NSString. Возвращаемое значение также доступно как NSString.
Использование кода из Swift
В созданном вами фреймворке есть вспомогательные атрибуты, упрощающие его использование со Swift. Преобразуем предыдущий пример на Objective-C в код Swift.
В каталоге фреймворка создайте файл main.swift со следующим кодом:
import Foundation
import Demo
let kotlinObject = Object.shared
let field = Object.shared.field
let clazz = Clazz()
clazz.member(p: 42)
LibKt.forIntegers(b: 1, s: 2, i: 3, l: 4)
LibKt.forFloats(f: 2.71, d: nil)
let ret = LibKt.acceptFun { "\($0) Kotlin is fun" }
if (ret != nil) {
print(ret!)
}
Между исходным кодом Kotlin и его версией на Swift есть несколько небольших различий. В Kotlin у любого объявления объекта есть только один экземпляр. Для доступа к этому единственному экземпляру используется синтаксис Object.shared.
Имена функций и свойств Kotlin переносятся без изменений. String из Kotlin преобразуется в String в Swift. Swift также скрывает упаковку NSNumber*. Кроме того, в Kotlin можно передать замыкание Swift и вызвать из Swift лямбда-функцию Kotlin.
Дополнительные сведения о сопоставлении типов см. в разделе Совместимость со Swift/Objective-C.
Подключение фреймворка к проекту iOS
Теперь вы можете подключить созданный фреймворк к проекту iOS в качестве зависимости. Настроить и автоматизировать процесс можно несколькими способами — выберите наиболее подходящий для вас:
Что дальше
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/apple-framework.html