StreetComplete(StreetComplete)项目在 GitHub(GitHub)上开设了编号 5421 的 issue,标题写作 iOS - Testing! :-D,创建于 2023 年 12 月 20 日。该 issue 围绕 iOS 版本的测试展开,供开发者和测试者跟进 iOS 端的进展与反馈。
正文
iOS - 测试中!:-D
这个工单是协调 StreetComplete iOS 移植开发的主工单,取代 #1892。后者也包含大量讨论、研究和观察工作。
TLDR:看 Project Board 了解任务列表。欢迎贡献!

方案
应用代码库 100% Kotlin,与 Swift 很像。Kotlin 可转译成 JavaScript,也可编译成机器码,和 Swift 一样。许多依赖如今是纯 Kotlin 库。
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:
- 把数据访问和 UI 状态代码与纯 UI 分离(使用 ViewModel:Use ViewModels#5070)

- 把目前使用 Android XML 布局的 UI 代码迁移到 Android Jetpack Compose。自底向上做可增量迁移(见迁移策略指南)。
- 然后从 Jetpack Compose 迁移到 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 架构
Android 上大概是这样:

视图布局、行为及其(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 步之后)
第 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,要么旧且不活跃。

我稍多看过、都做 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 进展更新

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

工作主要分两部分: 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
所以剩下的是所有 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)