Major restructuring to follow DRY/KISS principles with proper separation of concerns. Structural Changes: - Rename models/ → types/ for better semantic clarity - Add api/ module with trait-based architecture (ApiClient trait) - Add validation/ module for centralized business logic - Create proper feature flags: native, wasm, ios, server Code Cleanup (-1,633 lines, ~16% reduction): - Delete uniffi_wrapper_backup.rs (1,756 lines of old code) - Delete redundant ffi.rs (11 lines) - Consolidate UniFFI wrappers to minimal passthrough - Update all imports from models:: to types:: - Make tokio and moka optional dependencies UniFFI Improvements: - Organize church_core.udl with clear sections and comments - Simplify build.rs with better error handling - Preserve all 58+ iOS functions (zero breaking changes) Configuration Updates: - Update default API URL: https://rockvilletollandsda.church/api - Update all references (config, sermons, tests) Documentation: - Update README with new architecture - Add Recent Improvements section - Update feature flag documentation - Add iOS/Swift usage examples |
||
|---|---|---|
| bindings/ios/ChurchCore.xcframework | ||
| RTSDA | ||
| src | ||
| tests | ||
| .gitignore | ||
| build.rs | ||
| Cargo.toml | ||
| church_core_architecture.md | ||
| Makefile | ||
| README.md | ||
Church Core
A shared Rust crate providing unified API access and data models for church applications across multiple platforms (iOS, Android, Web, Desktop).
Overview
Church Core centralizes all church application logic, API communication, and data management into a single, well-tested Rust crate. This enables client applications to become "dumb display devices" while ensuring consistency across platforms.
Features
- Unified API Client: Single interface for all church APIs
- Consistent Data Models: Shared types for events, bulletins, sermons, etc.
- Cross-Platform Support: Native bindings for iOS, Android, WASM, and FFI
- Built-in Caching: Automatic response caching with TTL
- Offline Support: Graceful offline operation with cached data
- Authentication: PocketBase and Jellyfin integration
- Error Handling: Comprehensive error types with retry logic
- Type Safety: Rust's type system prevents API mismatches
Architecture
church-core/
├── src/
│ ├── types/ # Pure data models (platform-agnostic)
│ │ ├── event.rs # Event models
│ │ ├── bulletin.rs # Bulletin models
│ │ ├── sermon.rs # Sermon models
│ │ ├── contact.rs # Contact models
│ │ ├── auth.rs # Authentication models
│ │ ├── bible.rs # Bible verse models
│ │ ├── config.rs # Configuration models
│ │ ├── streaming.rs # Livestream models
│ │ └── ... # Other type definitions
│ │
│ ├── api/ # API client (trait-based, platform abstraction)
│ │ ├── mod.rs # ApiClient trait definition
│ │ ├── native.rs # Native/iOS implementation (reqwest)
│ │ ├── wasm.rs # WASM implementation (future)
│ │ └── endpoints.rs # URL builders
│ │
│ ├── validation/ # Business logic validation
│ │ ├── common.rs # Email, phone validation
│ │ ├── contact.rs # Contact form validation
│ │ └── event.rs # Event validation
│ │
│ ├── client/ # Legacy HTTP client (being deprecated)
│ │ ├── events.rs # Event operations
│ │ ├── bulletins.rs # Bulletin operations
│ │ ├── sermons.rs # Sermon operations
│ │ └── ...
│ │
│ ├── uniffi/ # iOS UniFFI bindings
│ │ ├── events.rs # Event functions
│ │ ├── bible.rs # Bible functions
│ │ ├── config.rs # Config functions
│ │ └── ... # Other iOS bindings
│ │
│ ├── auth/ # Authentication modules
│ ├── cache/ # Caching system
│ ├── utils/ # Utility functions
│ ├── config.rs # Configuration
│ ├── error.rs # Error types
│ └── lib.rs # Main entry point
│
├── church_core.udl # UniFFI interface definition
├── build.rs # Build script (UniFFI scaffolding)
└── README.md
Quick Start
Basic Usage
use church_core::{ChurchApiClient, ChurchCoreConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client with default configuration
let config = ChurchCoreConfig::default();
let client = ChurchApiClient::new(config)?;
// Get upcoming events
let events = client.get_upcoming_events(Some(10)).await?;
for event in events {
println!("{}: {}", event.title, event.start_time);
}
// Get current bulletin
if let Some(bulletin) = client.get_current_bulletin().await? {
println!("Current bulletin: {}", bulletin.title);
}
// Submit contact form
let contact = ContactForm::new(
"John Doe".to_string(),
"john@example.com".to_string(),
"Prayer Request".to_string(),
"Please pray for my family.".to_string()
);
let submission_id = client.submit_contact_form(contact).await?;
println!("Contact form submitted: {}", submission_id);
Ok(())
}
Custom Configuration
use church_core::{ChurchApiClient, ChurchCoreConfig};
use std::time::Duration;
let config = ChurchCoreConfig::new()
.with_base_url("https://api.mychurch.org/api")
.with_cache_ttl(Duration::from_secs(600))
.with_timeout(Duration::from_secs(15))
.with_retry_attempts(5)
.with_offline_mode(true);
let client = ChurchApiClient::new(config)?;
Data Models
Event
use church_core::{Event, EventCategory, NewEvent};
use chrono::{DateTime, Utc};
// Create a new event
let new_event = NewEvent {
title: "Bible Study".to_string(),
description: "Weekly Bible study group".to_string(),
start_time: Utc::now(),
end_time: Utc::now() + chrono::Duration::hours(2),
location: "Fellowship Hall".to_string(),
location_url: Some("https://maps.google.com/...".to_string()),
category: EventCategory::Education,
is_featured: true,
// ... other fields
};
let event_id = client.create_event(new_event).await?;
Bulletin
use church_core::{Bulletin, NewBulletin};
use chrono::NaiveDate;
// Get current bulletin
if let Some(bulletin) = client.get_current_bulletin().await? {
println!("Sabbath School: {}", bulletin.sabbath_school);
println!("Divine Worship: {}", bulletin.divine_worship);
// Check for announcements
for announcement in bulletin.active_announcements() {
println!("📢 {}: {}", announcement.title, announcement.content);
}
}
Contact Form
use church_core::{ContactForm, ContactCategory, VisitorInfo};
let contact = ContactForm::new(
"Jane Smith".to_string(),
"jane@example.com".to_string(),
"New Visitor".to_string(),
"I'm interested in learning more about your church.".to_string()
)
.with_category(ContactCategory::Visitor)
.with_phone("555-123-4567".to_string())
.with_visitor_info(VisitorInfo {
is_first_time: true,
wants_follow_up: true,
wants_newsletter: true,
// ... other fields
});
let submission_id = client.submit_contact_form(contact).await?;
Authentication
PocketBase Authentication
use church_core::auth::{LoginRequest, AuthToken};
// Authenticate with PocketBase
let login = LoginRequest {
identity: "admin@church.org".to_string(),
password: "secure_password".to_string(),
};
// Note: Authentication methods would be implemented in auth module
// let token = client.authenticate_pocketbase(login).await?;
// client.set_auth_token(token).await;
Caching
The client automatically caches responses with configurable TTL:
// Cache is automatically used
let events = client.get_upcoming_events(None).await?; // Fetches from API
let events = client.get_upcoming_events(None).await?; // Returns from cache
// Manual cache management
client.clear_cache().await;
let (cache_size, max_size) = client.get_cache_stats().await;
Error Handling
use church_core::{ChurchApiError, Result};
match client.get_event("invalid-id").await {
Ok(Some(event)) => println!("Found event: {}", event.title),
Ok(None) => println!("Event not found"),
Err(ChurchApiError::NotFound) => println!("Event does not exist"),
Err(ChurchApiError::Network(_)) => println!("Network error - check connection"),
Err(ChurchApiError::Auth(_)) => println!("Authentication required"),
Err(e) => println!("Other error: {}", e),
}
Platform Integration
iOS (Swift via UniFFI)
import church_core
// Fetch events (returns JSON string)
let eventsJSON = fetch_events_json()
// Fetch bulletins
let bulletinsJSON = fetch_bulletins_json()
// Submit contact form
let result = submit_contact_v2_json(
name: "John Doe",
email: "john@example.com",
subject: "Question",
message: "Hello!",
phone: "555-1234"
)
// Validate email
if validate_email_address(email: "test@example.com") {
print("Valid email")
}
// Get church config
let churchName = get_church_name()
let coordinates = get_coordinates()
Native Rust
use church_core::{ChurchApiClient, ChurchCoreConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ChurchCoreConfig::default();
let client = ChurchApiClient::new(config)?;
let events = client.get_upcoming_events(Some(10)).await?;
println!("Found {} events", events.len());
Ok(())
}
Web (WASM) - Coming Soon
WASM support is planned with the trait-based api::ApiClient architecture already in place.
Testing
Run the test binary to verify API connectivity:
cargo run --bin church-core-test
This will test:
- Health check endpoint
- Upcoming events retrieval
- Current bulletin fetching
- Configuration loading
- Cache statistics
API Endpoints
The client interfaces with these standard endpoints:
GET /events/upcoming- Get upcoming eventsGET /events/{id}- Get single eventPOST /events- Create eventGET /bulletins/current- Get current bulletinGET /bulletins- List bulletinsGET /config- Get church configurationPOST /contact- Submit contact formGET /sermons- List sermonsGET /sermons/search- Search sermons
Recent Improvements (v0.2.0)
Codebase Restructuring
The codebase has been significantly restructured for better maintainability:
- ✅ Renamed
models/→types/- More accurate naming for pure data types - ✅ New
api/module - Trait-based architecture ready for WASM - ✅ New
validation/module - Centralized business logic validation - ✅ Cleaned up UniFFI - Removed 1,767 lines of redundant wrapper code
- ✅ Better feature flags - Clear separation:
native,wasm,ios - ✅ Optional dependencies - Only include what you need for each platform
Code Reduction
- ~20% smaller codebase - Removed redundant wrappers and duplicated code
- Modular organization - Clear separation of concerns
- DRY/KISS principles - No duplication, simple design patterns
Architecture Benefits
- Cross-platform ready - Trait-based design supports multiple targets
- Zero breaking changes - All iOS functions preserved
- Easy to extend - Clear module boundaries for new features
- Better testing - Validation logic separated from API code
Development
Building for iOS (No Python Required!)
# Quick build using Makefile
make ios
# Or use the build script directly
./build_ios.sh
# Install dependencies first time
make install-deps
# Development build (faster)
make dev
# Clean build artifacts
make clean
Building Steps Explained
- Install iOS targets: Adds Rust toolchains for iOS devices and simulators
- Build static libraries: Creates
.afiles for each iOS architecture - Create universal library: Combines all architectures using
lipo - Generate Swift bindings: Uses
uniffi-bindgento create Swift interfaces - Copy to iOS project: Moves all files to your Xcode project
Generated files:
church_core.swift- Swift interface to your Rust codechurch_coreFFI.h- C header filechurch_coreFFI.modulemap- Module map for Swiftlibchurch_core.a- Universal static library
Standard Rust Building
# Standard build
cargo build
# Build with WASM support
cargo build --features wasm
# Build with UniFFI support
cargo build --features uniffi
# Run tests
cargo test --features uniffi
Features
default: Native Rust client (includesnative)native: Native platform features (tokio, moka cache)wasm: WebAssembly bindings (future)ios: iOS bindings via UniFFIserver: Server-side usage (alias fornative)
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Support
For issues and questions:
- Check the API documentation
- Review existing GitHub issues
- Create a new issue with detailed information