Hacker News 中文摘要

RSS订阅

StreetComplete iOS 版开启公测 -- StreetComplete on iOS is now in public beta

StreetComplete(StreetComplete)项目在 GitHub(GitHub)上开设了编号 5421 的 issue,标题写作 iOS - Testing! :-D,创建于 2023 年 12 月 20 日。该 issue 围绕 iOS 版本的测试展开,供开发者和测试者跟进 iOS 端的进展与反馈。

正文

iOS - 测试中!:-D

这个工单是协调 StreetComplete iOS 移植开发的主工单,取代 #1892。后者也包含大量讨论、研究和观察工作。

TLDR:看 Project Board 了解任务列表。欢迎贡献!

也看 SotM Europe 2024 演讲幻灯片。

iOS 移植相关图片

方案

应用代码库 100% Kotlin,与 Swift 很像。Kotlin 可转译成 JavaScript,也可编译成机器码,和 Swift 一样。许多依赖如今是纯 Kotlin 库。

Kotlin Multiplatform 方案示意图

iOS 版因此可用 Kotlin Multiplatform 实现。

UI 代码可用 Compose Multiplatform 共享;它目前对 iOS 处于 ~~alpha~~beta。它是 Jetpack Compose 的分支/扩展,API 大体相同。Jetpack Compose 是响应式 UI 框架,UI 完全用代码定义(类似 SwiftUI、Flutter)。不过我们必须逐步重建整个 UI。

这样可把平台相关(Android/iOS)代码量降到最低。

相比其他方案,最大优势是后续维护量不会明显增加,因为仍只有一个代码库。对比 Flutter 多平台(如 Every Door 所用),它让我们基本不用碰 Kotlin 代码库;用 Flutter 则需全部重写成 Dart。

步骤概要

  • 把平台相关代码与应用逻辑分离,用 Kotlin 多平台依赖替换应用逻辑里的 Android/Java 依赖。
  • 逐步把 UI 代码迁移到 Compose Multiplatform:
    1. 把数据访问和 UI 状态代码与纯 UI 分离(使用 ViewModel:Use ViewModels#5070)

Jetpack Compose 迁移

  1. 把目前使用 Android XML 布局的 UI 代码迁移到 Android Jetpack Compose。自底向上做可增量迁移(见迁移策略指南)。
  2. 然后从 Jetpack Compose 迁移到 Compose Multiplatform。和第一步相比这是小步。

Compose Multiplatform 迁移

当前状态

此节持续更新。

所需步骤详见看板式 Project Board。有些任务尚未创建,因为完全依赖其他任务先完成和一些研究。除非另有说明,这些任务都明确开放给读到这里并(希望 😊)想为 iOS 移植做贡献的人。想实现某个任务,就在该工单下留言。

我们之前粗略估计完整移植到 iOS 需要一个人年。所以范围仍很大。只有靠社区大量贡献,现实上才可能完成。幸运的是,过去几年正是这样。2024 上半年我全职做这个,迁移现在约完成 50%。

所以换挡吧!

你可以怎么帮

  • 从项目板接任务!或者先熟悉提到的技术/框架,之后做有意义的贡献。特别是 Jetpack Compose / Compose Multiplatform。这对我也很新,我也得摸索。
  • 赞助开发!见主页侧边栏 “Sponsor this project”。钱越多,我越能投入时间。知道适合大规模资助的来源,告诉我。
  • 帮助维护和 issue 分类。省下的时间可投入开发。

2024-01-17:UI 方面需要做什么

TL;DR: * 第一步是 Use ViewModels (master)#5530 * 第二步是自底向上把 view 转成 Composables(Jetpack Compose) * 其余以后再看

免责声明:我也得先熟悉这些没用过的 Jetpack 框架,主要是 Composables 和 Navigation。下面是研究示例代码的结果,如果我理解错了,请指出!

当前 UI 架构

Android Activity 大多只是容器,Fragment 构成定义视图行为、状态、生命周期、数据(更新)以及与父/子组件通信和导航的大部分组件。

视图通常由 Android XML 布局定义,有少量 “*ViewController” 类以可复用方式定义某些视图结构的行为。有些复合/自定义视图可能仍存在。

当前 UI 架构

预计的 UI 架构

Android 上大概是这样:

目标 UI 架构

视图布局、行为及其(UI)状态由 Jetpack Compose / Compose Multiplatform 的 Composable 完成。

UI 显示所用数据通过 ViewModel 访问。ViewModel 字段最好从一开始就用 Kotlin 的 StateFlow 而不是 LiveData,因为前者已是多平台。

最后,不同 composable 之间的导航用 Jetpack Navigation 的 NavHost 管理。这意味着 Activity 只是应用入口,实际上不再有 Fragment,一切都是 Composable 和 ViewModel,因此一切都能共享。

UI 架构步骤

必须避免同时开太多工地。无法从当前架构一步跳到新架构。要把迁移拆成尽可能多、能保证代码库自身一致的自包含步骤:

第 1 步:ViewModel

瘦 Fragment:数据访问和视图状态管理放进 ViewModel,由 Fragment 持有。已有工单:#5530。即使不关 iOS,这也有意义,因为它正确封装了视图逻辑与数据访问/状态管理。

第 2 步:Jetpack Compose

自底向上把 UI 布局定义和行为代码转成 Jetpack Compose。这不仅包括把 Android XML 布局转成 Compose 代码,还包括 “*ViewController” 的代码、RecyclerView view holder,最后还有 Fragment,因为大多数视图行为目前定义在那里。

第 1、2 步后的视图架构

(第 1、2 步之后)

第 3 步:Compose Multiplatform

走向(希望很小的)从 Jetpack Compose 到 Compose Multiplatform 的一步。最大问题是如何以多平台方式访问资源。已有一些库,如 MOKO resources,但愿届时已有流行且稳定的方案。我很希望 JetBrains 出官方方案。

第 4 步:多平台 ViewModel / Navigation

2024-09 编辑:生态变了!Android ViewModel 和 Android Navigation 也能多平台使用,所以下面研究已过时,我们可以继续用现在用的。

到第 2 步结束时,Fragment 会很瘦。我们可选择在视图架构上停在这里,把仍留在这些 Fragment 中的其余代码在 iOS 上用 Swift 写的 UIViewController 复制一份。

但至少 ViewModel 要多平台。多数提供多平台 ViewModel 概念的库也提供完全去掉 Fragment 的方案。只替代 ViewModel 的有 KMM-ViewModel、MOKO MVVM、Summer、kmp-viewmodel 等,但多数要么 alpha,要么旧且不活跃。

Image 28

我稍多看过、都做 ViewModel 并提供导航粘合的框架:

PreCompose:提供 ViewModel 和 Navigation,API 与 Android 对应物很像。README 说:“如果你熟悉 Jetpack Lifecycle、ViewModel 和 Navigation,就没什么要学的。”还集成了我们用的依赖注入框架 Koin。2021 年出现,2022 年中以来持续活跃,GitHub 0.6k 星。

Voyager:也提供 ViewModel 和导航。特色是引入 Screen 概念,可替代 Activity 或全屏 Fragment。用 ScreenModel 代替 ViewModel,目的相同。好处:ScreenModel 只是接口。也集成 Koin。还有底部弹窗导航(我们大量使用)和标签导航(profile 屏幕用)。2021 年底出现,非常活跃,GitHub 1.8k 星。2023 年 12 月发布 1.0.0,带来 API 稳定性,文档相当好。是我看的三个项目里最年轻的。

Decompose:也能替代上述功能,但三者中它离 Android Jetpack 术语、概念和 API 最远:Component 替代 Fragment/Activity 的逻辑/导航部分,可选关联 ComponentContext(约 ViewModel)以感知生命周期、配置变更保留状态、处理返回键;Value 是组件内可观察字段,约等于 LiveData 或 StateFlow;Child Stack 替代 FragmentManager,即导航。Decompose 不仅适配 Compose,也适配 SwiftUI 等。它更像通用框架,不绑特定 UI 框架。2020 年出现(最老),一直非常活跃,GitHub 1.7k+ 星。突出的是 open issue 极少。

结论:说实话,我没真正理解 Decompose 的概念,而且独立于 Compose Multiplatform 并非我们所需。迁移到 ViewModel + Composables + Navigation 已经够难消化,再学新概念没帮助。所以我目前稍微偏向 Voyager:像 PreCompose 一样贴近 Android Jetpack 概念,同时更流行,文档和有用组件更多。最终决定还在未来。这次初步研究的目标是先确认 ViewModel + Navigation 概念也能转到多平台,使第 1、2 步可行。结论:可行。

2024-05-23

Step 4:Android ViewModel 从 v2.8.0 起文档说支持多平台。JetBrains 正把 AndroidX Navigation 移植到多平台,目前实验性。继续用 AndroidX 当然最省力。

2024-07-22 louwers

感谢 State of the Map EU 的讨论。关于 Compose Multiplatform 的 MapLibre Native,似乎已有人跑通,另有人在做库。maplibre/maplibre-native#2638。

2024-08-08 Doomsdayrs

不确定放哪,但可能有用:Lifecycle path to Multiplatform。

2024-09-05 进展更新

Image 31

Prototype Fund 资助结束,更新进展、回顾和展望。

进展更新

Image 32

工作主要分两部分: 1. 用 Kotlin Multiplatform(KMP)库或 iOS 自有实现替换对 Java、Java 库和 Android 的依赖 2. 用 Compose 重新实现整个 UI,并使用 ViewModel 和导航。

1. 替换 Java 依赖

资助期内完成的最大任务:用 KMP 库 Ktor-Client 替换 Java Http Client;把 osmfeatures 库移植到 KMP;创建 osm-opening-hours KMP 库。具体: * https://github.com/westnordost/osm-opening-hours * Switch from java.net.URL to Ktor client#5483 * Multiplatform OSM API client#5686 * Multiplatform file access#5656 * Port to Kotlin Multiplatform westnordost/osmfeatures#25 * Use multiplatform settings or implement Preferences interface on iOS#5419 * 用 QRose 和 BigNum 处理二维码及解析/生成;写 HTML 解析器以正确显示用 HTML 排版的本地化文本,如 changelog(都是 About 和 Settings 屏幕 Compose 重做#5719 的一部分)

依赖替换进度

(蓝色 = 资助期开始前已完成)

还剩什么?大多是零碎和粘合。但这种东西常常比预期久。仍需做、代码量较大的: * WIP: test(KMP): use mockative instead of mockito#5706(进行中/暂停,@riQQ) * Set up KMM Project Structure#5412(我在等其他任务)

2. 用 Compose 重新实现 UI

并引入 ViewModel + 导航。绝对工作量约为上面两倍,是待办大头。

MapLibre

关键大工程是从废弃的地图渲染器 tangram-es 迁到 MapLibre-native,由 @Helium314 和我完成。最近合并到 master,因为我们终于找到绕过 MapLibre 一个关键错误的方法。 * Replace Tangram with Maplibre#5693

Compose

我先实现除主屏幕外的所有屏幕。最近开始动主屏幕,但主要是控件、对话框等。具体: * Re-do UI of About screens and Settings screens in Compose#5719 * First steps in Jetpack Compose (reimplement parts of the user screen)#5607, Compose statistics#5855 * Reimplemented Tutorials in Compose#5746 * Migrate main screen to Compose#5799

Compose 迁移进度

所以剩下的是所有 quest 和 overlay 表单(它们常常自定义,所以 UI 代码很多),以及抽象 MapLibre 的 Android 特有实现,即在 Compose 里用 MapLibre。

我很想不必自己做后者,但还没有这样的库。不过有开发者兴趣。看起来有组队在实现库,但目前有 3-4 个竞争项目,希望很快整合:maplibre/maplibre-native#2638。

回顾

对完整 iOS 版的时间估计(一个开发者全职一年)仍然相当准。

MapLibre 集成比预期多花约 6 周,尽管 @Helium314 之前做了初步工作并大力帮我完成。另一方面,用 Compose 重新实现 UI 比预期更直接更快,多少抵消了 MapLibre 多花的时间。

除此之外,自我全职做以来,没有其他开发者为此努力做贡献。所以我现在做了半年,大概还要半年完成。(再加现实中的意外缓冲。)

展望

我的待办

尽管资助结束,我还想完成几件事,让之前工作有个圆满结论。时间 1-2 个月(速度较慢,也需要休息): * 稳定 v59 * 升级到 Jetpack Compose 1.7,并(尝试)解决因 Jetpack Compose 1.6 缺功能未实现的项,如设置中重排 quest 类型、统计屏幕某些动画 * 为 v60 完成 Migrate main screen to Compose#5799。这很费力,因为它一次触及太多组件,还包括 MainActivity 里的所有东西。对 @Helium314 合并进 SCEE 也会是噩梦,所以给 v60 而非 v59。 * 参与一下想实现 Compose Multiplatform 版 MapLibre 的组,至少提建议 * 也许做 Set up KMM Project Structure#5412。我现在回避它,因为怕在我想收尾时又打开一罐虫。

继续

从过去半年外推,我现在不太指望仅靠社区贡献完成项目。剩余工作仍可观。我会找后续资助,但前景不太好:另一个 Prototype Fund 不可能;上次查 OpenStreetMap 没钱;NLNet 基金会被解散,让位给些 AI 垃圾;Liberapay、GitHub sponsors、Patreon 的赞助自宣布此努力以来也没增加,众筹也不行。看看吧,欢迎建议。

贡献

当然,感兴趣的开发者,任何贡献都会让完成更可行,且工作不会丢,因为会持续集成到新版本。若愿意帮,下一步是逐步用 (Jetpack) Compose 重新实现当前 UI 并使用 ViewModel。迁移到底向上进行(先 widget,再屏幕局部,最后整个屏幕)。所以最可持续的贡献是从重新实现 widget 开始,如 opening hours 表单(大工程)、hydrant diameter 表单(中等)或 building levels 输入表单(小),或更多。

理论上,在达到 100% 前可以有功能受限的 iOS 版,排除 UI 还没迁移的 quest。但之前必须完成: * Compose Multiplatform 的 MapLibre 库(见 maplibre/maplibre-native#2638)并迁移 * Set up KMM Project Structure#5412 及目前被它阻塞的任务

按 Prototype Fund 要求,我也写了份报告,会消失在没人看的档案里。但你也许想读,德语,内容大多与这里相同:Schlussbericht StreetComplete für iOS

2026-08-20 westnordost

我看了看,加了两个工单: * iOS Controlling Download- and Upload#7031 * iOS ActiveNetworkConnection#7032 * Implement IosPeriodicCleaner#7081

这些任务任何能在 iOS 上测试的开发者都可接。后者很小,前者可能稍多。

2026-09-02 tim4tech

大家好!刚发现项目,很想贡献。我本想为工作实现类似东西,但觉得贡献这个项目更合理。我有 Kotlin、Swift、Compose、SwiftUI、地图、OSM 等技能,看到上面 Download and Upload issue 还开着。我该试试吗?

2026-09-02 westnordost

Hi Timothy,哈,正是对的技能组合!确实,留给个人贡献者的不多了。但 download & upload 还开着。注意工单评论。

另一个永远开放的是在 iOS 上测试 UI(所有 quest 等),因为我预计测试中会有一长串 issue。(所有 quest 表单都可在设置屏幕查看。设置屏幕需要加进 iOS 测试脚手架。)@kiliankoe 在 #7000 开了头,但最近没时间。

2026-09-07 westnordost

好吧,没关系,那我就用 app.streetcomplete。

2026-09-15 sargunv

#7088 可以审阅了,和 #6352 一起解决 #6072。现在 #7123 和 #7124 也可以审阅了。

2026-09-30 westnordost

是时候开派对 🚀

StreetComplete 第一个构建现在在 TestFlight:https://testflight.apple.com/join/K1u3eUU5

已知问题(正在处理): * 表单只能用返回手势取消,不能点地图任意处 * 地图在 iOS 15.x 上完全不工作 * 以及任何已报告的 bug。所以报 bug 前先查已有列表。

我会让这个工单开放,直到第一次公开发布完成。标题从 iOS - Planning 改成 iOS - Testing! :-D。

评论

社区普遍称赞 StreetComplete 大幅降低 OpenStreetMap 编辑门槛并欢迎其 iOS 测试版;少数用户更偏好竞品 EveryDoor 或指出交互缺陷,亦有对 OpenStreetMap 能否替代谷歌地图体验的疑问。

StreetComplete 极大降低 OSM 贡献门槛且体验优秀

  • 无需专业知识的问答式编辑、离线可用,让新手快速贡献,多年安卓好评,iOS 测试版也受期待(较多人) > 这应用真的好,让 OpenStreetMap 变得平易近人,五分钟贡献胜过十五年。(@smcleod)

偏好竞品 EveryDoor 或认为其更贴合兴趣点

  • 部分用户习惯 EveryDoor,称其更偏向商店等地图兴趣点,且该应用早已支持 iOS(少数人) > 我两个都试过,更喜欢 EveryDoor,它更偏向地图上的店铺和我感兴趣的东西。(@tbo47)

当前 beta 存在交互缺陷与功能遗漏

  • 表单取消手势失效需强关,且缺少贡献历史以便纠错撤回(少数人) > 已知可用返回手势取消表单,但我这连返回都不管用,只能强制关闭。(@thrownawaysz)

对 OSM 作为谷歌地图替代品的易用性存疑

  • 尝试用 OSM 浏览餐厅时界面生疏,疑问能否像商业地图般直接消费数据(少数人) > 能不能像谷歌地图那样用 OpenStreetMap?我试着列餐厅,界面反应像从没人这么干过。(@conartist6)

有价值的补充

  • 有用户提供了原页面不易找到的 TestFlight 测试邀请链接(@greggsy)
  • 用户发现向 OSM 添加的小路很快被谷歌地图采纳,但旧路仍残留未完全同步,显示众包数据反哺商业地图(@anotherpaul)
  • 有人希望听到更多关于 Kotlin Multiplatform 实际使用经验,可能与本应用跨平台实现有关(@intrasight)