Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maybe support overridden declaration? #195

Open
oscarthecat opened this issue Oct 16, 2024 · 2 comments
Open

Maybe support overridden declaration? #195

oscarthecat opened this issue Oct 16, 2024 · 2 comments

Comments

@oscarthecat
Copy link

oscarthecat commented Oct 16, 2024

hi, we have a problem when apply NativeCoroutines, we currently use ksp with k2mode=false to generated code. we found @NativeCoroutines can not be applied to the subclass, but must be applied to the parent class or interface

Currently, when it is applied to the parent class or interface, it only generates the corresponding method in the parent class or inferface. For example, for the following classes:

interface Store<STATE, EVENT> {
    val state: Flow<STATE>
    suspend fun dispatch(action: EVENT)
}

class FooState
class FooEvent
class FooStore : Store<FooState, FooEvent> {
    override val state: Flow<FooState>
        get() = TODO("Not yet implemented")
    override suspend fun dispatch(action: FooEvent) {
        TODO("Not yet implemented")
    }
}

class BarState
class BarEvent
class BarStore : Store<BarState, BarEvent> {
    override val state: Flow<BarState>
        get() = TODO("Not yet implemented")
    override suspend fun dispatch(action: BarEvent) {
        TODO("Not yet implemented")
    }
}

if we add @NativeCoroutines annatations to interface

interface Store<STATE, EVENT> {
    @NativeCoroutines
    val state: Flow<STATE>
    @NativeCoroutines
    suspend fun dispatch(action: EVENT)
}

the generated code will be

@ObjCName(name = "state")
public val <STATE, EVENT> Store<STATE, EVENT>.stateNative: NativeFlow<STATE>
  get() = state.asNativeFlow(null)

@ObjCName(name = "dispatch")
public fun <STATE, EVENT> Store<STATE, EVENT>.dispatchNative(action: EVENT): NativeSuspend<Unit> =
    nativeSuspend(null) { dispatch(action) }

and in bin framework header we have

__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("FooNativeKt")))
@interface NCSSFooNativeKt : NCSSBase
+ (NCSSKotlinUnit *(^(^)(NCSSKotlinUnit *(^)(id _Nullable, NCSSKotlinUnit *(^)(void), NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError * _Nullable, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *)))(void))state:(id<NCSSStore>)receiver __attribute__((swift_name("state(_:)")));
+ (NCSSKotlinUnit *(^(^)(NCSSKotlinUnit *(^)(NCSSKotlinUnit *, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *)))(void))dispatch:(id<NCSSStore>)receiver action:(id _Nullable)action __attribute__((swift_name("dispatch(_:action:)")));
@end

but we expect generated code to be

@ObjCName(name = "state")
public val FooStore.stateNative: NativeFlow<FooState>
  get() = state.asNativeFlow(null)

@ObjCName(name = "dispatch")
public fun FooStore.dispatchNative(action: FooEvent): NativeSuspend<Unit> = nativeSuspend(null) {
    dispatch(action) }

and in bin framework header

@interface NCSSFooStore (Extensions)
- (NCSSKotlinUnit *(^(^)(NCSSKotlinUnit *(^)(NCSSKotlinUnit *, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *)))(void))dispatchAction:(NCSSFooEvent *)action __attribute__((swift_name("dispatch(action:)")));
@property (readonly) NCSSKotlinUnit *(^(^state)(NCSSKotlinUnit *(^)(NCSSFooState *, NCSSKotlinUnit *(^)(void), NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError * _Nullable, NCSSKotlinUnit *), NCSSKotlinUnit *(^)(NSError *, NCSSKotlinUnit *)))(void) __attribute__((swift_name("state")));
@end

just for FooStore, not BarStore, any way except modify kmp-nativecoroutines-ksp can do this? thank you very much!

@rickclephas
Copy link
Owner

Hi!

we found @NativeCoroutines can not be applied to the subclass, but must be applied to the parent class or interface

That's correct. The KMP-NativeCoroutines annotations inherit the @HiddenFromObjC behaviour.
Hiding declarations from ObjC must be done on the base declaration, and is inherited by all overrides.

just for FooStore, not BarStore, any way except modify kmp-nativecoroutines-ksp can do this? thank you very much!

The only way to achieve that is to manually define the extensions, instead of using the annotations.
Note: you won't be able to reuse the original name of the declaration since it can't be hidden from ObjC.

// FILE: src/appleMain/kotlin/your/package/FooStoreNative.kt

public val FooStore.stateNative: NativeFlow<FooState>
    get() = state.asNativeFlow(null)

public fun FooStore.dispatchNative(action: FooEvent): NativeSuspend<Unit> =
    nativeSuspend(null) { dispatch(action) }

Why would you like to only generate the extensions for FooStore and not BarStore (or any other Store)?

@oscarthecat
Copy link
Author

Sorry for the delayed reply.
Most of the time, we use the Store(interface), but sometimes we also want to use FooStore(impl). However, the currently generated code doesn’t allow us to access FooStore’s methods directly. Given the scenario, would you consider supporting overridden declarations? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants