-
Notifications
You must be signed in to change notification settings - Fork 4
Cross Platform Development
This guide details the approach, techniques, and best practices for developing QuietDrop across desktop and mobile platforms using a unified codebase.
QuietDrop follows a "write once, deploy everywhere" approach using Rust and Tauri 2.0. This strategy allows us to:
- Maintain a single codebase for all platforms
- Share cryptographic and business logic
- Provide consistent security across devices
- Adapt the UI for optimal experience on each platform
graph TD
subgraph "Unified Codebase"
Core[QuietDrop Core]
Frontend[Yew Frontend]
Backend[Tauri Backend]
end
Core --> Backend
Frontend --> Backend
subgraph "Desktop Platforms"
Backend --> Windows
Backend --> macOS
Backend --> Linux
end
subgraph "Mobile Platforms"
Backend --> Android
Backend --> iOS
end
The following matrix outlines the capabilities we're targeting across different platforms. This represents our development roadmap rather than current functionality. As QuietDrop evolves, we'll prioritize implementing these features while maintaining consistent security and user experience across all supported platforms.
Feature | Windows | macOS | Linux | Android | iOS |
---|---|---|---|---|---|
Encryption | ✅ | ✅ | ✅ | ✅ | ✅ |
Messaging | ✅ | ✅ | ✅ | ✅ | ✅ |
File Transfer | ✅ | ✅ | ✅ | ✅ | ✅ |
Multiple Windows | ✅ | ✅ | ✅ | ❌ | ❌ |
System Tray | ✅ | ✅ | ✅ | ❌ | ❌ |
Notifications | ✅ | ✅ | ✅ | ✅ | ✅ |
Background Service | ✅ | ✅ | ✅ | ||
Biometric Auth | ❌ | ✅ | ❌ | ✅ | ✅ |
Secure Storage | ✅ | ✅ | ✅ | ✅ | ✅ |
Camera Access | ❌ | ❌ | ❌ | ✅ | ✅ |
✅ = Fully supported
To develop for all platforms, you'll need:
# Install Rust and core tools
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown
cargo install trunk
cargo install tauri-cli
# Clone the repository
git clone https://github.com/chizy7/QuietDrop.git
cd QuietDrop
Then follow the platform-specific setups as needed.
Refer to the comprehensive setup instructions in the Tauri Integration page for detailed platform requirements.
QuietDrop uses several strategies to manage cross-platform code:
The quietdrop-core
crate contains platform-independent code:
quietdrop-core/
├── src/
│ ├── authentication.rs # Authentication logic
│ ├── encryption.rs # Cryptography operations
│ ├── message.rs # Message handling
│ ├── client.rs # Client communication
│ ├── server.rs # Server operations
│ └── lib.rs # Public API
└── Cargo.toml
This core library is used by all platforms and contains essential encryption and messaging logic.
We use Rust's conditional compilation to handle platform-specific code:
// Platform detection
#[cfg(desktop)]
fn desktop_specific_function() {
// Code that only compiles for desktop targets
}
#[cfg(target_os = "android")]
fn android_specific_function() {
// Android-specific code
}
#[cfg(target_os = "ios")]
fn ios_specific_function() {
// iOS-specific code
}
// For features available on both mobile platforms
#[cfg(any(target_os = "android", target_os = "ios"))]
fn mobile_specific_function() {
// Code for both Android and iOS
}
Platform-specific implementations behind common interfaces:
// Define a common trait
trait SecureStorage {
fn save_key(&self, key_name: &str, data: &[u8]) -> Result<(), Error>;
fn load_key(&self, key_name: &str) -> Result<Vec<u8>, Error>;
}
// Desktop implementation
#[cfg(desktop)]
struct DesktopSecureStorage;
#[cfg(desktop)]
impl SecureStorage for DesktopSecureStorage {
fn save_key(&self, key_name: &str, data: &[u8]) -> Result<(), Error> {
// Desktop-specific implementation
}
fn load_key(&self, key_name: &str) -> Result<Vec<u8>, Error> {
// Desktop-specific implementation
}
}
// Android implementation
#[cfg(target_os = "android")]
struct AndroidSecureStorage;
#[cfg(target_os = "android")]
impl SecureStorage for AndroidSecureStorage {
fn save_key(&self, key_name: &str, data: &[u8]) -> Result<(), Error> {
// Android-specific implementation
}
fn load_key(&self, key_name: &str) -> Result<Vec<u8>, Error> {
// Android-specific implementation
}
}
// Factory function to create the appropriate implementation
fn create_secure_storage() -> Box<dyn SecureStorage> {
#[cfg(desktop)]
return Box::new(DesktopSecureStorage);
#[cfg(target_os = "android")]
return Box::new(AndroidSecureStorage);
#[cfg(target_os = "ios")]
return Box::new(IOSSecureStorage);
}
QuietDrop uses a responsive design approach with Yew components:
#[function_component(AdaptiveLayout)]
fn adaptive_layout() -> Html {
let platform = use_platform_detection();
html! {
<div class={classes!(
"container",
match platform {
Platform::Desktop => "desktop-container",
Platform::Mobile => "mobile-container",
Platform::Tablet => "tablet-container"
}
)}>
{
match platform {
Platform::Desktop => html! { <DesktopLayout /> },
Platform::Mobile => html! { <MobileLayout /> },
Platform::Tablet => html! { <TabletLayout /> }
}
}
</div>
}
}
// Platform detection hook
#[hook]
fn use_platform_detection() -> Platform {
// Implementation that detects the current platform
// This can use window size, user agent, or other factors
}
In addition to conditional rendering, use responsive CSS:
/* Base styles for all platforms */
.message-container {
display: flex;
flex-direction: column;
}
/* Desktop-specific styles */
@media (min-width: 768px) {
.message-container {
max-width: 800px;
margin: 0 auto;
}
.message-input {
display: flex;
gap: 1rem;
}
}
/* Mobile-specific styles */
@media (max-width: 767px) {
.message-container {
width: 100%;
padding: 0 1rem;
}
.message-input {
display: flex;
flex-direction: column;
}
}
Adapt to different input methods based on platform:
#[function_component(MessageComposer)]
fn message_composer() -> Html {
let platform = use_platform_detection();
html! {
<div class="composer">
{
if let Platform::Desktop = platform {
// Desktop-friendly input with keyboard shortcuts
html! { <DesktopComposer /> }
} else {
// Touch-friendly input for mobile
html! { <MobileComposer /> }
}
}
</div>
}
}
QuietDrop uses different storage strategies depending on platform constraints:
#[tauri::command]
fn store_encryption_keys(
public_key: Vec<u8>,
private_key: Vec<u8>
) -> Result<(), String> {
#[cfg(desktop)]
{
// Use platform keychain/secure storage on desktop
#[cfg(target_os = "windows")]
windows_dpapi_store(&public_key, &private_key)?;
#[cfg(target_os = "macos")]
keychain_store(&public_key, &private_key)?;
#[cfg(target_os = "linux")]
secret_service_store(&public_key, &private_key)?;
}
#[cfg(target_os = "android")]
{
// Use Android Keystore
android_keystore_store(&public_key, &private_key)?;
}
#[cfg(target_os = "ios")]
{
// Use iOS Keychain
ios_keychain_store(&public_key, &private_key)?;
}
Ok(())
}
// Initialize database with platform-specific path
fn initialize_database() -> Result<Connection, Error> {
let db_path = get_platform_database_path()?;
let connection = Connection::open(db_path)?;
// Create tables and indices
connection.execute(
"CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
sender TEXT NOT NULL,
recipient TEXT NOT NULL,
content BLOB NOT NULL,
timestamp TEXT NOT NULL
)",
[],
)?;
Ok(connection)
}
fn get_platform_database_path() -> Result<PathBuf, Error> {
#[cfg(desktop)]
{
// Desktop path with fewer restrictions
let mut path = dirs::data_dir().ok_or(Error::DirectoryNotFound)?;
path.push("QuietDrop");
path.push("messages.db");
std::fs::create_dir_all(path.parent().unwrap())?;
Ok(path)
}
#[cfg(mobile)]
{
// Mobile path within app sandbox
let app_dir = tauri::api::path::app_dir(&Context::default())
.ok_or(Error::DirectoryNotFound)?;
let mut path = app_dir;
path.push("messages.db");
Ok(path)
}
}
#[cfg(desktop)]
fn setup_system_tray(app: &mut App) -> Result<(), Error> {
use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
let hide = CustomMenuItem::new("hide".to_string(), "Hide Window");
let tray_menu = SystemTrayMenu::new()
.add_item(quit)
.add_item(hide);
let tray = SystemTray::new().with_menu(tray_menu);
app.system_tray(tray);
Ok(())
}
#[cfg(desktop)]
#[tauri::command]
fn open_settings_window(app: tauri::AppHandle) -> Result<(), String> {
tauri::WindowBuilder::new(
&app,
"settings",
tauri::WindowUrl::App("settings.html".into())
)
.title("QuietDrop Settings")
.inner_size(600.0, 400.0)
.build()
.map_err(|e| e.to_string())?;
Ok(())
}
#[cfg(mobile)]
#[tauri::command]
async fn register_for_push_notifications(app: tauri::AppHandle) -> Result<(), String> {
#[cfg(target_os = "android")]
{
// Android FCM registration
let push_manager = app.push_notification_manager();
let token = push_manager.register().await?;
// Send token to server for message delivery
send_token_to_server(&token).await?;
}
#[cfg(target_os = "ios")]
{
// iOS APNS registration
let push_manager = app.push_notification_manager();
let token = push_manager.register().await?;
// Send token to server for message delivery
send_token_to_server(&token).await?;
}
Ok(())
}
#[cfg(mobile)]
#[tauri::command]
async fn authenticate_with_biometrics(app: tauri::AppHandle) -> Result<bool, String> {
#[cfg(target_os = "android")]
{
// Android biometric authentication
let biometric = app.biometric();
let result = biometric
.authenticate("Verify your identity to access messages")
.await?;
Ok(result.authenticated)
}
#[cfg(target_os = "ios")]
{
// iOS biometric authentication (Touch ID/Face ID)
let biometric = app.biometric();
let result = biometric
.authenticate("Verify your identity to access messages")
.await?;
Ok(result.authenticated)
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
Err("Biometric authentication not supported on this platform".to_string())
}
}
Use conditional compilation in tests:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_core_encryption() {
// Test encryption/decryption logic that works on all platforms
}
#[test]
#[cfg(desktop)]
fn test_desktop_specific_feature() {
// Test desktop-only functionality
}
#[test]
#[cfg(mobile)]
fn test_mobile_specific_feature() {
// Test mobile-only functionality
}
}
Set up automated testing for each target platform:
# .github/workflows/cross-platform-tests.yml
name: Cross-Platform Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test-desktop:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Run tests
run: cargo test --workspace
test-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Additional setup for Android testing
# ...
test-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
# Additional setup for iOS testing
# ...
// Optimize for mobile devices
#[cfg(mobile)]
fn configure_performance_settings(app: &mut App) {
// Reduce memory usage
app.config_mut().memory_cache_size = 10 * 1024 * 1024; // 10MB
// Implement lazy loading for message history
app.config_mut().message_page_size = 20;
// Reduce image quality for thumbnails
app.config_mut().image_thumbnail_quality = 60;
}
// Optimize for desktop capabilities
#[cfg(desktop)]
fn configure_performance_settings(app: &mut App) {
// Allow more memory usage
app.config_mut().memory_cache_size = 100 * 1024 * 1024; // 100MB
// Pre-fetch more message history
app.config_mut().message_page_size = 100;
// Higher quality thumbnails
app.config_mut().image_thumbnail_quality = 80;
}
# Build for Windows, macOS, and Linux
cd quietdrop-tauri
cargo tauri build
# Build for Android
cd quietdrop-tauri
cargo tauri android build
# Build for iOS
cd quietdrop-tauri
cargo tauri ios build
- Windows: MSI installer and portable EXE
- macOS: DMG installer and App Store
- Linux: AppImage, DEB, and RPM packages
- Android: APK file and Google Play Store
- iOS: App Store distribution
For detailed deployment instructions, see:
# Generate Windows MSI installer
cargo tauri build --target x86_64-pc-windows-msvc
# Generate macOS DMG
cargo tauri build --target x86_64-apple-darwin
# Generate Linux AppImage
cargo tauri build --target x86_64-unknown-linux-gnu
# Android APK
cargo tauri android build --release
# iOS IPA
cargo tauri ios build --release
Problem: Desktop uses multi-window navigation, while mobile uses tab-based navigation.
Solution: Implement platform-specific navigation controllers:
#[function_component(Navigation)]
fn navigation() -> Html {
let platform = use_platform_detection();
match platform {
Platform::Desktop => html! {
<DesktopNavigation>
<MainWindow />
<SettingsButton on_click={open_settings_window} />
<ContactsButton on_click={open_contacts_window} />
</DesktopNavigation>
},
Platform::Mobile => html! {
<MobileNavigation>
<TabBar>
<Tab id="messages" icon="message" title="Messages" />
<Tab id="contacts" icon="people" title="Contacts" />
<Tab id="settings" icon="settings" title="Settings" />
</TabBar>
<TabContent />
</MobileNavigation>
}
}
}
Problem: Desktop uses keyboard/mouse, while mobile uses touch input.
Solution: Adjust UI element sizes and interaction patterns:
/* Touch-friendly buttons on mobile */
.action-button {
padding: 8px 16px;
}
@media (max-width: 767px) {
.action-button {
padding: 12px 20px; /* Larger touch target */
margin-bottom: 12px; /* More space between buttons */
}
}
Problem: Mobile platforms have stricter storage limitations and sandbox restrictions.
Solution: Implement adaptive storage strategies:
fn get_message_storage_settings() -> MessageStorageSettings {
#[cfg(desktop)]
{
MessageStorageSettings {
max_storage_size: 1024 * 1024 * 1024, // 1GB
keep_messages_days: 365, // Keep for a year
auto_download_attachments: true,
}
}
#[cfg(mobile)]
{
MessageStorageSettings {
max_storage_size: 100 * 1024 * 1024, // 100MB
keep_messages_days: 30, // Keep for a month
auto_download_attachments: false, // Manual download to save data
}
}
}
- Design for the Most Constrained Platform First: Start with mobile constraints, then enhance for desktop
- Use Responsive Design Patterns: Create flexible layouts that adapt to different screen sizes
- Abstract Platform Differences: Use traits/interfaces to hide implementation details
- Minimize Platform-Specific Code: Keep as much logic as possible in the shared core
- Test on All Target Platforms: Regularly test on all supported platforms
- Consider Platform UX Expectations: Follow platform-specific UX guidelines
- Optimize Asset Sizes: Provide platform-appropriate assets (icons, images, etc.)
- Handle Offline States: Design for intermittent connectivity, especially on mobile
- Architecture: Overall architecture of QuietDrop
- Tauri Integration: Tauri 2.0 implementation details
- Mobile Usage Guide: Mobile-specific user guide
- Tauri 2.0 Documentation: Official Tauri framework documentation
QuietDrop Wiki | Home | Getting Started | FAQ | Security Model | Architecture | Development Guide
Main Repository | Report Issues | Contributing
© 2023-2025 QuietDrop Contributors | MIT License
Last updated: April 2025