MNGA consists of two main components:
- SwiftUI UI Module - Handles user interface and interactions
- Rust Business Module - Handles core business logic, networking, and data processing
The two modules communicate via FFI using Protocol Buffers for data exchange.
This section covers the design and implementation of the Rust-based business module.
logic/service: Tokio-based crate that implements business services and exposesdispatch_async/dispatch_syncfor the Swift bridge.logic/protos: Protobuf wrapper crate;build.rsregenerates Rust types inlogic/protos/src/generatedfrom shared.protofiles.- Supporting crates:
logic/text(rich-text parsing),logic/cache(sled-backed cache), andlogic/config(runtime configuration) are all consumed bylogic/service.
protos/DataModel.protodefines shared models such asTopic,User,Post, and enums used on both sides of the bridge.protos/Service.protodefines theSyncRequestandAsyncRequestoneofs plus every request/response pair thatlogic/servicedispatches.- Building
logic/protos(e.g., viacargo check -p logic-service,make logic-sim, or the iOS builds) rerunslogic/protos/build.rsto update the generated Rust bindings; remember to runmake swift-pbafter proto edits so Swift stays in sync.
- Extend
protos/Service.proto(andDataModel.protoif new types are needed) with the request/response messages and place the new entry in eitherSyncRequest.valueorAsyncRequest.value. - Regenerate protobuf bindings by rebuilding the Rust crate (
cargo check -p logic-serviceis enough) and re-runmake swift-pbfor Swift stubs. - Implement the business logic in
logic/service/src, creating a new module if the functionality does not fit an existing one. - Register the handler:
- Async services: add a
handle!(service_name, function_name);line inlogic/service/src/dispatch/handlers_async.rs. - Sync services: add a wrapper in
logic/service/src/dispatch/handlers_sync.rsand match the new variant indispatch_syncinsidelogic/service/src/dispatch/mod.rs.
- Async services: add a
- If the service needs caching or shared state, reuse the helpers in
logic/cache,logic/text, andlogic/service/src/utils.rsto stay consistent with existing code.
Here are the references for NGA's interface specifications. The information might not be completely accurate or up-to-date, so compare different information sources before taking action.
- https://github.com/wolfcon/NGA-API-Documents/wiki
- https://gitee.com/AgMonk/nga-api-doc
- Android open-source client: https://github.com/Justwen/NGA-CLIENT-VER-OPEN-SOURCE
Services under logic/service/src:
request.rs: stores and mutates the globalRequestOption, including base URL overrides and custom user agents.auth.rs: keeps the currentAuthInfoin memory and exposesset_auth/current_uid.cache.rs: implementsCacheRequesthandling with prefix-based cache scans and clears.clock_in.rs:clock_inendpoint that memoizes daily sign-ins per user via the shared cache.forum.rs: forum discovery (get_forum_list), subforum subscription management (set_subforum_filter), and forum search.topic.rs: core topic workflows—forum topic lists, topic details, hot topics, favorites/folders, topic search, per-user topic feeds, and related extraction helpers.post.rs: post-level operations including vote tracking, reply flows (post_reply,post_reply_fetch_content), attachment uploads, hot replies/comments parsing, and per-user post history.history.rs: stores topic snapshots in cache and servesget_topic_history.msg.rs: handles short message conversations (get_short_msg_list,get_short_msg_details,post_short_msg) and participant parsing.noti.rs: notification fetch (fetch_notis), normalization, caching, and synchronousmark_noti_read.user.rs: sharedUserController, anonymous ID handling, and theget_remote_userservice.attachment.rs: converts attachment nodes intoAttachmentmodels for post rendering.
cargo clippy
make logic-iosRun cargo clippy to check for potential issues.
Compiles a new logic framework for Swift to link against, if you're going to build the app.
make swift-pbGenerates Swift protobuf code. Rust protobuf code is generated automatically during compilation.
make swiftformat
make build # xcraft will regenerate the Xcode project via Tuist if necessaryUpdates the Xcode project file (if necessary, e.g., when adding new files, dependencies, etc.) and formats the Swift code. Build the app (for check purposes only) after making changes.
Remember to update localization file at app/Shared/Localization/zh-Hans.lproj/Localizable.strings after making changes to the UI, if applicable. No need to update English localization file, as you can directly use English string literals in the code.
make logic-ios- Build iOS frameworkmake swift-pb- Generate Swift protobuf codemake logic-sim- Build simulator-only version (faster for development)make logic-deploy- Build release version
- Avoid any Chinese in source code, including comments and string literals, unless it's a user-generated content in test data.
- The project is targeting iOS 26. Note that this is NOT a typo. Apple released iOS 26 in 2025.
- APIs of SwiftUI is evolving very fast. Always refer to the latest documentation via
sosumiMCP server. - If you need real NGA data for testing, study the existing Rust API implementations under
logic/service/srcfirst (topic.rs,post.rs,forum.rs, etc.) to understand the NGA request/response shape, then send real requests following those implementations instead of inventing new endpoints or payloads. You may have to pass some auth info when making requests, seelogic/.env; use them locally and do not expose or commit them.
When you are asked to submit a PR, please make sure to:
- Follow the PR title convention by checking the history of commit messages in
mainbranch. - Provide a clear and concise description of the changes you made and the reason behind them.
You can use the AXe CLI for iOS Simulator automation to conduct end-to-end testing and development of the app.
make launch-simis the fastest way to get a simulator build running before using AXe.- Always verify the current screen with
axe describe-uior a screenshot after taps; AXe confirms input dispatch, not successful navigation. - Prefer selector-based taps, but bottom bars, overlays, and duplicate labels can make coordinate taps necessary.
- AXe can be unreliable when toggling SwiftUI switches. After tapping a
Toggle, re-check itsAXValue; if it does not change, fall back to tapping the actual switch control, or verify behavior through the same persisted setting in the simulator container. - In MNGA, the accessibility tree is good enough for navigation and screen-state confirmation.
- Prefer deep links like
mnga://topic/<tid>when a known topic must be opened quickly.For testing purposes, you can use the well-known topic ID45150945to evaluate general functionality. - If you need to rotate the simulator from the terminal,
osascriptcan click Simulator menu items such asDevice > Rotate Left/Right, but this requires macOS Accessibility permission for the terminal process first.