3
24
Fork
You've already forked fedicat
1
issues and non-UI code for the Fedicat fediverse client https://fedicat.com/
  • Swift 100%
2026年07月13日 19:17:06 -06:00
Sources/Fedicat clean up depecrated account view 2026年07月13日 10:00:16 -06:00
Tests/TootSDKTests fix tests 2026年07月09日 00:39:18 -06:00
.gitignore add ActiveLocal/Global and NewLocal/Global localization keys 2025年11月14日 20:29:44 -07:00
.swift-format Create .swift-format 2025年08月17日 01:38:59 -06:00
LICENSE updated license and readme 2025年01月14日 17:48:57 -07:00
Makefile add tests 2025年06月04日 18:10:42 -06:00
Package.swift revert 2026年04月26日 18:50:57 -06:00
README.md add navigation title helpers 2026年05月28日 17:22:18 -06:00
RELEASE.md release notes 2026年07月13日 19:17:06 -06:00

Fedicat

This repo provides the issue tracker for the Fedicat fediverse app (available on Testflight) and a Swift package that currently comprises the lower layers, mostly non-UI code, although there is some app-specific SwiftUI support code here. (SwiftSys contains code that I reuse or theoretically could reuse in other apps, which is currently just Talk Dim Sum). Apple/Xcode/SPM doesn't like local packages depending on local packages, so heavily customized package dependencies (namely HTML2Markdown and TootSDK) are merged in and they're starting to creep into each other.

This package is open source and theoretically reusable but it is highly fluid and unstable with daily commits, undocumented, and tailored to Fedicat, and anything I don't need tossed out, so if you're looking to build an app, I recommend starting with the official TootSDK first and then feel free to use ideas from this code (and patch back to TootSDK if that makes sense). I got started by modifying one of their sample apps, but TootSDK-based TootyGraph is open source, and other open source apps are listed in awesome-mastodon ranging from fairly compact (Bubble is around 15k lines) to the aptly named Mammoth (over 100k and looking for a new caretaker).

Components

Session

This is the top and user-interface facing layer. A Session encapsulates an instance login, including a TootClient for communicating with the instance, and Platform/Instance/NodeInfo identifying the instance platform description and capabilities. An app that handles multiple active instances will have a Session for each login. It is essentially a generalization of TootManager in the TootSDK sample app, although maybe it should be flattened into TootClient.

HTML

A fork of HTML2Markdown with formatting support for Fedicat, e.g. bold tags and mentions, and geared for rendering markdown and math with Textual (which has more extensive and [CommonMark]-compliant Markdown support). Large posts (Textual currently has performance issues with a large number of markdown blocks) and plain text with emojis are rendered with EmojiText. There is no dependency on TootSDK except for the occasional shared string extensions and such. Note that it might be cleaner to implement an HTML to AttributedString converter for Textual or some other rendinging target (see the converter contributed by Pipilo to official TootSDK), but an advantage of this approach is that handles Markdown embedded in the HTML as is often done by Mastodon users in the expectation that the client will handle it (or the lack of awareness that Mastodon doesn't).

TootSDK

A fork of TootSDK (from a couple years ago, so it's diverged quite a bit), tailored for Fedicat. It omits code for features and endpoints not used by Fedicat (streaming feeds, web push...), and is intended to be used within Fedicat, so everything is internal except data types used by the UI, and the line between TootClient and Session is getting fuzzy.

Models are mostly structs (Swift treats classes like the bad sheep of the family), but classes are used when there is a circular definition (Post can contain a quoted or reblogged Post, and Account can reference a moved-to Account) or if there is an opportunity for substantial code sharing (Post and PostEdit subclass PostContent). They are generally immutable (let properties), and mostly only instantiated from decoders (so no plans to use these server-side). Decoders and initializers do much of the work in packaging data for the UI (trimming whitespace in strings, returning URLs and OrderedSet instead of strings and arrays). I like to see which specific cases fail, so I handle those on a case-by-case basis, but see original TootSDK for safer handling of enums. The fields correspond to those in the API specs, but can diverge for convenience (treat nil as empty string or empty set) or to accomodate multiple platforms (mastodon has different post quote structure than other platforms so use different fields for each).

TootFeature is removed, platform/feature checks are performed at the Session level and by the UI, so TootClient is more a union of platform APIs than a unified API.

Enums in the same form as Timeline are used to describe most endpoints, in the general direction of other apps like Feditext. They are generally grouped by feature.

Platform

A set of Platform descriptors that replaces the TootSDK feature/flavour tests, but it is outside TootSDK so theoretically TootSDK could be used with any platform/feature detection or none. Each platform is a Platform subclass and the hierarchy mirrors the forking relationship, e.g. Glitch is a subclass of Mastodon which is a subclass of MastoAPI which is a subclass of Platform, GotoSocial and Mitra subclasses of MastoAPI and so on. Within a platform, feature availability can be dependent on the Mastodon API and/or platform build.

The feature tests supplied by Platform, generally in the form of supports(_ endpoint), is somewhat arbitrary, but it is convenient to add a new platform simply by subclassing, and a new feature test usually requires modifying just a few files, the base Platform class, and enabling it in the platform that provides it.

The target platform is anything with pretty good Mastodon API compatibility but I'll try to support whatever is available, even if it's just the InstanceV1 info (like bookwyrm). A current requirement is that the platform identifies itself through a nodeinfo, which includes most of the major fediverse platforms.

An initial platform is derived from the nodeinfo, and then if it's a platform that supports the mastodon instance (v1 or v2) endpoint, more information is potentially derived from that, typically the build number but sometimes a more specific platform (the nodeinfo might indicate the platform is mastodon but the instance info may say it's actually some other mastodon-compatible platform).

Platform is mostly wrapped by Session wrappers (e.g. session.isMitra, session.supportsLists...) but is public so the UI can instantiate platforms to list in the instance browser. It's only dependency should be TootSDK.

Common

Some functions are shared among modules, e.g. trimming whitespace, at the risk of breaking their separation.

Lists

Assorted hardcoded lists used by the UI, such as instances and hashtags, which perhaps logically should go in the UI level, but I like to put here what I can.

Localizations

Also for the UI, localization keywords in the form of enums. The translations are exported to fedicat-localization.

Translations

Translator interfaces (conforming to the Translated protocol along with the Mastodon instance translation), really anything that can transform the content of a post, e.g. there is an Apple Translate implementation and one for deleted posts.

Draft

Observable classes representing sets of defaults and drafts, such as DraftPost for composing posts and DraftProfile for editing your account profile.

Style

Try to follow the Swift API Design Guidelines and DocC documentation

Formatted and checked with swift-format using default rules, except AlwaysUseLowerCamelCase turned off because we have some upper camelcase enums corresponding to JSON fields.

Credits

Open source and graphics credits are listed on the fedicat site

License

MIT License. The original HTML2Markdown and TootSDK licenses are in the respective directories.

AI

There is no AI-generated code in my code, but I can't vouch for third-party dependencies. There is no AI PR policy because I don't have PRs enabled and thus don't have to worry about it. AI-generated issues are not welcome.

Machine translation (google translate, libre translate...) is used in some localization, see details there.

At runtime the app provides access to the instance translation service if available (the provider used by the instance will be displayed in the translated post, e.g. DeepL, LibreTranslate...) and Apple's on-device translation and text-to-speech services (which I can't guarantee won't contact a server so as mastodon does with instance translation, I limit those to public posts, no follower-only or dm posts). The post composer uses the Apple Vision framework for detecting text in images.