A native iOS client for Linux.do — UIKit + Swift, no SwiftUI in the main app.
English | 中文
| Home | Mini Program | Me |
|---|---|---|
![]() |
![]() |
![]() |
| Login | Default Theme | Eye-care Theme |
|---|---|---|
![]() |
![]() |
![]() |
| Xiaohongshu Theme | WeChat Theme | Telegram Theme |
|---|---|---|
![]() |
![]() |
![]() |
| Default Topic Detail | WeChat Topic Detail | Telegram Topic Detail |
|---|---|---|
![]() |
![]() |
![]() |
- Linux.do browsing — Latest, top, categories, tags, and search, all rendered with native UIKit.
- Topic detail — Cooked HTML rendered as native text, images, quotes, code, polls, spoilers, oneboxes, tables, videos, and a timeline jumper.
- Replies & reactions — Reply to topics or floors, like posts, and use Linux.do emoji / Boost.
- Image viewer — Multi-image swipe, count, share, save, and close.
- Account & Me — Profile, badges, bookmarks, drafts, browsing history, notifications, and private messages.
- Auth — Web login, cookie reuse for native requests, and global Cloudflare challenge handling, plus silent session recovery for third-party platforms.
- Appearance — Default, eye-care, Xiaohongshu, and Telegram themes, plus fonts, font size, and tab-bar layout.
- Plugins — Mini programs, NewAPI check-in (with silent re-auth), toolbox, and a plugin dock.
- Share & widget — Share a topic URL into Doer, plus a home-screen quick-launch widget.
- Data management — Inspect and clear browsing data, image cache, cookies, and app storage.
- Updates — In-app check against GitHub Releases.
Doer follows a thin ViewController / fat ViewModel pattern with iOS 15-compatible observation. Since the project sets SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, all UI-adjacent code runs on the main actor by default.
ViewController (renders state)
│ observes DoerObservableObject.didChangeNotification
▼
ViewModel (owns state, calls notifyChanged())
│
▼
DiscourseAPI / DiscourseRouter (per-forum Alamofire instance)
Key layers:
Doer/Networking/—DiscourseAPI(one instance per forum, Alamofire-based) +DiscourseRouter(all API routes as enum).Doer/Core/Auth/— Web login viaWKWebView, cookie reuse for native requests, Keychain-backed credentials, and session refresh.Doer/Core/Plugins/— Plugin registry, runtime, and built-in plugins (mini programs, NewAPI check-in, toolbox).Doer/Database/— GRDBDatabasePoolwith versioned migrations, storesForumInstancerecords.Doer/Core/Settings/—AppSettings(DoerObservableObjectsingleton) for user preferences.Packages/CookedHTML/— Local Swift package that parses Discourse-cooked HTML intoBlockNode/InlineNodetrees withNSAttributedStringrendering support.
Topic rendering supports two paths: a WKWebView snapshot path (JS messaging extracts interactive regions) and native UIKit block renderers under Doer/Features/ForumDetail/TopicDetail/NativeContent/.
- Xcode 16+
- mise for tool versions (Tuist version is pinned in
.mise.toml) - A development team ID in
.mise.local.toml(not committed) asTUIST_DEVELOPMENT_TEAM
# Install tools, fetch dependencies, and generate the Xcode project
make setup
# Re-generate the project only
make generate
# Build an unsigned IPA via ci_scripts
make unsigned-ipa
# Clean generated artifacts
make cleanThen open Doer.xcworkspace (not the standalone .xcodeproj), select the Doer scheme and your development team, and run.
The generated workspace is not committed. Run
make generateagain after changingProject.swift.
# CookedHTML package tests
cd Packages/CookedHTML && swift test
# App unit tests: open Doer.xcworkspace and run the DoerTests scheme.
├── Project.swift # Tuist project: Doer / DoerTests / DoerShare / DoerWidget
├── Tuist/ # External Swift packages
├── Doer/ # App source
│ ├── AppDelegate.swift
│ ├── SceneDelegate.swift
│ ├── Info.plist
│ ├── Localizable.xcstrings # en / zh-Hans / zh-Hant / zh-HK
│ ├── Assets.xcassets/ # App icon, launch art, runtime images
│ ├── AppIcon.icon/ # Icon Composer asset
│ ├── Components/ # Shared feedback / empty-state views
│ ├── Resources/Fonts/ # Bundled icon font
│ ├── Core/
│ │ ├── Auth/ # Web login, cookie store, Keychain, session refresh
│ │ ├── ImageLoading/ # Avatar and image helpers
│ │ ├── Observable/ # Observable base controllers
│ │ ├── Plugins/ # Plugin registry and built-ins
│ │ ├── Settings/ # Theme, language, fonts, tab bar, DoH
│ │ └── Update/ # GitHub release checker
│ ├── Database/ # GRDB pool and ForumInstance records
│ ├── Features/
│ │ ├── ForumDetail/ # Home, topic, Me, notifications, search, chat
│ │ ├── ForumList/ # Multi-forum list
│ │ ├── Main/ # Root tab container
│ │ ├── Settings/ # Appearance, reading, data, network, about
│ │ ├── Plugins/ # Mini programs, NewAPI check-in, toolbox
│ │ ├── Notion/ # Notion topic sync
│ │ └── AIModelService/ # In-app AI providers
│ └── Networking/ # DiscourseAPI + DiscourseRouter + DoH
├── Extensions/
│ ├── DoerShare/ # Share extension → doer://
│ └── DoerWidget/ # Home-screen widget
├── Packages/CookedHTML/ # Cooked HTML → native block/inline tree
├── DoerTests/ # App unit tests
├── ci_scripts/ # Unsigned IPA build
└── assets/ # README icon and screenshots
Doer is a native iOS client for Linux.do. The UIKit architecture started from Dexo; several interaction details came from FluxDo. The product name and repository are independent.
- moliango/doer — This project.
- Linux.do — The community Doer is built for.
- Eilgnaw/dexo — Dexo.
- Lingyan000/fluxdo — FluxDo.











