Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ You can edit the code, rerun the app, and see how the output has changed.
<tr><td><a href="/docs/overview/Top-level%20functions.md">Top-level functions</a></td><td>You can access a top-level function via the wrapper class: TopLevelFunctionKt.topLevelFunction().</td></tr>
<tr><td><a href="/docs/overview/Exceptions.md">Exceptions</a></td><td>If you invoke a Kotlin function that throws an exception and doesn't declare it with `@Throws`, that crashes the app. Declared exceptions are converted to NSError and must be handled.</td></tr>
<tr><td><a href="/docs/overview/PublicAPI.md">Public API</a></td><td>Public classes, functions, and properties are visible from Swift. Marking classes, functions, and properties internal will exclude them from the public API of the shared code, and they will not be visible in Swift.</td></tr>
<tr><td><a href="/docs/overview/ObjCName.md">Interop annotation - @ObjCName</a></td><td>Gives better Objective-C/Swift names to Kotlin constructs like classes, functions and so on, without actually renaming the Kotlin constructs. Experimental.</td></tr>
<tr><td><a href="/docs/overview/HiddenFromObjC.md">Interop annotations - @HiddenFromObj</a></td><td>Hides a Kotlin declaration from Objective-C/Swift. Experimental.</td></tr>
<tr><td><a href="/docs/overview/ShouldRefineInSwift.md">Interop annotations - @ShouldRefineInSwift</a></td><td>Helps to replace a Kotlin declaration with a wrapper written in Swift. Experimental.</td></tr>
<tr><td><a href="/docs/overview/KDocComments.md">KDoc comments</a></td><td>You can see certain KDoc comments at development time. In Xcode, use Option+Double left click to see the docs. Note that many KDocs features don't work in Xcode, like properties on constructors (@property) aren't visible. In Fleet, use the 'Show Documentation' action.</td></tr>
</table>

Expand Down
41 changes: 41 additions & 0 deletions docs/overview/HiddenFromObjC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## Interop annotation - @HiddenFromObjC

[@HiddenFromObjC](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-hidden-from-obj-c/) hides a Kotlin declaration from Objective-C/Swift. Experimental.

### Explanations

In Kotlin:
```kotlin
@HiddenFromObjC
fun myKotlinOnlyFunction(){
println("Only Kotlin!")
}
```

In Swift:
```swift
func hiddenFromObjCExample(){
//Uncommenting gives compilation error
//myKotlinOnlyFunction()
}
```

Using the `@HiddenFromObjC` annotation, we can prevent the export of this function to Objective-C/Swift. This is different from using the `internal` keyword, because the construct can still be used from other Kotlin compilation units, just not from Objective-C/Swift.

#### Setup

Since the annotation is experimental, it is necessary to opt-in.

```kotlin
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
}
}
```

### Credits
With thanks to [Rick Clephas](https://github.com/rickclephas) for the annotation contribution and example code.

---
[Table of contents](/README.md)
39 changes: 39 additions & 0 deletions docs/overview/ObjCName.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Interop annotations - @ObjCName

[@ObjCName](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-obj-c-name/) gives better Objective-C/Swift names to Kotlin constructs like classes, functions and so on, without actually renaming the Kotlin constructs. Experimental.

### Explanations

In Kotlin:
```kotlin
@ObjCName(swiftName = "MySwiftArray")
class MyKotlinArray {
@ObjCName("index")
fun indexOf(@ObjCName("of") element: String): Int = 1
}
```

In Swift:
```swift
let array = MySwiftArray()
let index = array.index(of: "element")
```

Here we are renaming a class, function, and its parameter using the `@ObjCName` annotation so that we can use the alternative names from Objective-C/Swift. This is especially useful to change the Swift names of constructs to more idiomatic names without actually renaming them in Kotlin.

#### Setup

Since the annotation is experimental, it is necessary to opt-in.
```kotlin
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
}
}
```

### Credits
With thanks to [Rick Clephas](https://github.com/rickclephas) for the annotation contribution and example code.

---
[Table of contents](/README.md)
52 changes: 52 additions & 0 deletions docs/overview/ShouldRefineInSwift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## Interop annotations - @ShouldRefineInSwift

[@ShouldRefineInSwift](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-should-refine-in-swift/) helps to replace a Kotlin declaration with a wrapper written in Swift. Experimental.

### Explanations

In Kotlin:
```kotlin
interface Person {
@ShouldRefineInSwift
val namePair: Pair<String, String>
}

class RealPerson: Person {
override val namePair = "First" to "Last"
}
```

In Swift:
```swift
extension Person {
var name: (firstName: String, lastName: String) {
let namePair = __namePair
return (namePair.first! as String, namePair.second! as String)
}
}

func shouldRefineInSwiftExample(){
let authorNames = RealPerson().name
print("Author is: \(authorNames.firstName) \(authorNames.lastName)")
}
```

Using the `@ShouldRefineInSwift` annotation, we are indicating that we intend to refine the definition of `namePair` in Swift. The annotation marks the property as swift_private in the generated Objective-C API. Such declarations get a __ prefix, which makes them invisible from Xcode’s autocomplete.


#### Setup

Since the annotation is experimental, it is necessary to opt-in.
```kotlin
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
}
}
```

### Credits
With thanks to [Rick Clephas](https://github.com/rickclephas) for the annotation contribution and example code.

---
[Table of contents](/README.md)
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@
66D4CD442ADD715400552E0E /* FlowKMPNativeCoroutinesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D4CD432ADD715400552E0E /* FlowKMPNativeCoroutinesExample.swift */; };
66D4CD482ADD784F00552E0E /* FlowExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D4CD472ADD784F00552E0E /* FlowExample.swift */; };
66D4CD4C2ADEA4C000552E0E /* ExceptionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D4CD4B2ADEA4C000552E0E /* ExceptionsExample.swift */; };
66F10C792B19DC0700FFB2C8 /* HiddenFromSwiftExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F10C782B19DC0700FFB2C8 /* HiddenFromSwiftExample.swift */; };
7535496A2AE116ED0016A54B /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753549692AE116ED0016A54B /* ResultView.swift */; };
7535496C2AE1376C0016A54B /* InteropSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7535496B2AE1376C0016A54B /* InteropSamples.swift */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
E59830997CE3DC9E31D1E9C1 /* ObjCNameExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5983E5A1B87DC036ABBEFAD /* ObjCNameExample.swift */; };
E598319E705FEB827303DCB9 /* ShouldRefineInSwiftExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E59839FB583E1FD27DC551A2 /* ShouldRefineInSwiftExample.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -158,11 +161,14 @@
66D4CD432ADD715400552E0E /* FlowKMPNativeCoroutinesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowKMPNativeCoroutinesExample.swift; sourceTree = "<group>"; };
66D4CD472ADD784F00552E0E /* FlowExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowExample.swift; sourceTree = "<group>"; };
66D4CD4B2ADEA4C000552E0E /* ExceptionsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExceptionsExample.swift; sourceTree = "<group>"; };
66F10C782B19DC0700FFB2C8 /* HiddenFromSwiftExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenFromSwiftExample.swift; sourceTree = "<group>"; };
753549692AE116ED0016A54B /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = "<group>"; };
7535496B2AE1376C0016A54B /* InteropSamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteropSamples.swift; sourceTree = "<group>"; };
7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E59839FB583E1FD27DC551A2 /* ShouldRefineInSwiftExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShouldRefineInSwiftExample.swift; sourceTree = "<group>"; };
E5983E5A1B87DC036ABBEFAD /* ObjCNameExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCNameExample.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -208,6 +214,9 @@
66D4CD1D2AD9587900552E0E /* TopLevelFunctionExample.swift */,
66D4CD4B2ADEA4C000552E0E /* ExceptionsExample.swift */,
669CC60F2AEA910800DC4B6E /* PublicApiExample.swift */,
E5983E5A1B87DC036ABBEFAD /* ObjCNameExample.swift */,
66F10C782B19DC0700FFB2C8 /* HiddenFromSwiftExample.swift */,
E59839FB583E1FD27DC551A2 /* ShouldRefineInSwiftExample.swift */,
);
path = Overview;
sourceTree = "<group>";
Expand Down Expand Up @@ -311,6 +320,7 @@
7555FF7D242A565900829871 /* iosApp */,
7555FF7C242A565900829871 /* Products */,
7555FFB0242A642200829871 /* Frameworks */,
E5983519E4CA8889A93ADA6B /* configuration */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -351,6 +361,14 @@
name = Frameworks;
sourceTree = "<group>";
};
E5983519E4CA8889A93ADA6B /* configuration */ = {
isa = PBXGroup;
children = (
);
name = configuration;
path = iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/configuration;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -488,6 +506,7 @@
66D4CD222AD95DC900552E0E /* TopLevelPropertyMutableExample.swift in Sources */,
66D4CCE62AD6DDC400552E0E /* SuspendFunctionExample.swift in Sources */,
669CC6122AEE9CC200DC4B6E /* SuspendFunctionKMPNativeCoroutinesWithCancellationExample.swift in Sources */,
66F10C792B19DC0700FFB2C8 /* HiddenFromSwiftExample.swift in Sources */,
66D4CD082AD9173F00552E0E /* ConstructorWithDefaultArgumentsExample.swift in Sources */,
665A18952A9614E9000087BE /* FunctionReturnsLambda.swift in Sources */,
66D4CCD82AD6835D00552E0E /* FunctionWithLambdaArgsExample.swift in Sources */,
Expand Down Expand Up @@ -517,6 +536,8 @@
66D4CD042AD81B6F00552E0E /* StarProjectionExample.swift in Sources */,
66D4CCFE2AD80F2B00552E0E /* GenericFunctionsExample.swift in Sources */,
7555FF83242A565900829871 /* ContentView.swift in Sources */,
E59830997CE3DC9E31D1E9C1 /* ObjCNameExample.swift in Sources */,
E598319E705FEB827303DCB9 /* ShouldRefineInSwiftExample.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ func overviewSection() -> InteropSection {
protectedPropertyExample()
protectedFunctionExample()
},
InteropSample(
title: "@ObjCName",
description: """
Gives better Objective-C/Swift names to Kotlin constructs like classes, functions and so on, without actually renaming the Kotlin constructs.
"""
) {
objCNameExample()
},
InteropSample(title: "@HiddenFromObjC",
description: "Hides a Kotlin declaration from Objective-C/Swift.",
action: {
hiddenFromObjCExample()
}),
InteropSample(title: "@ShouldRefineInSwift",
description: "Helps to replace a Kotlin declaration with a wrapper written in Swift.") {
shouldRefineInSwiftExample()
},
InteropSample(
title: "KDoc Comments",
description:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation
import shared

func hiddenFromObjCExample(){
//Uncomment to see error
//myKotlinOnlyFunction()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation
import shared

func objCNameExample(){
let array = MySwiftArray()
let index = array.index(of: "element")
print(index)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import shared

extension Person {
var name: (firstName: String, lastName: String) {
let namePair = __namePair
return (namePair.first! as String, namePair.second! as String)
}
}

func shouldRefineInSwiftExample(){
let authorNames = RealPerson().name
print("Author is: \(authorNames.firstName) \(authorNames.lastName)")
}
1 change: 1 addition & 0 deletions kotlin-swift-interopedia-samples/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ kotlin {
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
}

val commonMain by getting {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.jetbrains.swiftinteropplayground.overview

import kotlin.native.HiddenFromObjC

@HiddenFromObjC
fun myKotlinOnlyFunction(){
println("Only Kotlin!")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jetbrains.swiftinteropplayground.overview

import kotlin.native.ObjCName

@ObjCName(swiftName = "MySwiftArray")
class MyKotlinArray {
@ObjCName("index")
fun indexOf(@ObjCName("of") element: String): Int = 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.jetbrains.swiftinteropplayground.overview

import kotlin.native.ShouldRefineInSwift

interface Person {
@ShouldRefineInSwift
val namePair: Pair<String, String>
}

class RealPerson: Person {
override val namePair = "Rick" to "Clephas"
}