Raccoon/docs/tech_manual/tech_stack.md
Dieguitux 8b67612f1d
refactor(l10n): migrate l10n from Lyricist to Compose resources (#164)
* update release checklist

* convert resource files of preserved languages

* remove other resource files

* rewrite L10Manager and Strings

* add test for DefaultL10nManager

* cleanup language extensions

* update app initialization

* update settings business logic

* update check notification worker

* remove old conversion scripts

* remove lyricist dependency

* remove lyricist from licenses

* update documentation
2024-12-17 19:10:24 +01:00

122 lines
6.9 KiB
Markdown

## Tech stack
Here is a list of the technologies used in the project, with a short historical explanation in the
cases where the initial choice changed over time and the reasons why the change was made.
<dl>
<dt>Dependency Injection</dt>
<dd>
The choice here is the <a href="https://github.com/InsertKoinIO/koin">Koin</a> library. The main
reason it was selected because of its great multiplatform support and the integration with the
navigation library (which at the beginning of the project was not there yet, but was added later and
proved to work great). You can find module definitions (beware, Gradle modules and Koin modules are
two different concepts and should not be confused) in a `di` package inside each subproject, modules
can include each other and all top-level modules are included in the shared module, more on it in
"Module overview and dependencies".
</dd>
<dt>Navigation</dt>
<dd>
For navigation the <a href="https://github.com/adrielcafe/voyager">Voyager</a> library has been
selected. Again, the choice was driven by its multi-platform support, its very practical approach
and ease to set up and get going, compared to other multi-platform libraries like Decompose that
were used in the past. Nonetheless, and this lesson was learned the hard way, easiness of use and
compactness mean that things will go smooth in the future, and as the project grew the navigation
library started to show its limits. Part of them were addressed by encapsulating the transition
logic (to push/pop screens into the navigation stack and open/close modal bottom sheets) into a
centralized component NavigationCoordinator.kt.
Something similar was done for the navigation drawer in DrawerCoordinator.kt.
Even the DI integration with Koin was not pain-free, the <code>:core:navigation</code> module contains
some glue code that is used to work around some of the issues that were encountered.
</dd>
<dt>Networking</dt>
<dd>Here, at least for Android developers, no surprises: the choice was <a href="https://github.com/ktorio/ktor">Ktor</a>
which has great multiplatform support. Instead of using Retrofit, to create network adapters the
<a href="https://github.com/Foso/Ktorfit">Ktorfit</a> library is used, which uses KSP to parse
annotations and generate code.
</dd>
<dt>Resource management</dt>
<dd>
Initially the project used the <a href="https://github.com/icerockdev/moko-resources">Moko resources</a>
library to load fonts, icons and all the localized messages used in the UI. It worked great, since in those areas
Compose multiplatform missed the needed functionalities. But as long as the project grew in size and
more complex KSP configurations were needed, having all modules depend on resources became unmaintainable.
This is why I migrated drawable and font loading to Compose build-in system. For localization, the choice was the
<a href="https://github.com/adrielcafe/lyricist">Lyricist</a> library, which better handles dynamic language changes
but it had a couple of issues: its XML processor made builds non-reproducible which was a huge issue in order
to publish on F-Droid and it did not manage plurals correctly; if using pure Kotlin files its format is non-standard
and therefore incompatible with tools like Weblate.
</dd>
<dt>Image loading</dt>
<dd>
This was something that was expected to be simpler but unfortunately it wasn't. In the beginning
the project used the <a href="https://github.com/Kamel-Media/Kamel">Kamel</a> library
which had a major bug while rendering large images, which took a long time to be considered.
The project was already relying on Kamel for many things, from loading images on demand
to Markdown rendering, so deciding to switch was not easy at all. So as a first step,
<a href="https://github.com/coil-kt/coil">Coil</a> 2.x was adopted on Android while keeping Kamel
on iOS and, finally, when Coil 3.x became stable with multiplatform support all the project has
been completely migrated.
</dd>
<dt>Preference storage</dt>
<dd>
Here the choice was the <a href="https://github.com/russhwolf/multiplatform-settings">Multiplatform settings</a>
libary which not only works great but also offers support for encryption.
</dd>
<dt>Primary persistence</dt>
<dd>
This project was a chance to experiment with <a href="https://github.com/cashapp/sqldelight">SQLDelight</a>
(in other multiplatform projects other libraries were tested like Exposed), whereas database encryption
is obtained through <a href="https://www.zetetic.net/sqlcipher/sqlcipher-for-android">SQLCipher Android</a>,
formerly <a href="https://github.com/sqlcipher/android-database-sqlcipher">Android Database SQLCipher</a>.
</dd>
<dt>Markdown rendering</dt>
<dd>
This was another part, like image loading, where KMP was at the beginning quite lacky. After having given
up for some time and used Markwon (Java + Views) on the Android part of the app, I decided to give a
second chance to <a href="https://github.com/mikepenz/multiplatform-markdown-renderer">Multiplatform Markdown Renderer</a>
which was initially user for the multiplatform source set. The project grew and matured over time and
it made it possible to add custom handlers (like modular plug-ins) which made possible to support
Lemmy's custom features like spoilers. The migration from multiplatform renderer to Markwon and back to
multiplatform renderer was not easy, but this project is about KMP so, as a consequence, a pure Kotlin and
pure Compose solution <em>had to</em> be preferred. Even if it implies to sacrifice some functionality.
</dd>
<dt>Video playback</dt>
<dd>
The initial choice was to write a custom native implementation based on <code>Exoplayer</code> on Android
and on <code>AVPlayer</code> on iOS. This solution kind of worked but had some issues and with later
updates of Compose Multiplatform it was using deprecated functions on iOS. This is why the video player
was eventually migrated to
<a href="https://github.com/Chaintech-Network/ComposeMultiplatformMediaPlayer">ComposeMultiplatformMediaPlayer</a>.
</dd>
<dt>Theme generation</dt>
<dd>
The application allows to select a custom color and generate a Material 3 color scheme as a
palette originate from that seed color. This is achieved by using the
<a href="https://github.com/jordond/MaterialKolor">MaterialKolor</a> library which was designed to
be multiplatform and works as a charm for the purpose. Thanks!
</dd>
<dt>Reorderable lists</dt>
<dd>
The ability to reorder lists is achieved thanks to the <a href="https://github.com/Calvin-LL/Reorderable">Reorderable</a>
library which starting from version 1.3.1 has become multiplatform. This functionality is still
experimental and is used only in the instance selection bottom sheet for anonymous users.
</dd>
<dt>Web view</dt>
<dd>
Initially a custom web view was implemented, relying on native views (WebView on Android and WKWebView
on iOS), but in the end the project was migrated to <a href="https://github.com/MohamedRejeb/Calf">Calf</a>
component to display portions of the Web.
</dd>
</dl>