Trying to use Swift with Obj C header for Coda2 plugin. Getting error: Undefined symbols for architecture x86_64 - objective-c

Trying to build a PlugIn for Coda 2.5 with swift.I'm getting this error:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_CodaPlugInsController", referenced from:
_get_field_types_PowPlugInViewController in PowPlugInViewController.o
_get_field_types_PowPlugIn in PowPlugin.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I have cleaned my build folder.
I have created and added import statements to the project name-bridging-swift.h.
Here is a look at my project.
CodaPlugInsController.h
You can find this file here.
https://github.com/panicinc/CodaPluginKit/tree/master/Cocoa%20Plug-ins
Discription:
This header provides protocols and facilities to implement Coda
text-based, syntax validator and sidebar plug-in.
CodaPow-Bridging-Header.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "CodaPlugInsController.h"
PowPlugin.swift
import Foundation
class PowPlugIn: NSObject, CodaPlugIn, CodaSidebarPlugIn {
let PowBundle: CodaPlugInBundle
let PowController: CodaPlugInsController
required init(plugInController: CodaPlugInsController, plugInBundle: CodaPlugInBundle) {
self.PowBundle = plugInBundle
self.PowController = plugInController
super.init()
}
func name() -> String {
return "Coda Pow"
}
func didLoadSiteNamed(name: String!) {
}
func viewController() -> NSViewController {
return PowPlugInViewController(nibName: "PowPlugInView", plugInBundle: PowBundle, plugInController: PowController)!
}
}
PowPlugInViewController.swift
import Foundation
class PowPlugInViewController: NSViewController, CodaSidebarViewController {
let PowController: CodaPlugInsController
init?(nibName: String, plugInBundle: AnyObject, plugInController: CodaPlugInsController) {
self.PowController = plugInController
super.init(nibName: nibName, bundle: plugInBundle as? NSBundle)
}
// Xcode Says I need this.
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I have two more source files.
ServerAndHosts.swift and
Shell.swift They don't use any of the classes in CodaPlugInsController.h. The Shell.swift file is just a set of Class functions.
Edit:
I can use swift as long as I don't subclass from the CodaPlugInsController.h or pass in an object that has been.
Swift and Obj-C versions of Project: https://www.dropbox.com/sh/bsw8cinn7kp9kfe/AAAgG_B44dbHNP5-JwJ3Amf8a?dl=0

The project compiles, so the bridging headers may not be the issue.
You fail at the linking stage, the compiler can't find a set of symbols for your current architecture.
This could either mean
1) The Coda library is not compiled for your architecture
or
2) the linker couldn't find the library at all.
I looked at your Obj-C project, and it appears the Coda library is not in the project, so I'm guessing its #2. You'll either want to move the lib into the project somehow, or add a path to the lib elsewhere on your system in your project's "library search paths"

Related

Resolving third party cocoapod dependencies in Kotlin MPP

I am trying to setup a tracking library written in Kotlin Multiplatform to support all our mobile clients.
Tests for Android went well (integrating snowplow via gradle).
I also managed to integrate Snowplow via cocoapods into the MPP.
kotlin {
...
cocoapods {
...
pod("SnowplowTracker") {
version = "~> 1.3.0"
}
}
And wrote following class in the X64 sourceset:
import cocoapods.SnowplowTracker.*
import com.tracking.domain.model.*
class X64Tracker {
private val tracker: SPTracker
init {
val emitter = SPEmitter.build {
it?.setUrlEndpoint("our.staging.endpoint")
it?.setProtocol(SPProtocol.SPProtocolHttps)
}
tracker = SPTracker.build {
emitter?.let { spEmitter -> it?.setEmitter(spEmitter) }
it?.setBase64Encoded(false)
it?.setSubject(SPSubject(platformContext = true, andGeoContext = true))
it?.setAppId("MPP.test")
it?.setApplicationContext(true)
}
}
fun trackSomething() {
track(
eventData = getEventData(
name = "MPP_test_event",
appArea = EventArea.Lifecycle,
action = EventAction.Check,
objectType = EventObjectType.Device,
source = EventSource.Client,
screenName = EventScreenName.Congratulations,
), contexts = emptyList()
)
}
private fun track(eventData: SPSelfDescribingJson, contexts: List<SPSelfDescribingJson?>) {
try {
val yo = SPSelfDescribing.build {
it?.setEventData(eventData)
it?.setContexts(contexts.toMutableList())
}
tracker.track(yo)
} catch (e: IllegalStateException) {
print("snowplow was not yet initialized when the following event occurred: $eventData")
}
}
private fun getEventData(
name: String,
appArea: EventArea,
action: EventAction,
objectType: EventObjectType,
source: EventSource,
screenName: EventScreenName,
) = SPSelfDescribingJson(
SCHEMA_EVENT, mapOf(
PROPERTY_EVENT_NAME to name,
PROPERTY_APP_AREA to appArea.serializedName,
PROPERTY_ACTION to action.serializedName,
PROPERTY_OBJECT_TYPE to objectType.serializedName,
PROPERTY_SOURCE to source.serializedName,
PROPERTY_SCREEN_NAME to screenName.serializedName,
)
)
}
Which is compiling and building our .framework files fine. Here is how we do that:
tasks.register<org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask>("debugFatFramework") {
baseName = frameworkName + "_sim"
group = "Universal framework"
description = "Builds a universal (fat) debug framework"
from(iosX64.binaries.getFramework("DEBUG"))
}
tasks.register<org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask>("releaseFatFramework") {
baseName = frameworkName + "_arm"
group = "Universal framework"
description = "Builds a universal (release) debug framework"
from(iosArm64.binaries.getFramework("RELEASE"))
}
Afterwards we combine this into an .xcframework file using following command:
xcrun xcodebuild -create-xcframework \
-framework tracking_arm.framework \
-framework tracking_sim.framework \
-output tracking.xcframework
We use Carthage to integrate it into our main app, but as soon I try to build the iOS project following error pops up:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_SPSelfDescribing", referenced from:
objc-class-ref in tracking_sim(result.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The weird thing: No matter which version of Snowplow I define in the cocoapods block - the syntax in my class does not need to change. Even updating to Snowplow 2.x doesn't require me to get rid of the SP prefixes.
I am not sure how to continue at all and appreciate any help.
The following:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_SPSelfDescribing", referenced from:
objc-class-ref in kaiaTracking_sim(result.o)
ld: symbol(s) not found for architecture x86_64
That says the linker can't find SPSelfDescribing. I assume SPSelfDescribing is part of SnowplowTracker. You'll need to add SnowplowTracker to the iOS app.
If you were using Cocoapods to integrate Kotlin into your iOS project, the podspec generated would include SnowplowTracker as a dependency.
Since you are not using Cocoapods, you need to include it yourself. We recently had to figure out Carthage for a client. I would highly recommend moving to SPM or Cocoapods for a number of reasons, but that's a different discussion. To add SnowplowTracker with Carthage, add this to your Cartfile:
github "snowplow/snowplow-objc-tracker" ~> 1.3
Then when you update Carthage, add that to your iOS project. The linker will be able to find it.
To be clear, the Undefined symbols for architecture x86_64 error isn't complaining about Kotlin. It's saying it can't find SnowplowTracker, which you need to add to the iOS project.

Unable to resolve cinterop IOS import in Kotlin Multiplatform

I have followed the Kotlin documentation for adding iOS dependencies. In my case the dependency is a pre-compiled framework provided through a third party. So I have followed the case for framework without cocoapod.
I placed my MyFramework.def file in /src
language = Objective-C
modules = MyFramework
package = MyFramework
Then I added the following to the build.gradle.kts in the Kotlin object
```
ios {
binaries {
framework {
baseName = "shared"
}
}
}
iosArm64() {
compilations.getByName("main") {
val JWBLe by cinterops.creating {
// Path to .def file
defFile("src/nativeInterop/cinterop/MyFramework.def")
compilerOpts("-framework", "MyFramework", "-F/Users/user/Projects/MyFramework/ios/SDK")
}
}
binaries.all {
// Tell the linker where the framework is located.
linkerOpts("-framework", "MyFramework", "-F/Users/user/Projects/MyFramework/ios/SDK")
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation("com.google.android.material:material:1.2.1")
}
}
val androidTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13")
}
}
val iosMain by getting
val iosTest by getting
}
Then I build the project. The library does indeed get seen and I see that in External Libraries, there is a shared-cinterop-MyFramework.klib
However, when I try to import this package into my code under src/iosMain/kotlin/com.example.testapp.shared/platform.kt
I get unresolved error for the library. It seems like I should also need to add something to sourceSets? But I am unsure.
First of all, I got to notice that the Gradle script is incorrect. In this case, the iosArm64 target was declared twice - by the target shortcut and once again where you configure the cinterop. To avoid this duplication, it would be better to configure cinterop like that:
ios()
val iosArm = targets.getByName("iosArm64") as org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
// A bit dirty cast, but as I'm sure iosArm64 is the Native target, it should be fine. Needed to make highlighting below work as expected.
iosArm.apply {
compilations.getByName("main") {
val JWBLe by cinterops.creating {
// Path to .def file
defFile("src/nativeInterop/cinterop/MyFramework.def")
compilerOpts("-framework", "MyFramework", "-F/Users/user/Projects/MyFramework/ios/SDK")
}
}
binaries.all {
// Tell the linker where the framework is located.
linkerOpts("-framework", "MyFramework", "-F/Users/user/Projects/MyFramework/ios/SDK")
}
}
However, this adjustment won't help with accessing cinterop bindings from the iosMain. In the current state of commonizer, it can share only platform libraries. So anyway, moving all code utilizing those bindings into the src/iosArm64Main folder is the best option available at the moment. Here go an issue from the official tracker to upvote and subscribe - Support commonization of user-defined libraries.
So after some playing around I found the answer.
The dependency was set for a module of iosArm64 which is not available to the iosMain.
I created another folder src/iosArm64Main and placed the source file there. At that point it was able to resolve the library.

Kotlin's cinterop .def wont work with relative paths

I have a static library that needs to link to other static libraries.
When I attempt to run ./gradlew common:cinteropSomeLibIos :
I always get IllegalStateException: Could not find 'libabseil.a' binary in neither of [external/lib]
my def file:
headers = SomeHeader.h
language = Objective-C
package = com.mypackage
staticLibraries = libabseil.a libMyLib.a
libraryPaths = external/lib
linkerOpts = -lObjC -all_load
Everything works within AndroidStudio/IntelliJ but when using the command line interface or building with bazel i consistently get the above error.
I tried adding:
val main by compilations.getting {
kotlinOptions {
freeCompilerArgs = listOf(
"-include-binary", "$libLocation/libabseil.a"
)
}
}
as well as setting linkerOpts within the gradle file but that results in:
warning: -linker-option(s)/-linkerOpts option is not supported by cinterop. Please add linker options to .def file or binary compilation instead.
Is there any way to get this working or at least to call the cinterop task in a way that the relative paths in the .def file will work?

Kotlin MPP - Cannot resolve Stetho Interceptor

I'm trying out for the first time to build a Kotlin MPP using a Kotlin DSL gradle file.
The issue is quite simple but I've been trying everything I could think of to fix it : the android shared code can't resolve Stetho interceptor
Screenshot of the issue
My build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
id("kotlinx-serialization")
}
kotlin {
//select iOS target platform depending on the Xcode environment variables
val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iOSTarget("ios") {
binaries {
framework {
baseName = "SharedCode"
}
}
}
jvm("android")
sourceSets["commonMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0")
// HTTP
implementation("io.ktor:ktor-client-core:1.3.0-rc")
implementation("io.ktor:ktor-client-json:1.3.0-rc")
implementation("io.ktor:ktor-client-serialization:1.3.0-rc")
}
sourceSets["androidMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3")
implementation("io.ktor:ktor-client-android:1.3.0-rc")
implementation("io.ktor:ktor-client-json-jvm:1.3.0-rc")
implementation("io.ktor:ktor-client-serialization-jvm:1.3.0-rc")
implementation("io.ktor:ktor-client-okhttp:1.3.0-rc")
implementation("com.squareup.okhttp3:logging-interceptor:4.0.1")
implementation( "com.facebook.stetho:stetho-okhttp3:1.5.1")
}
Thanks a lot for your help !
EDIT :
So I found out that when I replace
jvm("android")
with
android()
Stetho-interceptor can be imported, but the "expected" and "actual" keyword are not correctly linked by Android studio for iOS :
"Actual property ... has no corresponding expected declaration" when I hover an actual declaration for iOS
When I use both
jvm("android")
android()
I get a gradle error :
"The target 'android' already exists, but it was not created with the 'jvm' preset. To configure it, access it by name in kotlin.targets or use the preset function 'android'."
you do not need to have jvm("android") if you already are using the android() target. if you want a separate jvm target then you should name it something different (ex. jvm("something") ).
Once you add your android target and manifest + android definitions, the libraries should resolve. the facebook library likely depends on an android target which is why it is not resolving.

Kotlin native interop linker could not find framework

I'm trying to use cocoapods framework in Kotlin Multiplatform project.
So I
added framework to Pods file.
ran pod install.
created .def file
added cinterop config in build.gradle
./gradlew cinteropFirebaseIos runs successfully. It generates .klib so I can see classes in kotlin code.
But when I'm trying to run iOS app build fails with message:
Showing Recent Messages
> Task :app:linkDebugFrameworkIos
ld: framework not found FirebaseDatabase
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld invocation reported errors
Here is my config in build.gradle
fromPreset(presets.iosX64, 'ios') {
compilations.main {
outputKinds('FRAMEWORK')
cinterops {
firebase {
def proj = "${System.getProperty("user.home")}/Projects/kmpp"
def pods = "${proj}/iosApp/Pods"
defFile "${proj}/app/src/iosMain/c_interop/libfirebase.def"
includeDirs "${pods}/Firebase",
"${pods}/Firebase/CoreOnly/Sources",
"${pods}/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers"
}
}
}
}
here is my .def file:
language = Objective-C
headers = /Users/oleg/Projects/klug/crckalculator/iosApp/Pods/FirebaseCore/Firebase/Core/Public/FIRApp.h /Users/oleg/Projects/klug/crckalculator/iosApp/Pods/FirebaseDatabase/Firebase/Database/Public/FIRDatabase.h /Users/oleg/Projects/klug/crckalculator/iosApp/Pods/FirebaseCore/Firebase/Core/Public/FirebaseCore.h
compilerOpts = -framework FirebaseDatabase
linkerOpts = -framework FirebaseDatabase
How can I figure out what is wrong ? Did I miss something in .def file ? In build.gradle ?
There are two problematic moments here:
full paths to C headers in .def file are usually not desirable, instead passing includeDirs to Firebase installation, like in https://github.com/JetBrains/kotlin-native/blob/c7c566ce0f12221088a8908b6dc8e116c56a931b/samples/gtk/build.gradle#L22 would be helpful
linking problem comes from the similar issue - linker just got no idea where to look for framework libraries, so passing to compilations.main.linkerOpts smth like -F /Users/oleg/Projects/klug/crckalculator/iosApp/Pods/FirebaseCore/ shall help, see for example https://github.com/JetBrains/kotlin-native/blob/c7c566ce0f12221088a8908b6dc8e116c56a931b/samples/videoplayer/build.gradle#L15