ACINQ / phoenix

Phoenix is a self-custodial Bitcoin wallet using Lightning to send/receive payments.
https://phoenix.acinq.co
Apache License 2.0
616 stars 93 forks source link

(ios) Integrate Touchlab's SKIE #529

Closed robbiehanson closed 3 months ago

robbiehanson commented 3 months ago

The interface between Kotlin & Swift has always been ... let's just say it leaves something to be desired. The root cause of the problem is that Kotlin libraries are exposed via an Objective-C layer, which is then converted to Swift.

This is a bit like converting Spanish to Japanese, and then from Japanese to Italian. It would have been much better to do the conversion directly, and taking the longer route means things are lost in translation.

However, Touchlab has released a tool called SKIE (Swift Kotlin Interface Enhancer), which is a drastic improvement.

For example:

Previously, if Kotlin exposed something like ratesFlow: StateFlow<List<ExchangeRate>>, then in Swift we had to write a wrapper function:

func ratesPubliser() -> AnyPublisher<[ExchangeRate], Never> {
  self.getSetAssociatedObject(storageKey: &_Key.ratesPublisher) {
    // Transforming from Kotlin:
    // `ratesFlow: StateFlow<List<ExchangeRate>>`
    //
    KotlinCurrentValueSubject<NSArray>(
      self.ratesFlow
    )
    .compactMap { $0 as? [ExchangeRate] }
    .eraseToAnyPublisher()
  }
}

Plus there's custom code for getSetAssociatedObject, and a custom class KotlinCurrentValueSubject.

With SKIE, you can just access the value directly:

for await rates in currencyManager.ratesFlow {/* ... */}

So we can drop a bunch of boilerplate/workaround code that we had. Plus we get to switch from the old Publisher syntax to the modern async/await syntax.

It's important to note how SKIE actually works:

SKIE improves interoperability between Kotlin and Swift by generating Swift wrappers for Objective-C headers created by the Kotlin compiler. It recreates features supported by both languages that are lost in the translation from Kotlin to Objective-C to Swift.

SKIE (pronounced as sky) is a special Kotlin native compiler plugin that brings back support for some of [the lost-in-translation] features by modifying the Xcode Framework produced by the Kotlin compiler.

In other words, we don't have to change the way we write the Kotlin code. We just get a much better interface on the Swift side. And the improvement is so drastic, I do wonder if the Kotlin team will officially adopt this approach.

dpad85 commented 3 months ago

@robbiehanson Since this is a significant change it will need quite a lot of testing. So we can merge this PR to master now, but if we need to patch the mainnet iOS app for some reason, then we'll have to fork from an earlier commit.