https://kotlinlang.org logo
#touchlab-tools
Title
# touchlab-tools
r

Ratul Sarna

12/04/2023, 8:51 PM
Hi, I've been trying to use SealedInterop on the following sealed class.
Copy code
@SealedInterop.Enabled
sealed interface NativeScreen {

    data object Home : NativeScreen

    data class Song(val argument: ScreenArgument) : NativeScreen

    data class Artist(val argument: ScreenArgument) : NativeScreen
}
But the only case that is available on Swift side is
.else
after switching
onEnum(of: nativeScreen)
Can someone help me understand why this could be happening? There are other sealed classes in the project that are working fine.
Nevermind, this doesn't seem to be a SKIE issue. Removing annotation reveals that none of the subclasses are being created in ObjC.
f

Filip Dolník

12/05/2023, 8:13 AM
Hi! This can be a SKIE issue. SKIE should export these subclasses to Obj-C even if they are not exported otherwise. Can you send me your SKIE configuration from Gradle?
r

Ratul Sarna

12/05/2023, 9:09 AM
Thank you for replying! I figured this out an hour ago. It was my mistake that I'd forgotten to add the relevant sub-module in the following code block -
Copy code
targets.withType<KotlinNativeTarget>().configureEach {
    binaries.withType<Framework> {}
}
f

Filip Dolník

12/05/2023, 9:11 AM
yeah but what I don’t understand is why you were able to access the NativeScreen from Swift given you didn’t include the submodule in which it’s declared
r

Ratul Sarna

12/05/2023, 9:14 AM
I'm not sure about that. The name of the sealed class was also weird, the module name was prefixed as such -
Content_stateNativeScreen
instead of
NativeScreen
.
f

Filip Dolník

12/05/2023, 9:16 AM
Hm, that prefix is used if the type comes from a different library - so maybe it’s a name collision thing? Try searching for “NativeScreen” in the Obj-C header file (build/bin/$arch/$framework/Headers)
r

Ratul Sarna

12/05/2023, 9:18 AM
would you like me to do that before or after including the module in this block -
Copy code
targets.withType<KotlinNativeTarget>().configureEach {
    binaries.withType<Framework> {}
}
f

Filip Dolník

12/05/2023, 9:20 AM
with; but you can try both and see the difference
r

Ratul Sarna

12/05/2023, 9:21 AM
When doing it with, the name is showing up as expected
Copy code
__attribute__((swift_name("NativeScreen")))
@interface ProjectNameNativeScreen : ProjectNameBase
@end
f

Filip Dolník

12/05/2023, 9:23 AM
yeah, without you should get something like ProjectNameModuleNameNativeScreen
r

Ratul Sarna

12/05/2023, 9:24 AM
Yes, that was the case
f

Filip Dolník

12/05/2023, 9:24 AM
and without you can’t find any occurences of “NativeScreenArtist”?
1
because when I try the same in my demo project, SKIE exports it for me
the header file contains a function called “skieTypeExports”
which should reference these types
in my case it looks like this:
Copy code
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("__SkieTypeExportsKt")))
@interface Kotlin__SkieTypeExportsKt : KotlinBase
+ (void)skieTypeExportsP0:(KotlinDependencyNativeScreenArtist *)p0 p1:(KotlinDependencyNativeScreenHome *)p1 p2:(KotlinDependencyNativeScreenSong *)p2 __attribute__((swift_name("skieTypeExports(p0:p1:p2:)")));
@end
r

Ratul Sarna

12/05/2023, 9:28 AM
Header file doesn't contain such a block for me when module is included in the target block
f

Filip Dolník

12/05/2023, 9:28 AM
the function is not generated if it’s not needed. However, it should be needed if you don’t include it
r

Ratul Sarna

12/05/2023, 9:28 AM
Aah okay. Let me re-check by removing the module
This is strange that after reverting, the
SkieTypeExports
section doesn't have this particular sealed interface but it has another from the same module which is also annotated with
SealedInterop.Enabled
p147:(ProjectNameContent_stateIndexViewStateLoading *)
where Loading is an implementation of IndexViewState sealed interface
f

Filip Dolník

12/05/2023, 9:38 AM
hm, this might be a bug what version of SKIE and Kotlin do you have?
and what is your SKIE configuration in Gradle?
r

Ratul Sarna

12/05/2023, 9:38 AM
the latest versions -
0.5.6
and
1.9.21
Copy code
skie {
    features {
        group {
            FlowInterop.Enabled(false)
            SuspendInterop.Enabled(false)
            SealedInterop.Enabled(false)
            EnumInterop.Enabled(false)
        }
    }
}
f

Filip Dolník

12/05/2023, 9:42 AM
that should be fine so I think that SKIE doesn’t correctly evaluates the NativeScreen as being exported. The NativeScreen is exported by the Kotlin compiler because it is referenced in some declaration (class, function, etc.) that is exported. So my guess is that the specific way this class is referenced is not accounted for. Can you search all occurrences of “NativeScreen” in the header file without adding the module to the binaries block and with SKIE disabled? (you can disable SKIE by
skie { isEnabled.set(false) }
r

Ratul Sarna

12/05/2023, 9:43 AM
Can I do it in a few hours? Rushing into a meeting.
f

Filip Dolník

12/05/2023, 9:43 AM
yeah, no problem
1
r

Ratul Sarna

12/06/2023, 9:05 AM
Hi, sorry for the late reply. Here are my findings - 1. When setting
skie { isEnabled.set(false) }
and not including the module in binaries block, there is no mention of "NativeScreen" in the headers file. 2. After setting skie enabled to true and not including module in binaries, here are the mentions of NativeScreen in headers file - a.
@class ....., ProjectNameModuleNameNativeScreen, .....
b.
Copy code
__attribute__((swift_name("ModuleNameNativeScreen")))
@interface ProjectNameModuleNameNativeScreen : ProjectNameBase
@end
As before, it is not included in
SkieTypeExports
section.
f

Filip Dolník

12/06/2023, 9:08 AM
Hi! No problem These are the only two occurrences of the
ProjectNameModuleNameNativeScreen
class? Because there should be at least one extra reference somewhere (it might a return type or value parameter of some function, or an inheritance relation)
r

Ratul Sarna

12/06/2023, 9:11 AM
Yes, I've included NativeScreen as a property in another class, but didn't share because that is a project specific thing and I thought if I hadn't used it in the class, the block would not exist.
Here it is -
Copy code
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("ModuleNameNativeNavigationDescriptor")))
@interface ProjectNameModuleNameNativeNavigationDescriptor : ProjectNameBase
@property (readonly) BOOL clearScreenStack __attribute__((swift_name("clearScreenStack")));
@property (readonly) ProjectNameModuleNameNativeScreen *screen __attribute__((swift_name("screen")));
- (instancetype)initWithScreen:(ProjectNameModuleNameNativeScreen *)screen clearScreenStack:(BOOL)clearScreenStack __attribute__((swift_name("init(screen:clearScreenStack:)"))) __attribute__((objc_designated_initializer));
- (ProjectNameModuleNameNativeNavigationDescriptor *)doCopyScreen:(ProjectNameModuleNameNativeScreen *)screen clearScreenStack:(BOOL)clearScreenStack __attribute__((swift_name("doCopy(screen:clearScreenStack:)")));
- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)")));
- (NSUInteger)hash __attribute__((swift_name("hash()")));
- (NSString *)description __attribute__((swift_name("description()")));
@end
f

Filip Dolník

12/06/2023, 9:14 AM
thanks
so if I understand correctly, this is a data class?
Copy code
data class NativeNavigationDescriptor(
    val screen: NativeScreen, 
    val clearScreenStack: Boolean,
)
r

Ratul Sarna

12/06/2023, 9:17 AM
yes
f

Filip Dolník

12/06/2023, 9:18 AM
ok, I’ll try that locally
1
nice I have a reproducer!
r

Ratul Sarna

12/06/2023, 9:34 AM
that's great!
f

Filip Dolník

12/06/2023, 9:38 AM
Thanks for your help! The problem is a little bit more complex because it’s two SKIE features not working correctly together: The important missing piece in the puzzle is this code:
Copy code
val foo: Flow<NativeNavigationDescriptor>
SKIE also automatically exports a class used exclusively in the Flow type argument because Kotlin doesn’t do that (it doesn’t need to because without SKIE the type argument is erased) This is handled by the same code as for the sealed classes and the code is supposed to support this, but there is some bug in it.
so if you added this code to your exported module instead:
Copy code
val foo: NativeNavigationDescriptor
it would work as expected
r

Ratul Sarna

12/06/2023, 9:40 AM
aah Okay. That makes sense. Overlapping targets need to be processed by the same code and this exact scenario might have some issue that got missed.
Thanks for taking the time to look into this! Should I file an issue on github for tracking sake?
f

Filip Dolník

12/06/2023, 9:43 AM
You can if you want, but it’s not needed, I will create an issue in our local issue tracker and I’ll fix it hopefully in the next release
r

Ratul Sarna

12/06/2023, 9:43 AM
Sure. Not needed then.
2 Views