Cocoapod dependency in iosMain of a Kotlin Multiplatform project, cocoapod unresolved reference - kotlin

I am trying to create a wrapper around AWS Amplify to use for my project. I have a module inside my shared ( common ) module called Amplify. Here I integrated cocoapods as instructed in the official docs. But when I try to import anything from the iosMain I keep getting Unresolved reference: cocoapods.
My project structure is as follows
Common module (shared)
|- root module
|- other features
|- amplify wrapper module
In the root module I have the
kotlin {
ios {
binaries {
framework {
baseName = "Framework"
linkerOpts.add("-lsqlite3")
export(project(":common:main"))
}
}
}
And the setup for the cocoapods in the amplify module
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
id("multiplatform-setup") // this is from buildSrc, it adds the multiplatform plugin
id("android-setup")
id("org.jetbrains.kotlin.native.cocoapods")
kotlin("plugin.serialization") version "1.5.10"
}
version = "1.0"
kotlin {
sourceSets {
named("commonMain") {
dependencies {
}
}
}
cocoapods {
summary = "Amplify wrapper for KMP project"
homepage = "Link to a Kotlin/Native module homepage"
frameworkName = "AmplifyKMP"
pod("Amplify")
pod("AmplifyPlugins/AWSCognitoAuthPlugin")
pod("AmplifyPlugins/AWSPinpointAnalyticsPlugin")
}
}
My idea is that I'd be able to expose my wrapper from the commonMain code, that would call actual implementations from the Amplify Android and Amplify IOS libraries. My first problem is the cocoapods being unresolved and secondly all of the examples from the official docs and github all have cocoapods in the main module (root module in my case) from where they export the framework, I am not sure my approach is even feasible.

The first problem is easy. You need to add kotlin("native.cocoapods") to your plugins section.
On the second, can a sub-module import pods with cinterop and make them available to a module that depends on them? I haven't tried it. In theory the cocoapods plugin should be able to just import pods definitions to kotlin. However, the cocoapods Kotlin gradle plugin (aka kotlin("native.cocoapods")) will configure ios targets to create a framework. That might create issues with the dependency config.
In the amplify module I don't see that you're defining any iOS targets, so you'll probably need to do that, but that config will be altered by kotlin("native.cocoapods"). You'll likely need to step in and modify that yourself. You can step in and do that in gradle, but I would prepare to spend some time tweaking that.
https://github.com/touchlab/KaMPKit/blob/main/shared/build.gradle.kts#L114

Related

add google ml-kit dependecy outside the app-level gradle file

I'm following Googles instructions on how to implement "Google ML Kit" into my application. It says
Before you begin
In your project-level build.gradle file, make sure to include Google's Maven repository in both your buildscript and allprojects sections.
Add the dependencies for the ML Kit Android libraries to your module's app-level gradle file, which is usually app/build.gradle:
dependencies {
// If you want to use the base sdk
implementation 'com.google.mlkit:pose-detection:17.0.1-beta6'
// If you want to use the accurate sdk
implementation 'com.google.mlkit:pose-detection-accurate:17.0.1-beta6'
}
However I need to use ML-Kit in a library outside my app. It's basically a java library that gets used from within my app. It also has a gradle file. However when I add the dependency there, I cannot import any ml-kit classes.
Here is the gradle file of my java library:
apply plugin: 'java-library'
dependencies {
api 'com.annimon:stream:1.2.2'
api 'org.javatuples:javatuples:1.2'
api 'com.google.guava:guava:30.1.1-jre'
// JSON
implementation 'org.json:json:20210307'
implementation 'androidx.annotation:annotation:1.2.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.4'
implementation 'com.google.mlkit:pose-detection:17.0.1-beta6'
}
sourceCompatibility = "8"
targetCompatibility = "8"
gradle sync is still successful, but upon importing any class inside any other class of my java library, I get an import error:
import com.google.mlkit.vision.pose.defaults.PoseDetectorOptions;
--> cannot resolve symbol mlkit
Any idea what I could do about that?
Meanwhile I found the problem: The above gradle file belongs to a pure java library that is integrated into android later. ML-Kit apparantly cannot be used in Java projects. It only works if I integrate ML-Kit in pure android gradle projects

Skeleton for Kotlin/native multiproject with dll library for backend and frontend app using it

I'm trying to create a multiproject using Kotlin/native and gradle in IDEA that consists of:
A backend subproject library. I want to use this library in frontend Kotlin app and also produce a native DLL that can be later used in other software. I doubt I'll need any platform specific behavior -- the most I'll interact with the system is read, write and watch a file for changes.
A frontend jvm app in Kotlin using this library as required dependency. To be more precise I'm going to write a glfw app in Kotlin that will use this lib, but that's a detail you don't have to bother with.
I want to be able to:
build DLL on it's own
build an app that depends on library and rebuilds if needed when the lib changed.
I made a hyperlink trip over gradle docs, JetBrains examples and repos but I don't quite understand how to make a multiproject like that. Can someone provide a minimal working example of such a Hello World project?
Right now this works for me in the initial stage of the project:
Use gradle init with basic type and kotlin DSL to generate a wrapper project
Add 2 modules with New > Module > Gradle > Kotlin/Multiplatform and Kotlin/JVM. That should add include("...") entries to root settings.gradle.kts At this point those modules will be empty with a single build.gradle.kts scripts
Root build.gradle.kts can be deleted - both library and app use their own
In the Multiplatform library setup at least one target for example like that:
// rootProject/library/build.gradle.kts
// ...
kotlin {
jvm() // used by jvm app
sourceSets { /*...*/ }
}
Now there should be no gradle errors and IDEA will detect the project properly - refresh gradle configuration (CTRL+SHIFT+O)
Create source directories for each module (because the modules rn): IDEA should hint the names of corresponding source sets (src/<target>/<kotlin|resources> etc.)
So now just to link the app and library together in the the app's buildscript add the implementation(project(":library")) dependency
Of course don't forget to configure the library target for example using linuxX64("native") { binaries { sharedLib {/*...*/ } } } block in kotlin plugin when trying to generate a DLL
Now it should mostly work. The structure of a project I'm working on rn looks like that:

Kotlin - Import Jar and Use Class

I have created a library in kotlin using gradle init and following the prompts. It compiles and produces a jar file in lib/build/libs. I then have another project that needs to access functions in the library. through intelliJ, I add the dependency by going to Project Settings > Modules > Dependencies and adding the jar file. I then attempt to import the package defined in the library with import DemoLib where DemoLib is the name of the package (and the name of the library). This does not compile as it does not recognize the package name. I have also tried importing as a library rather than a jar, with the same results. How can I achieve the desired result?
EDIT: In case it helps here is the code:
Library:
package DemoLib
class Library {
fun someLibraryMethod(): Boolean {
return true
}
}
Client Code:
package DemoClientAppOne
import DemoLib.*
class App {
val greeting: String
get() {
return "Hello World!"
}
}
fun main() {
println(App().greeting)
}
Not terribly interesting, but the point is that DemoLib is an unresolved reference even after adding the jar as a dependency
You have three options
Combine two projects under same IDE project and use Gradle dependencies on project:
settings.gradle:
include 'project-lib'
build.gradle:
implementation project(':project-lib')
See https://docs.gradle.org/current/userguide/declaring_dependencies.html#sub:project_dependencies
Install the library into local repository and add it as a Gradle dependency:
apply plugin: 'maven'
then run gradle publishToMavenLocal
and add mavenLocal() repository in gradle.build file.
See How to install a compiled by Gradle jar into the local Gradle Repository files-2.1 instead of the Maven repository?
Use this jar as a flat library Gradle dependency (assuming this jar is located in the libs directory:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation name: 'lib-jar-name'
}
If DemoLib is a package,
you need to
import DemoLib.*
to import all its names into scope. Just import DemoLib wouldn't do anything. Or better,
import DemoLib.SomeClass
to import only one specific name. Actually, if you start typing SomeClass (assuming that's a name in your DemoLib package), IDEA should suggest adding the import.
Also, it's better to follow naming conventions from the beginning, and DemoLib is not a good package name:
Names of packages are always lower case and do not use underscores (org.example.project). Using multi-word names is generally discouraged, but if you do need to use multiple words, you can either simply concatenate them together or use camel case (org.example.myProject).

Can't add kotlinx to a fresh Kotlin/JS project

Trying to figure out what I'm doing wrong. I created a small video of exactly what I'm doing in IntelliJ on Windows.
https://www.youtube.com/watch?v=nIH_55Zbxus&feature=youtu.be
And I'll describe it here in text.
Create a new project
Tick the Gradle > Kotlin/JS for browser template and untick everything else
Add implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' to the dependencies block in the build.gradle.
Sync the gradle files
Attempt to use something from the kotlinx.coroutines namespace
Hopefully its just a silly thing I'm missing. I expected to just have to add the coroutines library to be able to actually import it. It looks like the library is listed in the project structure for the main module so I'm not sure what else might be wrong. Here is a repo of the project too.
plugins {
id 'org.jetbrains.kotlin.js' version '1.3.72'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
testImplementation "org.jetbrains.kotlin:kotlin-test-js"
}
kotlin.target.browser { }
You should add kotlinx-coroutines-core-js dependency. See the documentation: https://github.com/Kotlin/kotlinx.coroutines#js

Kotlin multiplatform/native interoperability with Objective-C framework

I'm trying to call Swift/Objective-C code from Kotlin in a multiplatform project. There are no problems with calls to platform code. But when I'm trying to call some library (or framework, not sure how it is properly called as I'm not an iOS dev) it fails. Docs states that it is possible to call Objective-C code and Swift if it is properly exported:
Kotlin/Native provides bidirectional interoperability with Objective-C. Objective-C frameworks and libraries can be used in Kotlin code if properly imported to the build (system frameworks are imported by default). See e.g. "Using cinterop" in Gradle plugin documentation. A Swift library can be used in Kotlin code if its API is exported to Objective-C with #objc. Pure Swift modules are not yet supported.
But it does not say anything about how can I import them properly. It only point to gradle plugin description that describes old version of gradle plugin. So it does not work for me. Finally I figured out something might be the way to import Objective-C code:
fromPreset(presets.iosX64, 'ios') {
compilations.main.outputKinds('FRAMEWORK')
compilations.main {
cinterops {
firebase {
def pods = '${System.getProperty("user.home")}/Projects/kmpp/iosApp/Pods/'
includeDirs '${pods}Firebase/CoreOnly/Sources',
'${pods}FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers'
}
}
}
}
Build runs without failures, but it does not import anything. What am I doing wrong? Is it possible to import such a lib at all?
UPD:
here I found an example of usage cinterop tool like this:
cd samples/gitchurn
../../dist/bin/cinterop -def src/main/c_interop/libgit2.def \
-compilerOpts -I/usr/local/include -o libgit2
It looks like cinterop tool should be in /dist/bin/ folder in my projects but there is no such folder. Where do I get cinterop tool ?
I ended up with this cinterops section in build.gradle
fromPreset(presets.iosX64, 'ios') {
// This preset is for iPhone emulator
// Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
compilations.main {
outputKinds('FRAMEWORK')
cinterops {
firebase {
defFile "$projectDir/src/iosMain/cinterop/firebase.def"
includeDirs {
allHeaders "$projectDir/../iosApp/Pods/FirebaseCore/Firebase/Core/Public",
"$projectDir/../iosApp/Pods/FirebaseDatabase/Firebase/Database/Public"
}
compilerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
linkerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
}
}
}
}
end this .def file:
language = Objective-C
headers = FirebaseCore.h FirebaseDatabase.h
What's going on here? Cocopods frameworks are placed in Pods directory in your Xcode project. Navigating this folder a bit, you'll find what you need. I'm not sure if there is some standard but firebase place main header file in Public folder. And it contains references to other header files it needs... So you specify these file names in your .def file in the headers section.
Next, you need to specify where to look for these files and others referenced by them. You can do it in the .def file in includeDirs or in build.gradle file. I prefer to use the build.gradle file as it can use variables. So you specify the path to these Public folders. (This is enough for kotlin to see library API, but in order to be able to run the app you need to compile and link this library...)
Then the compiler and linker need to know where library/framework is. So you specify path to root folder of the framework in compilerOpts and linkerOpts prefixing them with -F if it is a framework or -L if it is a library.
It looks like you are going to use a cocoapods library. Currently the Gradle plugin has no support for cocapods out of the box. But may be a dependency on your library can be configured "manully". Could you please share a link to your project?