Litho
linux.do 原生客户端
linux.do 的原生 iOS / Android 客户端,让浏览器替它发出每一个请求,从而穿过 Cloudflare。
- 日常主力 · iOS 公测 / Android 已发布
- Swift · SwiftUI · iOS 17+ · Kotlin
Why
linux.do 是我每天都在看的社区,它值得在手机上拥有原生、保持登录的体验。
Discourse 本身有完善的 API。难点从来不是数据,而是请求根本发不出去。
Problem
linux.do 部署在 Cloudflare 之后,普通的 URLSession 或 curl 请求会直接吃 403 人机验证页。
第三方工具甚至简单脚本都会反复撞盾、丢失会话。
用 WebView 套壳能过验证,却放弃了原生的阅读、互动和通知。
System
Litho 不自己发请求。所有调用都经过一个离屏的浏览器网关。
原生 App
iOS 用 SwiftUI,Android 用 Kotlin + Compose:话题流、阅读、表情回应、聊天、私信与通知。
统一入口
TopicList、Reply、Reaction 等所有业务只调用一个网关 API,不直接碰网络。
WebView 网关
离屏 WKWebView 用页面自身的
fetch()发出请求,带着真实浏览器的 UA、Cookie 和 TLS 指纹。Discourse
REST API 返回真实 JSON,MessageBus 长轮询承载实时更新。
Implementation
图片也过盾
头像、帖内图、徽章图在页面里以二进制 fetch,base64 回传给原生,再交给带缓存的加载器。
挑战自愈
偶发撞盾时,网关自动重载页面完成挑战并恢复请求,顶部状态横幅提示,无需重启 App。
实时通道
通过网关订阅 Discourse MessageBus,驱动聊天、新回复与通知的实时推送。
渲染与存储
帖子用单个 WKWebView 文档渲染;GRDB 负责本地缓存;UserNotifications 加后台刷新负责通知。
Android 端
Kotlin + Compose,按 iOS 架构 1:1 移植。
Decisions
- D1
借用浏览器,而不是对抗它
不去模仿指纹、不去破解挑战,而是把请求交还给真实的浏览器上下文。对 Cloudflare 来说,这就是一个浏览器。
- D2
一层网关承载所有网络
业务从不直接访问网络,过盾、图片和实时更新都只需要在一个地方解决一次。
- D3
只把三件事做到位
打穿 Cloudflare、原生体验、对齐网页端交互。不做全能论坛客户端。
Result
- 日常主力
- TestFlight 公测 · iOS 17+
- APK 已发布
它已经从 MVP 长成我每天在用的客户端:阅读、表情回应、聊天、私信和实时通知。