Hi, I've been trying to use SealedInterop on the f...
# touchlab-tools
r
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
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
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
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
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
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
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
with; but you can try both and see the difference
r
When doing it with, the name is showing up as expected
Copy code
__attribute__((swift_name("NativeScreen")))
@interface ProjectNameNativeScreen : ProjectNameBase
@end
f
yeah, without you should get something like ProjectNameModuleNameNativeScreen
r
Yes, that was the case
f
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
Header file doesn't contain such a block for me when module is included in the target block
f
the function is not generated if it’s not needed. However, it should be needed if you don’t include it
r
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
hm, this might be a bug what version of SKIE and Kotlin do you have?
and what is your SKIE configuration in Gradle?
r
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
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
Can I do it in a few hours? Rushing into a meeting.
f
yeah, no problem
1
r
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
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
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
thanks
so if I understand correctly, this is a data class?
Copy code
data class NativeNavigationDescriptor(
    val screen: NativeScreen, 
    val clearScreenStack: Boolean,
)
r
yes
f
ok, I’ll try that locally
1
nice I have a reproducer!
r
that's great!
f
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
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
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
Sure. Not needed then.