在 SwiftUI 中使用 Compose 共享业务逻辑

前言

看到标题也许会一头雾水,SwiftUI、Compose、业务逻辑这几个词分开看得懂,连起来是个什么玩意?

首先,关于 Compose 和业务逻辑的关系,在之前的文章提到过:Compose 和 Compose UI 有些不同,Compose 可以完全不包含 UI,结合 Compose 的特性,你甚至拿 Compose 来写业务逻辑也完全没有问题。有不了解的朋友可以先看看之前的文章,在这里就不赘述了。

那么更进一步,Compose 编写的业务逻辑能不能用在 SwiftUI 中呢?

答案是可以的!

下面就来讲讲如何在 iOS 甚至其他更多平台使用 Compose 编写的业务逻辑代码,从而达到多个平台共享同一套业务逻辑代码。

如何在 SwiftUI 中使用 Compose 编写的业务逻辑

之前的文章有提到 Jake Wharton 大佬编写的 Molecule,简单说就是一个使用 Compose 的特性来生成 Flow 的一个工具,在这个工具的帮助下,我们就可以编写一套共享的业务逻辑。

还是举 Counter 的例子来说,我们首先定义一个 CounterState 和CounterPresenter:

Kotlin 复制代码
@Composable
fun counterPresenter(): CounterState {
    var count by remember { mutableStateOf(0) }
    return object : CounterState(count.toString()) {
        override fun increment() {
            count++
        }
    }
}

abstract class CounterState(
    val count: String,
) {
    abstract fun increment()
}

当然,在 iOS 中,counterPresenter 是无法直接调用的,这时候就需要使用 Molecule 来进行桥接,输出一个 Flow 给 iOS 使用:

Kotlin 复制代码
class CounterPresenter {
    private val scope = CoroutineScope(Dispatchers.Main + DisplayLinkClock)

    actual val models: StateFlow<CounterState> by lazy {
        scope.launchMolecule(mode = RecompositionMode.ContextClock) {
            counterPresenter()
        }
    }
}

在 iOS 中,Flow 并不是很容易直接调用,这时候需要 SKIE 来帮我们将 Flow 的调用简化,这样我们就可以在 iOS 中这样调用 CounterPresenter

Swift 复制代码
@Observable
class CounterViewModel {
    private let presenter = CounterPresenter()
    var model: CounterState
    init() {
        // 初始化 model的值
        model = presenter.models.value
    }
    @MainActor
    func activate() async {
        // 借助 SKIE 完成对 CounterPresenter.models 的监听
        for await model in presenter.models {
            self.model = model
        }
    }
}

struct CounterView: View {
    @State var viewModel = CounterViewModel()
    var body: some View {
        VStack {
            // 直接使用 CounterState 的值
            Text("Counter: \(viewModel.model.count)")
            Button(action: {
                // 直接调用 CounterState 内的方法
                viewModel.model.increment()
            }, label: {
                Text("Increment")
            })
        }
        // 确保 viewModel 监听正常 Presenter 的 Flow
        .task {
            await viewModel.activate()
        }
    }
}

而在 Android 中,我们可以直接在 Compose 中使用 counterPresenter,相关的内容已经在前文有介绍过,就不再赘述了。

限制

当然这样的做法也是有一些限制的,毕竟 Kotlin/Native 还是一个很初期的阶段

范型

Kotlin/Native 在 iOS 上还不是直接与 Swift 进行交互,还只是通过 Objective-C 来进行的,这个过程中泛型就很容易出问题,几乎所有的 interface 定义的泛型都无法使用,但是 class 可以,举个例子:

Kotlin 复制代码
interface SomeInterface<T> {}
val i: SomeInterface<T> // 在 Swift 中 i 的泛型会被抹除

class SomeClass<T> {}
val c: SomeClass<T> // 而 c 的泛型能够正确使用

最典型的例子就是 List<T>,我们需要返回一个 class 来避免泛型类型被抹除,需要手写一个类似 ListWrapper 的 class,并且不能让这个 class 继承 List<T>

生命周期

SwiftUI 和 Compose UI 的生命周期还是有一些差别的,比如在 iOS 中,当页面返回之后,我们的 CounterPresenter 似乎会被重新创建,导致数据会被重新获取,如果是严格使用单一数据源的架构的话,这倒不是什么大问题。不过我对于 SwiftUI 的了解还是非常浅显的,有可能是我理解错误,这里就抛砖引玉,还请各位 iOS 开发来解答比较好。

FAQ

Q:这样做有什么好处吗?

一定会有人遇到因为平台表现不一致而导致的问题,相同的一套业务逻辑代码能够同时在多端共享,在保证业务逻辑一致的情况下还能拥有 Native 级别的性能,并且还能与 Swift 代码交互。

Q:都用上 Compose 了,我直接用 Compose 画 UI 不是更好吗?

确实,这更多是一种选择。如果更偏向体验一致可以选择直接用 Compose 画 UI,如果想要 Native 的体验,还需要多端业务逻辑保持一致,那么文章提到的做法也不失为一种选择。

Q:这些限制这么多,感觉会有很多坑,而且不稳定

确实,Kotlin Multiplatform 甚至目前为止都还不是正式版,仍然有不少基础组件缺失,写一些业务逻辑其实也会遇到因为基础组件的缺失而导致很别扭的情况,比如 HTML 解析,所以文章提到的更多是一种尝试。

Q:说了这么些,有没有什么实际项目在这样用呢?

您好,有的。这是我最近一直在写的一个项目:github.com/DimensionDe... ,这是一个跨平台的 Mastodon/Misskey/Bluesky 的社交客户端,支持 Android/iOS/macOS,代码层面完全是按照文章所说的实现的,核心业务逻辑完全使用 Kotlin 编写,在 Android 上使用 Jetpack Compose 实现 UI,在 iOS/macOS 上使用 SwiftUI,目前也进入了公开测试阶段,欢迎体验。

是的,为了写这篇文章,我特地写了一个项目(狗头)。

另外这篇文章从去年9月拖到现在终于写完了。

相关推荐
vortex527 分钟前
谷歌黑客语法挖掘 SQL 注入漏洞
android·数据库·sql
-指短琴长-4 小时前
MySQL快速入门——基本查询(下)
android·mysql·adb
stevenzqzq5 小时前
android lambda回调
android
林北北的霸霸7 小时前
django初识与安装
android·mysql·adb
Java 码农9 小时前
MySQL EXPLAIN 详解与优化指南
android·mysql·adb
stevenzqzq13 小时前
Android Hilt 入门教程_传统写法和Hilt写法的比较
android
wuwu_q13 小时前
用通俗易懂方式,详细讲讲 Kotlin Flow 中的 map 操作符
android·开发语言·kotlin
_李小白14 小时前
【Android FrameWork】第五天:init加载RC文件
android
2501_9160074714 小时前
手机使用过的痕迹能查到吗?完整查询指南与步骤
android·ios·智能手机·小程序·uni-app·iphone·webview
黄毛火烧雪下15 小时前
React Native (RN)项目在web、Android和IOS上运行
android·前端·react native