In my Kotlin multiplatform project i heavily use namespaces. Also since i'm using MVP i have similar classnames. eg:
com.company.project.usecase1.Model
com.company.project.usecase1.View
com.company.project.usecase1.Presenter
com.company.project.usecase2.Model
com.company.project.usecase2.View
com.company.project.usecase2.Presenter
Now i want to use it in iOS app (written in Swift).
So i've added iOS target to build.gradle
- everything like in example. I was able to generate Cocoapod (gradlew podspec
) and use it Swift app.
Related part of build.gradle
:
version = "$rootProject.module_version"
kotlin {
cocoapods {
summary = "App MVP of NotesClientApp"
homepage = "some url"
}
}
However since Swift does not have namespaces and classnames look similar generated obj-c wrapper looks ugly: it uses artificial mutations (underline) in classnames just to distinguish the names, eg.
__attribute__((swift_name("Presenter__")))
@protocol App_mvpPresenter__ <App_mvpBasePresenter>
@required
@end;
__attribute__((swift_name("View__")))
@protocol App_mvpView__ <App_mvpBaseView>
@required
- (void)showValidationErrorError:(App_mvpKotlinException *)error __attribute__((swift_name("showValidationError(error:)")));
- (void)showNotesList __attribute__((swift_name("showNotesList()")));
@property NSString *host __attribute__((swift_name("host")));
@property NSString *port __attribute__((swift_name("port")));
@end;
__attribute__((swift_name("Model__")))
@interface App_mvpModel__ : KotlinBase
- (instancetype)initWith_host:(NSString * _Nullable)_host _port:(App_mvpUInt * _Nullable)_port __attribute__((swift_name("init(_host:_port:)"))) __attribute__((objc_designated_initializer));
- (void)updateHost:(NSString *)host port:(uint32_t)port __attribute__((swift_name("update(host:port:)")));
@property (readonly) NSString * _Nullable host __attribute__((swift_name("host")));
@property (readonly) App_mvpUInt * _Nullable port __attribute__((swift_name("port")));
@property id<App_mvpPresenter__> _Nullable presenter __attribute__((swift_name("presenter")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("PresenterImpl__")))
@interface App_mvpPresenterImpl__ : KotlinBase <App_mvpPresenter__>
- (instancetype)initWithModel:(App_mvpModel__ *)model __attribute__((swift_name("init(model:)"))) __attribute__((objc_designated_initializer));
- (void)attachViewView:(id<App_mvpView__>)view __attribute__((swift_name("attachView(view:)")));
- (void)onModelChanged __attribute__((swift_name("onModelChanged()")));
- (void)onViewChanged __attribute__((swift_name("onViewChanged()")));
- (void)onViewDetached __attribute__((swift_name("onViewDetached()")));
@property (readonly) App_mvpModel__ *model __attribute__((swift_name("model")));
@end;
I guess there should be some possibility to adjust the behaviour: add some config file or annotations for naming adjustments.
Any other possibility to rename classes in Obj-c/Swift not related to Kotlin? i've tried to use Swift typealiases, but got "typealias invalid redeclaration" error only.
Any thoughts?
PS. Also i can see module name (app-mvp
) works as prefix, eg. classname App_mvpView__
- any possibility to adjust generated Framework name without changing of Gradle module name (since i still want to use proper JVM build artifact names: app-mvp-jvm.jar
)?
PPS. I do understand it could be easier just rename classes to make them unique in all namespaces, but anyway.
At this time it isn't possible to override the class names for native. JavaScript MPP has an annotation called JSName
so I wouldn't rule it out in the future. To change your framework name you can simply set the baseName
value in your framework
configuration under targets.
fromPreset(iOSTarget, 'ios') {
binaries {
framework {
baseName = "MyFrameworkName"
}
}
}
If you're using the packForXcode
task you will likely need to update it to find your new framework. Mine looks like this:
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = (project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG').split("_").first()
inputs.property "mode", mode
def bin = kotlin.targets.ios.compilations.main.target.binaries.findFramework("", mode)
dependsOn bin.linkTask
from bin.outputDirectory
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
setExecutable(true)
}
}
}
Mine also has a customization to support multiple project schemes in Xcode. I have one for each environment (dev, test, prod) the app can point to.