icerockdev / moko-kswift

Swift-friendly api generator for Kotlin/Native frameworks
https://moko.icerock.dev
Apache License 2.0
351 stars 21 forks source link

Enhancement: Convert sealed classes with deep hierarchy into single Swift enum #21

Open PStrelchenko opened 2 years ago

PStrelchenko commented 2 years ago

Plugin version I've used

plugins {
    id("dev.icerock.moko.kswift") version "0.3.0"
}

Problem

If we define a sealed class with deep hierarchy (when the sealed class has several inheritor sealed classes), moko-kswift will generate different enums for every sealed class.

In Kotlin:

sealed class SealedWithDeepHierarchy {  

    abstract val param: String  

    sealed class LocalTrigger : SealedWithDeepHierarchy() {  
        object First : LocalTrigger() {  
            override val param: String = "first"  
        }  
    }  

    sealed class RemoteTrigger : SealedWithDeepHierarchy() {  
        class Second(override val param: String) : RemoteTrigger()  
        data class Third(override val param: String) : RemoteTrigger()  
    }  

}

// On the Kotlin side this hierarchy splitted into different branches of 'when'-expression
private fun example(s: SealedWithDeepHierarchy) {  
    when (s) {  
        SealedWithDeepHierarchy.LocalTrigger.First -> TODO()  
        is SealedWithDeepHierarchy.RemoteTrigger.Second -> TODO()  
        is SealedWithDeepHierarchy.RemoteTrigger.Third -> TODO()  
    }  
}

moko-kswift will generate bridge code for every sealed class separately:

public enum SealedWithDeepHierarchyLocalTriggerKs {  

  case first  

  public init(_ obj: SealedWithDeepHierarchy.LocalTrigger) {  
    if obj is HHMobileSdk.SealedWithDeepHierarchy.LocalTriggerFirst {  
      self = .first  
    } else {  
      fatalError("SealedWithDeepHierarchyLocalTriggerKs not syncronized with SealedWithDeepHierarchy.LocalTrigger class")  
    }  
  }  

}  

public enum SealedWithDeepHierarchyRemoteTriggerKs {  

  case second(SealedWithDeepHierarchy.RemoteTriggerSecond)  
  case third(SealedWithDeepHierarchy.RemoteTriggerThird)  

  public init(_ obj: SealedWithDeepHierarchy.RemoteTrigger) {  
    if let obj = obj as? HHMobileSdk.SealedWithDeepHierarchy.RemoteTriggerSecond {  
      self = .second(obj)  
    } else if let obj = obj as? HHMobileSdk.SealedWithDeepHierarchy.RemoteTriggerThird {  
      self = .third(obj)  
    } else {  
      fatalError("SealedWithDeepHierarchyRemoteTriggerKs not syncronized with SealedWithDeepHierarchy.RemoteTrigger class")  
    }  
  }  

}  

public enum SealedWithDeepHierarchyKs {  

  case localTrigger(SealedWithDeepHierarchy.LocalTrigger)  
  case remoteTrigger(SealedWithDeepHierarchy.RemoteTrigger)  

  public init(_ obj: SealedWithDeepHierarchy) {  
    if let obj = obj as? HHMobileSdk.SealedWithDeepHierarchy.LocalTrigger {  
      self = .localTrigger(obj)  
    } else if let obj = obj as? HHMobileSdk.SealedWithDeepHierarchy.RemoteTrigger {  
      self = .remoteTrigger(obj)  
    } else {  
      fatalError("SealedWithDeepHierarchyKs not syncronized with SealedWithDeepHierarchy class")  
    }  
  }  

}

So in Swift you will use it like this:

func mokoKswiftUsage(c: SealedWithDeepHierarchy) {
        switch SealedWithDeepHierarchyKs(c) {

        case let .localTrigger(tr):
            switch SealedWithDeepHierarchyLocalTriggerKs(tr) {
            case .first:
                <#code#>
            }

        case let .remoteTrigger(tr):
            switch SealedWithDeepHierarchyRemoteTriggerKs(tr) {
            case .second(_):
                <#code#>
            case .third(_):
                <#code#>
            }
        }
    }

It would be great, if in such case moko-kswift will generate single enum for more transparent usage, e.g:

func mokoKswiftUsage(c: SealedWithDeepHierarchy) {
        switch SealedWithDeepHierarchyKs(c) {

        case .localTriggerFirst:
            <code>

       case .remoteTriggerSecond(_):
           <code>

       case .remoteTriggerThird(_):
            <code>     
    }