# Background Data Fetching Implementation Plan **Author**: Matthew Raymer **Version**: 1.0.0 **Created**: 2025-10-02 07:47:04 UTC **Last Updated**: 2025-10-02 12:30:00 UTC ## Overview This document outlines the implementation plan for background data fetching in the Capacitor Daily Notification Plugin, replacing web push implementations with native platform solutions. The plan covers Android, iOS, and cross-platform considerations for API data fetching with authentication. ## Platform Architecture Overview ``` JavaScript Layer → Native Bridge → Native Background Executor ↓ HTTP Client + Auth ↓ API Server Response ↓ Parse & Cache Data ↓ Trigger Notifications ``` ## Android Implementation Strategy ### A. Background Execution Framework - **Use WorkManager** for reliable background HTTP requests - **Replace axios** with native Android HTTP clients: - OkHttp for synchronous requests - Retrofit for type-safe API interfaces - **Handle Android-specific constraints**: Doze mode, app standby, battery optimization ### B. Authentication Implementation ```kotlin // JWT Generation in Android - Enhanced with DID support class JWTHelper { fun generateJWT(userDid: String, expiresInSeconds: Int): String { val payload = mapOf( "exp" to (System.currentTimeMillis() / 1000 + expiresInSeconds), "iat" to (System.currentTimeMillis() / 1000), "iss" to userDid, // Include DID-specific claims for verification "aud" to "timesafari.notifications", "sub" to userDid ) return signWithDID(payload, userDid) } // Enhanced authentication with Passkey support fun generateJWANT(userDid: String, biometricData: ByteArray): String { val payload = mapOf( "exp" to (System.currentTimeMillis() / 1000 + 3600), // 1 hour "iat" to (System.currentTimeMillis() / 1000), "iss" to userDid, "aud" to "timesafari.notifications", "sub" to userDid, "auth_data" to android.util.Base64.encodeToString(biometricData, android.util.Base64.NO_WRAP) ) return signWithDIDPasskey(payload, userDid, biometricData) } } ### C. HTTP Request Implementation ```kotlin // Background HTTP Worker class DataFetchWorker : CoroutineWorker { suspend fun doWork(): Result { val jwt = generateJWT(activeDid, 60) val headers = mapOf( "Authorization" to "Bearer $jwt", "Content-Type" to "application/json" ) val offersResponse = httpClient.get("$apiServer/api/v2/report/offers") { headers { headers.forEach { append(it.key, it.value) } } parameter("recipientId", activeDid) // Use activeDid for recipient filtering parameter("afterId", lastKnownOfferId) } return storeAndScheduleNotification(offersResponse.body()) } } ``` ## iOS Implementation Strategy ### A. Background Execution Framework - **Use BGTaskScheduler** for background HTTP requests - **Replace axios** with native iOS HTTP clients: - URLSession for HTTP requests - Combine framework for async/await patterns ### B. Authentication Implementation ```swift // JWT Generation in iOS class JWTHelper { func generateJWT(userDid: String, expiresInSeconds: Int) -> String { let payload: [String: Any] = [ "exp": Int(Date().timeIntervalSince1970) + expiresInSeconds, "iat": Int(Date().timeIntervalSince1970), "iss": userDid ] return signWithDID(payload, userDid) } } ``` ### C. HTTP Request Implementation ```swift // Background HTTP Task class DataFetchTask { func fetchData() async { let jwt = generateJWT(userDid: activeDid, expiresInSeconds: 60) var request = URLRequest(url: apiURL) request.setValue("Bearer \(jwt)", forHTTPHeaderField: "Authorization") request.setValue("application/json", forHTTPHeaderField: "Content-Type") do { let (data, _) = try await URLSession.shared.data(for: request) let offersResponse = try JSONDecoder().decode(OffersResponse.self, from: data) await scheduleNotification(with: offersResponse.data) } catch { // Handle errors } } } ``` ## Data Models & Type Safety ### Shared TypeScript Interfaces ```typescript // Definitions for native bridge interface OffersResponse { data: OfferSummaryRecord[]; hitLimit: boolean; } interface OfferSummaryRecord { jwtId: string; handleId: string; issuedAt: string; offeredByDid: string; recipientDid: string; unit: string; amount: number; // ... other fields } ``` ### Native Implementations - **Kotlin sealed classes** for type-safe responses - **Swift Codable structs** for JSON parsing - **Shared error handling** patterns ## Configuration Management ### Plugin Configuration ```typescript interface PluginConfig { apiServer: string; jwtExpirationSeconds: number; requestTimeoutMs: number; retryAttempts: number; activeDid: string; // Simplified to single active DID lastKnownOfferId?: string; lastKnownPlanId?: string; } ``` ### Platform-Specific Settings - **Android**: Manage API keys in `AndroidManifest.xml`, use SharedPreferences for runtime config - **iOS**: Use `Info.plist` for static config, UserDefaults for runtime settings ## Error Handling & Resilience ### Network Error Handling - **Connectivity checks** before making requests - **Exponential backoff** for retry scenarios - **Circuit breaker pattern** for API failures - **Graceful degradation** when offline ### Authentication Error Handling - **Token refresh** mechanisms - **Fallback to anonymous** requests when authentication fails - **Secure credential storage** using platform keychains ## Cache & State Management ### Data Persistence #### **Platform-Specific Storage Architecture** **Android/Electron Platforms:** - **@capacitor-community/sqlite** plugin integration for native SQLite access - **Shared plugin database** - Plugin manages its own SQLite database instance - **Direct SQL execution** via plugin's `dbExec()` methods for complex queries - **Background worker integration** for database operations during content fetch **Web Platform:** - **absurd-sql** for SQLite support in browser (managed by host application) - **Plugin delegation pattern** - Plugin provides SQL queries, host executes them - **IndexedDB fallback** for basic caching when SQLite unavailable **iOS Platform:** - **Core Data integration** via native Swift implementation - **Background task compatibility** with iOS background refresh constraints ### State Synchronization - **JavaScript → Native** configuration updates - **Native → JavaScript** status reporting - **Cross-platform state consistency** - **Background ↔ Foreground** state synchronization - **Database logging** for audit trails and debugging ### Enhanced Caching Strategy Based on TimeSafari's optimization patterns: - **Batch-oriented processing** for API requests to reduce overhead - **Intelligent batching** with configurable timing (max 100ms wait, max 10 items) - **Memory-optimized caching** with automatic cleanup (keep last 1000 log entries) - **Request deduplication** to prevent redundant API calls - **Performance monitoring** with operation timing and metrics collection ## Performance Optimizations ### Request Optimization - **Deduplication** of identical requests - **Batch requests** when possible - **Intelligent polling** based on user activity ### Memory Management - **Background memory limits** enforcement - **Cache cleanup** on memory pressure - **Resource lifecycle** management ## Integration Points ### Enhanced Plugin Interface for Host Application Integration #### **Database Integration Patterns** **Android/Electron: Host-Provided activeDid Approach (Option A)** ```typescript // Host queries its own database and provides activeDid const activeIdentity = await this.$getActiveIdentity(); // Uses host's CapacitorSQLite await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin configures its own isolated database await plugin.configureDatabase({ platform: 'android', storageType: 'plugin-managed' // Plugin owns its storage }); // Set up activeDid change listener for future changes plugin.onActiveDidChange(async (newActiveDid) => { await plugin.clearCacheForNewIdentity(); await plugin.refreshAuthenticationForNewIdentity(newActiveDid); logger.info(`[TimeSafari] ActiveDid changed to: ${newActiveDid}`); }); ``` **Web: Host-Provided activeDid Approach (Option A)** ```typescript // Host queries its absurd-sql database and provides activeDid const activeIdentity = await this.$getActiveIdentity(); // Uses host's absurd-sql await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin uses host-delegated storage for its own data await plugin.configureDatabase({ platform: 'web', storageType: 'host-managed' // Plugin delegates to host for storage }); // Plugin operates independently with provided activeDid const results = await plugin.executeContentFetch(contentConfig); ``` **iOS: Host-Provided activeDid Approach (Option A)** ```typescript // Host queries its CapacitorSQLite database and provides activeDid const activeIdentity = await this.$getActiveIdentity(); // Uses host's CapacitorSQLite await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin configures its own Core Data storage await plugin.configureDatabase({ platform: 'ios', storageType: 'plugin-managed' // Plugin owns Core Data storage }); // Plugin operates with provided activeDid, no database sharing needed const results = await plugin.executeBackgroundContentFetch(); ``` #### **New Plugin Methods Required** ```typescript interface EnhancedDailyNotificationPlugin { // Database configuration - Simplified Option A approach configureDatabase(options: { platform: 'android' | 'ios' | 'web' | 'electron'; storageType: 'plugin-managed' | 'host-managed'; dbPath?: string; encryption?: boolean; }): Promise; // Host-provided activeDid Management (Option A Implementation) setActiveDidFromHost(activeDid: string): Promise; // Content fetch with database integration fetchAndStoreContent(config: ContentFetchConfig): Promise; // Data access for host application getStoredContent(query: string, params?: any[]): Promise; clearStoredContent(options?: { olderThan?: number }): Promise; // Background task coordination getBackgroundTaskStatus(): Promise; pauseBackgroundTasks(): Promise; resumeBackgroundTasks(): Promise; // TimeSafari Integration Methods initializeFromTimeSafari(): Promise; listenForActiveDidChanges(): Promise; // Critical: ActiveDid Change Handling onActiveDidChange(callback: (newActiveDid: string) => Promise): void; clearCacheForNewIdentity(): Promise; refreshAuthenticationForNewIdentity(activeDid: string): Promise; } ``` ### Background Scheduling with Hybrid activeDid Management - **Integrate** with existing WorkManager/BGTaskScheduler - **Coordinate** API fetch timing with notification schedules - **Handle** app lifecycle events (background/foreground) - **Implement** host-provided activeDid access (Option A): - **Always**: Host provides activeDid via `setActiveDidFromHost()` - **No Database Sharing**: Plugin never accesses TimeSafari's active_identity table - **Critical**: Plugin **MUST** know when activeDid changes for: - **Event-Based Notification**: Host dispatches `activeDidChanged` events - **Cache Invalidation**: Clear cached content when user switches identity - **Token Refresh**: Generate new JWT tokens with updated active Did - **Background Task Coordination**: Restart tasks with new identity context - **Maintain** clear separation: Host owns identity management, plugin owns notifications ## Migration & Testing Strategy ### Gradual Migration 1. **Phase 1**: Implement basic HTTP + JWT authentication 2. **Phase 2**: Add caching and state management 3. **Phase 3**: Integrate with notification scheduling 4. **Phase 4**: Add passkey authentication support ### Testing Approach with Host-Provided activeDid Management - **Unit tests** for JWT generation and HTTP clients with activeDid - **Integration tests** for API endpoint interactions using TimeSafari active_identity patterns - **Host-provided activeDid testing**: - Test `setActiveDidFromHost()` with TimeSafari PlatformServiceMixin - Test host event dispatch and plugin event listening - **Critical**: Test `onActiveDidChange()` listener with identity switches - Test cache invalidation and token refresh during activeDid changes - Verify database isolation between host and plugin - **Background testing** on real devices (doze mode, app backgrounding) - **Authentication testing** with actual DID credentials from TimeSafari active_identity table - **Cross-platform testing** for Android/Electron (SQLite access) vs Web (host delegation) patterns ## API Endpoints to Support ### Offers to User Endpoint ``` GET {apiServer}/api/v2/report/offers?recipientDid={userDid}&afterId={jwtId}&beforeId={jwtId} ``` **Response Structure:** ```json { "data": Array, "hitLimit": boolean } ``` ### Offers to User Projects Endpoint ``` GET {apiServer}/api/v2/report/offersToPlansOwnedByMe?afterId={jwtId}&beforeId={jwtId} ``` **Response Structure:** ```json { "data": Array, "hitLimit": boolean } ``` ## Authentication Implementation Strategy ### Option 1: Simple DID Authentication (Basic) - Generate traditional JWT using DID signing - Short-lived tokens (60 seconds) - Suitable for basic notification data fetching - Use `did-jwt` library for token generation and verification - Based on TimeSafari's existing JWT implementation patterns ### Option 2: Enhanced Passkey Authentication (Advanced) - Leverage device biometrics/security keys - Longer-lived tokens with automatic refresh - Support for cached authentication state - Better user experience for frequent polling - Integrate with SimpleWebAuthn for cross-platform biometric support - Support JWANT tokens (JWT + WebAuthn) for enhanced security ## Platform-Specific Considerations ### Android Considerations - Use OkHttp or native Android HTTP clients - Handle certificate pinning if required - Support Android Keystore for secure key storage - Handle biometric prompt integration for passkeys ### iOS Considerations - Use URLSession for HTTP requests - Support iOS Keychain for authentication tokens - Handle Face ID/Touch ID integration for passkeys - Support certificate pinning if required - Use BGTaskScheduler for reliable background execution - Handle iOS-specific background refresh restrictions - Support Core Data for notification metadata persistence ## Data Flow Integration Points ### Token Generation - Accept activeDid as input - Generate JWT authentication token using DID signing - Include activeDid as both issuer (`iss`) and subject (`sub`) - Return token for immediate use or caching ### Request Execution - Construct full API URLs with query parameters - Apply authentication headers - Execute HTTP requests with proper error handling - Return structured response data ### Caching Strategy - Support token caching with expiration management - Implement request deduplication for same endpoints - Support cache invalidation for authentication failures ## Implementation Phases ### Phase 1: Core Infrastructure (Weeks 1-2) - Set up native HTTP clients for Android/iOS - Implement basic JWT generation - Create plugin configuration interfaces - Set up basic error handling ### Phase 2: Host-Provided activeDid Integration & API Access (Weeks 3-4) - Implement host-provided activeDid management (Option A approach) - Add `setActiveDidFromHost()` method (no database access needed) - Remove database sharing complexity - host always provides activeDid - Integrate API endpoint calls with activeDid-based authentication - Add response parsing and validation - Implement platform-specific database integration: - **Android/Electron**: Plugin-managed @capacitor-community/sqlite (no host database access) - **Web**: Host-managed storage delegation (plugin doesn't access absurd-sql directly) - **iOS**: Plugin-managed Core Data (no host database access) ### Phase 3: Background Integration (Weeks 5-6) - Integrate with WorkManager/BGTaskScheduler - Coordinate with notification scheduling - Handle app lifecycle events - Implement state synchronization ### Phase 4: Advanced Features (Weeks 7-8) - Add passkey authentication support - Implement advanced caching strategies - Optimize performance and memory usage - Add comprehensive testing ## Success Criteria - [ ] **Functional Requirements**: API data fetching works reliably in background - [ ] **Performance Requirements**: Requests complete within 30 seconds - [ ] **Security Requirements**: Secure credential storage and token management - [ ] **Reliability Requirements**: Handles network failures and offline scenarios - [ ] **Integration Requirements**: Seamless integration with existing plugin APIs - [ ] **Testing Requirements**: Comprehensive test coverage for all platforms - [ ] **Authentication Requirements**: Support both DID-based JWT and Passkey JWANT tokens - [ ] **Optimization Requirements**: Implement batch processing with sub-100ms delays - [ ] **Logging Requirements**: Structured logging with database persistence for debugging - [ ] **Cross-Platform Requirements**: Unified SQLite/IndexedDB storage across platforms ## Risks & Mitigation ### Technical Risks - **Background execution limits**: Mitigated by using platform-specific background task systems - **Authentication complexity**: Mitigated by implementing gradual migration path - **Cross-platform consistency**: Mitigated by shared interfaces and careful API design ### Timeline Risks - **Platform-specific complexity**: Mitigated by prioritizing Android first, then iOS - **Testing complexity**: Mitigated by automated testing and CI/CD integration - **Integration challenges**: Mitigated by maintaining backward compatibility --- **Status**: Host-provided activeDid implementation plan (Option A) - Ready for implementation **Next Steps**: Begin Phase 1 implementation with Android HTTP client setup **Dependencies**: Android Studio, Xcode, Capacitor CLI, existing plugin infrastructure, @capacitor-community/sqlite **Option A Features**: - **Simplified Architecture**: Host always provides activeDid, no database sharing - **TimeSafari Integration**: PlatformServiceMixin coordination without database access - **Cross-Platform**: Android/Electron plugin-managed SQLite, Web host-managed storage, iOS plugin-managed Core Data - **Authentication**: DID-based JWT with host-provided activeDid - **Background Tasks**: Event-based activeDid changes, clear separation of concerns