Swift-friendly api generator for Kotlin/Native frameworks

Overview

moko-kswift
GitHub license Download kotlin-version

MOKO KSwift

KSwift it's gradle plugin for generation Swift-friendly API for Kotlin/Native framework.

Kotlin sealed interface/class to Swift enum

sealed classes compare

Kotlin extensions for K/N platform classes

extensions compare

Your own case

KSwift give you API for adding your own generator based on KLib metadata information.

Posts

Table of Contents

Features

  • API for extend logic for own cases - just implement your own ProcessorFeature
  • Reading of all exported klibs - you can generate swift additions to the api of external libraries
  • Kotlin sealed class/interface to Swift enum
  • Kotlin extensions for platform classes to correct extensions instead of additional class with static methods
  • Flexible filtration - select what you want to generate and what not

Requirements

  • Gradle version 6.0+
  • Kotlin 1.5.20

Installation

Plugin

Using legacy plugin application

root build.gradle

buildscript {
    repositories {
        mavenCentral()
        google()
        gradlePluginPortal()

        maven("https://jitpack.io")
    }
    dependencies {
        classpath("dev.icerock.moko:kswift-gradle-plugin:0.2.0")
    }
}

project where framework compiles build.gradle

plugins {
    id("dev.icerock.moko.kswift")
}

Using the plugins DSL

settings.gradle

pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()

        maven("https://jitpack.io")
    }
}

project where framework compiles build.gradle

plugins {
    id("dev.icerock.moko.kswift") version "0.2.0"
}

Runtime library

root build.gradle

allprojects {
    repositories {
        mavenCentral()
    }
}

project build.gradle

dependencies {
    commonMainApi("dev.icerock.moko:kswift-runtime:0.2.0") // if you want use annotations
}

Usage

Sealed classes/interfaces to Swift enum

Enable feature in project build.gradle:

kswift {
    install(dev.icerock.moko.kswift.plugin.feature.SealedToSwiftEnumFeature)
}

That's all - after this setup all sealed classes and sealed interfaces will be parsed by plugin and plugin will generate Swift enums for this classes.

For example if you have in your kotlin code:

sealed interface UIState<out T> {
    object Loading : UIState<Nothing>
    object Empty : UIState<Nothing>
    data class Data<T>(val value: T) : UIState<T>
    data class Error(val throwable: Throwable) : UIState<Nothing>
}

Then plugin will generate source code:

/**
 * selector: ClassContext/moko-kswift.sample:mpp-library-pods/com/icerockdev/library/UIState */
public enum UIStateKs<T : AnyObject> {

  case loading
  case empty
  case data(UIStateData<T>)
  case error(UIStateError)

  public init(_ obj: UIState) {
    if obj is shared.UIStateLoading {
      self = .loading
    } else if obj is shared.UIStateEmpty {
      self = .empty
    } else if let obj = obj as? shared.UIStateData<T> {
      self = .data(obj)
    } else if let obj = obj as? shared.UIStateError {
      self = .error(obj)
    } else {
      fatalError("UIStateKs not syncronized with UIState class")
    }
  }

}

For each generated entry in comment generated selector - value of this selector can be used for filter. By default all entries generated. But if generated code invalid (please report issue in this case) you can disable generation of this particular entry:

kswift {
    install(dev.icerock.moko.kswift.plugin.feature.SealedToSwiftEnumFeature) {
        filter = excludeFilter("ClassContext/moko-kswift.sample:mpp-library-pods/com/icerockdev/library/UIState")
    }
}

As alternative you can use includeFilter to explicit setup each required for generation entries:

kswift {
    install(dev.icerock.moko.kswift.plugin.feature.SealedToSwiftEnumFeature) {
        filter = includeFilter("ClassContext/moko-kswift.sample:mpp-library-pods/com/icerockdev/library/UIState")
    }
}

Extensions from platform classes

Enable feature in project build.gradle:

kswift {
    install(dev.icerock.moko.kswift.plugin.feature.PlatformExtensionFunctionsFeature)
}

That's all - after this setup all extension functions for classes from platform.* package will be correct swift code.

For example if you have in your kotlin code:

class CFlow<T>(private val stateFlow: StateFlow<T>) : StateFlow<T> by stateFlow

fun UILabel.bindText(coroutineScope: CoroutineScope, flow: CFlow<String>) {
    val label = this
    coroutineScope.launch {
        label.text = flow.value
        flow.collect { label.text = it }
    }
}

Then plugin will generate source code:

public extension UIKit.UILabel {
  /**
   * selector: PackageFunctionContext/moko-kswift.sample:mpp-library/com.icerockdev.library/Class(name=platform/UIKit/UILabel)/bindText/coroutineScope:Class(name=kotlinx/coroutines/CoroutineScope),flow:Class(name=com/icerockdev/library/CFlow)<Class(name=kotlin/String)> */
  public func bindText(coroutineScope: CoroutineScope, flow: CFlow<NSString>) {
    return UILabelExtKt.bindText(self, coroutineScope: coroutineScope, flow: flow)
  }
}

Selector from comment can be used for filters as in first example.

Implementation of own generator

First create buildSrc, if you don't. build.gradle will contains:

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.5.21"
}

repositories {
    mavenCentral()
    google()
    gradlePluginPortal()

    maven("https://jitpack.io")
}

dependencies {
    implementation("com.android.tools.build:gradle:7.1.0-alpha06")
    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21")
    implementation("dev.icerock.moko:kswift-gradle-plugin:0.2.0")
    implementation("com.github.icerockdev:swiftpoet:1.1.2")
    implementation("org.jetbrains.kotlinx:kotlinx-metadata-klib:0.0.1")
}

Then in buildSrc/src/main/kotlin create MyKSwiftGenerator:

import dev.icerock.moko.kswift.plugin.context.ClassContext
import dev.icerock.moko.kswift.plugin.feature.ProcessorContext
import dev.icerock.moko.kswift.plugin.feature.ProcessorFeature
import io.outfoxx.swiftpoet.DeclaredTypeName
import io.outfoxx.swiftpoet.ExtensionSpec
import io.outfoxx.swiftpoet.FileSpec

class MyKSwiftGenerator(filter: Filter<ClassContext>) : ProcessorFeature<ClassContext>(filter) {
    override fun doProcess(featureContext: ClassContext, processorContext: ProcessorContext) {
        val fileSpec: FileSpec.Builder = processorContext.fileSpecBuilder
        val frameworkName: String = processorContext.framework.baseName

        val classSimpleName = featureContext.clazz.name.substringAfterLast('/')

        fileSpec.addExtension(
            ExtensionSpec
                .builder(
                    DeclaredTypeName.typeName("$frameworkName.$classSimpleName")
                )
                .build()
        )
    }

    class Config(
        var filter: Filter<ClassContext> = Filter.Exclude(emptySet())
    )

    companion object : Factory<ClassContext, MyKSwiftGenerator, Config> {
        override fun create(block: Config.() -> Unit): MyKSwiftGenerator {
            val config = Config().apply(block)
            return MyKSwiftGenerator(config.filter)
        }
    }
}

in this example will be generated swift extension for each class in kotlin module. You can select required Context to got required info from klib metadata.

last step - enable feature in gradle:

kswift {
    install(MyKSwiftGenerator)
}

FAQ

Where destination directory for all generated sources?

Swift source code generates in same directory where compiles Kotlin/Native framework. In common case it directory build/bin/{iosArm64 || iosX64}/{debugFramework || releaseFramework}/{frameworkName}Swift.

Kotlin/Native cocoapods plugin (and also mobile-multiplatform cocoapods plugin by IceRock) will move this sources into fixed directory - build/cocoapods/framework/{frameworkName}Swift.

How to exclude generation of entries from some libraries?

kswift {
    excludeLibrary("{libraryName}")
}

How to generate entries only from specific libraries?

kswift {
    includeLibrary("{libraryName1}")
    includeLibrary("{libraryName2}")
}

Samples

More examples can be found in the sample directory.

Set Up Locally

Clone project and just open it. Gradle plugin attached to sample by gradle composite build, so you will see changes at each gradle build.

Contributing

All development (both new features and bug fixes) is performed in develop branch. This way master sources always contain sources of the most recently released version. Please send PRs with bug fixes to develop branch. Fixes to documentation in markdown files are an exception to this rule. They are updated directly in master.

The develop branch is pushed to master during release.

More detailed guide for contributers see in contributing guide.

License

Copyright 2021 IceRock MAG Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • kotlin 1.7.0 support

    kotlin 1.7.0 support

    @Alex009, thanks, sure, I managed to reproduce by adding kswift to the project, sample here gradle build is successful, but xcode build fails

    Originally posted by @aleksey-ho in https://github.com/icerockdev/moko-resources/issues/358#issuecomment-1183951009

    enhancement 
    opened by Alex009 6
  • Describe Xcode integration ways

    Describe Xcode integration ways

    How to integrate without cocoapods, directly to project - https://github.com/Alex009/moko-kswift-usage-sample/tree/regular-framework How to integrate with cocoapods - call kswift****podspec task, add generated podspec to project podfile (sample in moko-kswift use this way)

    documentation 
    opened by Alex009 5
  • No way to specify spec.ios.deployment_target for generated spec file

    No way to specify spec.ios.deployment_target for generated spec file

    Because there's no way to specify the iOS deployment target, the build defaults to the lowest support iOS version (I believe iOS 4.3) for the sharedSwift project. This results in the build failing with:

    <unknown>:0: error: Swift requires a minimum deployment target of iOS 7
    Command CompileSwiftSources failed with a nonzero exit code
    

    This can be worked around by forcing the target in the projects Podfile, but this is obviously not ideal.

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
          config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
        end
      end
    end
    

    Firstly it would be good to have a setting to be able to specify this. Although, would it make sense for a future version to use the cocoapods plugin configuration if available?

    enhancement 
    opened by mattmook 5
  • AtomicFu breaks reading metadata

    AtomicFu breaks reading metadata

    Not really a kswift issue but thought I worth raise it so it can be noted in kswift's readme to save someone else some time

    Basically, if your project uses AtomicFu (I'm using the latest, v0.16.2) then the below exception occurs. Sample app attached below.

    Error only shows with --info flag.

    library can't be read
    java.lang.IllegalStateException: metadata-jvm doesn't have any extensions for module fragment!
    	at kotlinx.metadata.jvm.impl.JvmMetadataExtensions.createModuleFragmentExtensions(JvmMetadataExtensions.kt:292)
    	at kotlinx.metadata.KmModuleFragment.<init>(nodes.kt:255)
    	at kotlinx.metadata.klib.KlibModuleMetadata$Companion.read(KlibModuleMetadata.kt:93)
    	at kotlinx.metadata.klib.KlibModuleMetadata$Companion.read$default(KlibModuleMetadata.kt:81)
    	at dev.icerock.moko.kswift.plugin.KotlinMetadataLibraryProvider$Companion.readLibraryMetadata(KotlinMetadataLibraryProvider.kt:32)
    	at dev.icerock.moko.kswift.plugin.KLibProcessor.processFeatureContext(KLibProcessor.kt:28)
    	at dev.icerock.moko.kswift.plugin.PostProcessLinkTask.execute(PostProcessLinkTask.kt:37)
    	at dev.icerock.moko.kswift.plugin.PostProcessLinkTask.execute(PostProcessLinkTask.kt:13)
    	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:752)
    	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:725)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:502)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:74)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:74)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:487)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:470)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:106)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:271)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:249)
    	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
    	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
    	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
    	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
    	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
    	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
    	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
    	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:50)
    	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
    	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
    	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
    	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
    	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
    	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
    	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:58)
    	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:39)
    	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:60)
    	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:27)
    	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:180)
    	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:75)
    	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:46)
    	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:40)
    	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:29)
    	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
    	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:105)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:98)
    	at java.base/java.util.Optional.map(Optional.java:265)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:53)
    	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:37)
    	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:85)
    	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:42)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
    	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:92)
    	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
    	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:114)
    	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
    	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:73)
    	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:47)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:92)
    	at java.base/java.util.Optional.orElseGet(Optional.java:369)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:92)
    	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:33)
    	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
    	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:43)
    	at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:31)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution$2.withWorkspace(ExecuteActionsTaskExecuter.java:284)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
    	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
    	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
    	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
    	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
    	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
    	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:185)
    	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:174)
    	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
    	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:79)
    	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:402)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:389)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:382)
    	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:368)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
    	at java.base/java.lang.Thread.run(Thread.java:834)
    
    bug 
    opened by mattmook 4
  • Using with regular framework

    Using with regular framework

    👋

    I'm using :embedAndSignAppleFrameworkForXcode to build a regular framework instead of using Cocoapods.

    I can see the generated .swift sources available in build/xcode-framework/$(CONFIGURATION)/$(SDK_NAME) but I can't reference them from the iOS project. Is there anything manual process that needs to be done here?

    question 
    opened by ychescale9 4
  • #46 Remove bang operator to eliminate Xcode warnings

    #46 Remove bang operator to eliminate Xcode warnings

    This PR was created to eliminate the following Xcode warning:

    Forced cast from 'Class.dataclass1' to 'Class.dataclass2' always succeeds
    

    Update

    It looks like my proposed fix of replacing as! with as breaks the sample app 😞. My apologies. This replacement seemed to resolve the issue with the project I'm on, but it may not be the way forward for everyone.

    opened by atticus183 3
  • Eliminate Xcode warnings on generated code

    Eliminate Xcode warnings on generated code

    It seems 0.5.0 introduced the following Xcode warning:

    Forced cast from 'Class.dataclass1' to 'Class.dataclass2' always succeeds
    

    I propose removing the bang operator from the SealedToSwiftEnumFeature.kt file, specifically inside the buildSealedProperty method.

    I will happily fork and create the PR myself 👍🏼.

    enhancement 
    opened by atticus183 3
  • Internal sealed class are exported

    Internal sealed class are exported

    Internal sealed class are exported by kswift but won't be available in the library. Swift compilation failed because it can not find types.

    internal sealed class Response<T> {
        data class Success<T>(val result: T) : Response<T>()
        data class Failure<T>(val error: String?) : Response<T>()
    }
    
    bug 
    opened by cl3m 3
  • Gradle Sync deletes generated KSwift files

    Gradle Sync deletes generated KSwift files

    Hi here! First of all I want to thank the team who made this plugin possible, it's really useful 🌟

    Now, I'm not sure if this is an issue of the plugin or gradle itself (or maybe I'm not setting up the plugin correctly) but whenever I make some updates to the build.gradle of the shared module (where I have set up kswift as well) and click on the Sync now button (or even just restart IntelliJ IDEA and it automatically syncs gradle), it deletes the existing shared/build/cocoapods/framework/sharedSwift folder that won't be generated until I do the following:

    Starting point: After gradle sync, the sharedSwift folder is no longer present Screenshot 2022-12-28 at 9 26 15 AM

    Step 1: Go to XCode and run the app until the build fails due to sharedSwift not being available anymore Screenshot 2022-12-28 at 9 30 09 AM

    Step 2: Come back to IntellJ IDEA, open the terminal, ensure that the sharedSwift folder is generated now and then run pod install in its terminal Screenshot 2022-12-28 at 9 32 11 AM

    Step 3: Go back to XCode, and from the popup, select Read from disk to make sure XCode reads the newly generated files Screenshot 2022-12-28 at 9 32 37 AM

    Step 4: Let Step 3 finish its build completely and then the app run/build would finally succeed Screenshot 2022-12-28 at 9 34 53 AM

    This becomes quite tedious while developing 😅 Therefore I want to ask if there's a way to retain the previously generated files inside the build folder every time we sync gradle (or manually allow the generation of these generated files)?

    opened by shubhamsinghshubham777 2
  • I experienced an error generated in KSwift when I updated the kotlin version and kmp-nativecoroutines version

    I experienced an error generated in KSwift when I updated the kotlin version and kmp-nativecoroutines version

    I updated Kotlin Version to 1.7.20 and updated KMP-NativeCoroutines to version 0.13.1.

    The error is like this:

    e: java.lang.NoSuchMethodError: 'void org.jetbrains.kotlin.ir.util.IrUtilsKt.passTypeArgumentsFrom$default(org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression, org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer, int, int, java.lang.Object)'
            at com.rickclephas.kmp.nativecoroutines.compiler.KmpNativeCoroutinesIrTransformer.callOriginalFunction(KmpNativeCoroutinesIrTransformer.kt:162)
            at com.rickclephas.kmp.nativecoroutines.compiler.KmpNativeCoroutinesIrTransformer.createNativeBody(KmpNativeCoroutinesIrTransformer.kt:111)
            at com.rickclephas.kmp.nativecoroutines.compiler.KmpNativeCoroutinesIrTransformer.visitFunctionNew(KmpNativeCoroutinesIrTransformer.kt:100)
            at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFunction(IrElementTransformerVoidWithContext.kt:83)
            at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:72)
            at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:73)
            at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:24)
            at org.jetbrains.kotlin.ir.declarations.IrSimpleFunction.accept(IrSimpleFunction.kt:28)
            at org.jetbrains.kotlin.ir.IrElementBase.transform(IrElementBase.kt:24)
            at org.jetbrains.kotlin.ir.util.TransformKt.transformInPlace(transform.kt:35)
            at org.jetbrains.kotlin.ir.declarations.IrClass.transformChildren(IrClass.kt:56)
            at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitDeclaration(IrElementTransformerVoid.kt:57)
    

    Is there any solution for this ?

    opened by Egi10 2
  • It is possible to exclude all by default?

    It is possible to exclude all by default?

    I want to exclude all classes minus the ones included via annotations or via gradle configuration.

    I do not find a way to do it. Is it possible with the current implementation?

    Thanks a lot.

    question 
    opened by franmontiel 2
  • How do you prevent the library from being generated?

    How do you prevent the library from being generated?

    I have some libraries that shouldn't be generated, I've tried using the following:

    excludeLibrary("com.russhwolf:multiplatform-settings-no-arg") excludeLibrary("com.russhwolf:multiplatform-settings-serialization") excludeLibrary("com.russhwolf:multiplatform-settings-coroutines") excludeLibrary("com.russhwolf:multiplatform-settings-test")

    or like this:

    excludeLibrary("multiplatform-settings-no-arg") excludeLibrary("multiplatform-settings-serialization") excludeLibrary("multiplatform-settings-coroutines") excludeLibrary("multiplatform-settings-test")

    but it always generates, how do you prevent it from generating?

    opened by Egi10 0
  • Filter with mask/wildcard

    Filter with mask/wildcard

    Can we filter with mask? like this

    kswift {
      install(dev.icerock.moko.kswift.plugin.feature.SealedToSwiftEnumFeature) {
        filter = includeFilter("ClassContext/GithubSearchKMM:shared/com/hoc081098/github_search_kmm/**")
      }
    }
    
    opened by hoc081098 0
  • Provide Swift friendly alternative to data class copy methods

    Provide Swift friendly alternative to data class copy methods

    Similar to #8, but requires a different solution There is no good Swift way to call a copy methods on a Kotlin data class because the default parameter values are not provided, but it isn't possible to actually have parameters default to values based on the object so it is not possible to write an exact replica of the city function. It is.t important that what is generated be the same syntactically. It is more important to get the same functionality, e.g to make a copy of the data class changing only part of the object without having to specify every property.

    Here is a SO post that talks about how to write such a method in Dart, which I know is not Swift, but the same concepts and limitations apply: https://stackoverflow.com/questions/68009392/dart-custom-copywith-method-with-nullable-properties

    The best alternative is probably the one that takes nullable parameters that default to nil and if it is nil the property is unchanged. The wrinkle is for properties that were nullable, in which case you add a boolean parameter for each such property that says whether to treat null as null or unchanged.

    opened by dalewking 0
  • Xcode warnings on generated code: forced cast always succeeds

    Xcode warnings on generated code: forced cast always succeeds

    I believe I'm experiencing the same issue as #46, even though this is supposed to be fixed in 0.6.0.

    I'm seeing 125 Xcode warnings inside my generated Swift file for "forced cast from '..' to '..' always succeeds, did you mean to use 'as'?". These all occur inside the sealed Swift property relating to Kotlin sealed classes, where the Swift enum case always has 1 parameter and the Kotlin sealed class does not use generics at all.

    I believe this should fall into the logic mentioned in #46:

    If the enum case has a parameter, and the return type is not generic, it does not need to be force-cast.

    My environment:

    • KSwift version 0.6.0
    • Xcode 13.4.1 (13F100)
    • iOS project targeting iOS 14.1+
    • Manually integrating the generated Swift files rather than using Cocoapods
    opened by sebj 1
  • `No extensions handle the extension type: kotlinx.metadata.klib.KlibModuleFragmentExtensionVisitor`

    `No extensions handle the extension type: kotlinx.metadata.klib.KlibModuleFragmentExtensionVisitor`

    I tried to integrate this plugin by adding id("dev.icerock.moko.kswift") version "0.6.0" to my plugins{} section and kswift { install(dev.icerock.moko.kswift.plugin.feature.SealedToSwiftEnumFeature) } as config. Unfortunately, the following error shows up when I try to build my KMM project using ./gradlew shared:build:

    > Task :shared:linkDebugFrameworkIosArm64 FAILED
    
    FAILURE: Build failed with an exception.
    
    * What went wrong:
    Execution failed for task ':shared:linkDebugFrameworkIosArm64'.
    > No extensions handle the extension type: kotlinx.metadata.klib.KlibModuleFragmentExtensionVisitor
    

    I am using Kotlin 1.7.10.

    Do you have any idea how to solve this?

    opened by ln-12 2
  • Redundand libs are processed and generates code

    Redundand libs are processed and generates code

    In moko-kswift 0.6.0 changed libs processing logic - later we generates code only for libs that added to export. but in 0.6.0 we start generates code for all libs, not only exported. and this generated code is invalid by default because naming. Need to rollback logic

    bug 
    opened by Alex009 0
Releases(release/0.6.1)
  • release/0.6.1(Oct 28, 2022)

  • release/0.6.0(Aug 7, 2022)

    Changes

    • #46 fix warnings in generated code at compile time in Xcode
    • #53 Kotlin 1.7.0 compatibility
    • fix public visibility for sealed property
    • fix error message typo

    Thanks

    @atticus183 @nateansel @kazumanagano

    Source code(tar.gz)
    Source code(zip)
  • release/0.5.0(Apr 2, 2022)

  • release/0.4.0(Feb 25, 2022)

    Changes

    • #26 official swiftpoet version
    • #28 generics for platform extensions
    • #24 fix duplicate module names
    • #25 fix KotlinThrowable name
    • #35 fix internal usage in swift code
    • #22 extensions for primitive types
    • Apple Silicon support

    Attention

    required migration.

    • names of generated files was changed.
    Source code(tar.gz)
    Source code(zip)
  • release/0.3.0(Aug 24, 2021)

    Changes

    • #11 fix AnyObject support in generics
    • #14 fix setup from groovy
    • #13 add iosDeploymentTarget config

    Attention

    Required migration of gradle configuration (see readme or sample)

    Source code(tar.gz)
    Source code(zip)
  • release/0.2.0(Aug 11, 2021)

  • release/0.1.0(Aug 7, 2021)

Owner
IceRock Development
Kotlin Multiplatform developers team
IceRock Development
Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Gérard Paligot 98 Dec 15, 2022
Library to use Kotlin Coroutines from Swift code in KMP apps

KMP-NativeCoroutines A library to use Kotlin Coroutines from Swift code in KMP apps. Flows Kotlin Create an extension property to expose the Flow as a

Rick Clephas 508 Jan 3, 2023
KmMScientists is a Kotlin multiplatform app with swift ui, jetpack compose, koin and realm

KmMScientists KmMScientists is a Kotlin multiplatform app built with swift ui, jetpack compose, koin and realm. Whats Shared? Local Data Persistence w

Kashif Mehmood 20 Dec 27, 2022
A lightweight Kotlin friendly wrapper around Couchbase lite for Android.

CouchBaseKtx ?? Work In-Progress ?? A lightweight Kotlin friendly wrapper around Couchbase-lite for Android Read up a little bit of documentation abou

Jaya Surya Thotapalli 5 Feb 15, 2022
KustomExport: a KSP generator of JS facade

Status: ⚠ Experimentation ⚠ KustomExport: a KSP generator of JS facade Motivation Providing a nice JS API can sometimes be complex from a Kotlin Multi

Deezer 49 Dec 1, 2022
Run Kotlin/JS libraries in Kotlin/JVM and Kotlin/Native programs

Zipline This library streamlines using Kotlin/JS libraries from Kotlin/JVM and Kotlin/Native programs. It makes it possible to do continuous deploymen

Cash App 1.5k Dec 30, 2022
An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile.

An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile. 项目架构主要分为原生系统层、Android/iOS业务SDK层、KMM SDK层、KMM业务逻辑SDK层、iOS sdkfra

libill 4 Nov 20, 2022
A Kotlin Native program to show the time since a date, using Kotlin LibUI

TimeSince A Kotlin Native program to show the time since a date, using Kotlin LibUI Report Bug . Request Feature About The Project TimeSince is a Kotl

Russell Banks 2 May 6, 2022
Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.

Codename One - Cross Platform Native Apps with Java or Kotlin Codename One is a mobile first cross platform environment for Java and Kotlin developers

Codename One 1.4k Jan 9, 2023
Kotlin Native Xcode Plugin

Kotlin Native Xcode Support Plugin to facilitate debugging iOS applications using Kotlin Native in Xcode. Defines Kotlin files as source code, with ba

Touchlab 834 Jan 7, 2023
AppCode helper for Kotlin/Native and Xcode

Kotlin Xcode compatibility Gradle plugin The plugin is used by AppCode to set up Kotlin/Native project along with Xcode Sources A multi-build sample w

Kotlin 21 Oct 23, 2022
Native android app made with Kotlin & Compose with example usage of Ktor, SqlDelight.

Delight-Playground ?? Native Android application built with Kotlin and Jetpack Compose. This app also illustrates the usage of advance libraries such

Kasem SM 41 Nov 6, 2022
Utility - The cross-platform native Kotlin command line tool template

Utility The cross-platform native Kotlin command line tool template. Usage Make

null 0 Jan 3, 2022
Kotlin-native webserver for simple image annotations

Kotlin-native webserver for simple image annotations

null 1 Feb 26, 2022
A Kotlin library providing a simple, high-performance way to use off-heap native memory in JVM applications.

native_memory_allocator A library which uses sun.misc.Unsafe to allocate off-heap native memory. Motivation The goal of this project is to provide a s

Target 5 Dec 8, 2022
The KPy gradle plugin allows you to write Kotlin/Native code and use it from python.

The KPy gradle plugin allows you to write Kotlin/Native code and use it from python.

Martmists 14 Dec 26, 2022
🎑 Up to date IANA timezone database library for Kotlin (JVM, JS, Native)

?? IANA Timezone Library for Kotlin Multiplatform Up to date IANA timezone database library for Kotlin (JVM, JS, Native) Usage import org.noelware.ian

Noelware 3 Jun 18, 2022
API-Annotate - Single annotation to mark API elements

API-Annotate A single annotation for annotating externally-consumed elements in

null 0 Feb 5, 2022