Replace the buggy temp file polling mechanism with direct native-to-JS communication via custom Capacitor plugins for iOS and Android. This eliminates race conditions, file management complexity, and polling overhead. BREAKING CHANGE: Removes backward compatibility with temp file approach. Android minSdkVersion upgraded from 22 to 23. Changes: - iOS: Created SharedImagePlugin (CAPBridgedPlugin) and SharedImageUtility - Uses App Group UserDefaults for data sharing between extension and app - Implements getSharedImage() and hasSharedImage() methods - Removes temp file writing/reading logic from AppDelegate - Android: Created SharedImagePlugin with @CapacitorPlugin annotation - Uses SharedPreferences for data storage instead of temp files - Implements same interface as iOS plugin - Removes temp file handling from MainActivity - TypeScript: Added plugin definitions and registration - Created SharedImagePlugin interface and web fallback - Updated main.capacitor.ts to use plugin instead of Filesystem API - Removed pollForFileExistence() and related file I/O code - Android: Upgraded minSdkVersion from 22 to 23 - Required for SharedPreferences improvements and better API support Benefits: - Eliminates race conditions from file polling - Removes file cleanup complexity - Direct native-to-JS communication (no file I/O) - Better performance (no polling overhead) - More reliable data transfer between share extension and app - Cleaner architecture with proper plugin abstraction
330 lines
11 KiB
Markdown
330 lines
11 KiB
Markdown
# Shared Image Plugin - Pre-Implementation Decision Checklist
|
|
|
|
**Date:** 2025-12-03
|
|
**Status:** Pre-Implementation Planning
|
|
**Purpose:** Identify and document decisions needed before implementing SharedImagePlugin
|
|
|
|
## ✅ Completed Decisions
|
|
|
|
### 1. Minimum OS Versions
|
|
- ✅ **iOS**: Keep at 13.0 (no changes needed)
|
|
- ✅ **Android**: Upgraded from API 22 to API 23 (completed)
|
|
- ✅ **Rationale**: Meets Capacitor 6 requirements, minimal device impact
|
|
|
|
### 2. Data Storage Strategy
|
|
- ✅ **iOS**: Use App Group UserDefaults (already implemented in Share Extension)
|
|
- ✅ **Android**: Use SharedPreferences (to be implemented)
|
|
- ✅ **Rationale**: Direct, efficient, no file I/O needed
|
|
|
|
## 🔍 Decisions Needed Before Implementation
|
|
|
|
### 1. Plugin Method Design
|
|
|
|
#### Decision: What methods should the plugin expose?
|
|
|
|
**Options:**
|
|
- **Option A (Minimal)**: Only `getSharedImage()` - read and clear in one call
|
|
- **Option B (Recommended)**: `getSharedImage()` + `hasSharedImage()` - allows checking without reading
|
|
- **Option C (Extended)**: Add `clearSharedImage()` - explicit clearing without reading
|
|
|
|
**Recommendation:** **Option B**
|
|
- `getSharedImage()`: Returns `{ base64: string, fileName: string } | null`
|
|
- `hasSharedImage()`: Returns `{ hasImage: boolean }` - useful for quick checks
|
|
|
|
**Decision Needed:** ✅ Confirm Option B or choose alternative
|
|
|
|
---
|
|
|
|
### 2. Error Handling Strategy
|
|
|
|
#### Decision: How should the plugin handle errors?
|
|
|
|
**Options:**
|
|
- **Option A**: Return `null` for all errors (no shared image = no error)
|
|
- **Option B**: Use `call.reject()` for actual errors, return `null` only when no image exists
|
|
- **Option C**: Return error object in result: `{ error: string } | { base64: string, fileName: string }`
|
|
|
|
**Recommendation:** **Option B**
|
|
- `getSharedImage()` returns `null` when no image exists (normal case)
|
|
- `call.reject()` for actual errors (UserDefaults unavailable, data corruption, etc.)
|
|
- Clear distinction between "no data" (normal) vs "error" (exceptional)
|
|
|
|
**Decision Needed:** ✅ Confirm Option B or choose alternative
|
|
|
|
---
|
|
|
|
### 3. Data Clearing Strategy
|
|
|
|
#### Decision: When should shared image data be cleared?
|
|
|
|
**Current Behavior (temp file approach):**
|
|
- Data cleared after reading (immediate)
|
|
|
|
**Options:**
|
|
- **Option A**: Clear immediately after reading (current behavior)
|
|
- **Option B**: Clear on next read (allow re-reading until consumed)
|
|
- **Option C**: Clear after successful storage in temp DB (JS confirms receipt)
|
|
|
|
**Recommendation:** **Option A** (immediate clearing)
|
|
- Prevents accidental re-reading
|
|
- Simpler implementation
|
|
- Matches current behavior
|
|
- If JS fails to store, user can share again
|
|
|
|
**Decision Needed:** ✅ Confirm Option A or choose alternative
|
|
|
|
---
|
|
|
|
### 4. iOS Plugin Registration
|
|
|
|
#### Decision: How should the iOS plugin be registered?
|
|
|
|
**Options:**
|
|
- **Option A**: Auto-discovery (Capacitor finds plugins by naming convention)
|
|
- **Option B**: Manual registration in AppDelegate
|
|
- **Option C**: Hybrid (auto-discovery with manual registration as fallback)
|
|
|
|
**Recommendation:** **Option A** (auto-discovery)
|
|
- Follows Capacitor best practices
|
|
- Less code to maintain
|
|
- Other plugins in project use auto-discovery (SafeAreaPlugin uses manual, but that's older pattern)
|
|
|
|
**Note:** Need to verify plugin naming convention:
|
|
- Class name: `SharedImagePlugin`
|
|
- File name: `SharedImagePlugin.swift`
|
|
- Location: `ios/App/App/SharedImagePlugin.swift`
|
|
|
|
**Decision Needed:** ✅ Confirm Option A, or if auto-discovery doesn't work, use Option B
|
|
|
|
---
|
|
|
|
### 5. TypeScript Interface Design
|
|
|
|
#### Decision: What should the TypeScript interface look like?
|
|
|
|
**Proposed Interface:**
|
|
```typescript
|
|
export interface SharedImageResult {
|
|
base64: string;
|
|
fileName: string;
|
|
}
|
|
|
|
export interface SharedImagePlugin {
|
|
getSharedImage(): Promise<SharedImageResult | null>;
|
|
hasSharedImage(): Promise<{ hasImage: boolean }>;
|
|
}
|
|
```
|
|
|
|
**Questions:**
|
|
- Should `fileName` be optional? (Currently always provided, but could be empty string)
|
|
- Should we include metadata (image size, MIME type)?
|
|
- Should `hasSharedImage()` return more info (like fileName without reading)?
|
|
|
|
**Recommendation:** Keep simple for now:
|
|
- `fileName` is always a string (may be default "shared-image.jpg")
|
|
- No metadata initially (can add later if needed)
|
|
- `hasSharedImage()` only returns boolean (keep it lightweight)
|
|
|
|
**Decision Needed:** ✅ Confirm interface design or request changes
|
|
|
|
---
|
|
|
|
### 6. Android Data Storage Timing
|
|
|
|
#### Decision: When should Android store shared image data in SharedPreferences?
|
|
|
|
**Current Flow:**
|
|
1. Share intent received in MainActivity
|
|
2. Image processed and written to temp file
|
|
3. JS reads temp file
|
|
|
|
**New Flow Options:**
|
|
- **Option A**: Store in SharedPreferences immediately when share intent received (in `processSharedImage()`)
|
|
- **Option B**: Store when plugin is first called (lazy loading)
|
|
- **Option C**: Store in both places during transition (backward compatibility)
|
|
|
|
**Recommendation:** **Option A** (immediate storage)
|
|
- Data available immediately when plugin is called
|
|
- No timing issues
|
|
- Matches iOS pattern (data stored by Share Extension)
|
|
|
|
**Implementation:**
|
|
- Update `processSharedImage()` in MainActivity to store in SharedPreferences
|
|
- Remove temp file writing
|
|
- Plugin reads from SharedPreferences
|
|
|
|
**Decision Needed:** ✅ Confirm Option A
|
|
|
|
---
|
|
|
|
### 7. Migration Strategy
|
|
|
|
#### Decision: How to handle the transition from temp file to plugin?
|
|
|
|
**Options:**
|
|
- **Option A (Clean Break)**: Remove temp file code immediately, use plugin only
|
|
- **Option B (Gradual)**: Support both approaches temporarily, remove temp file later
|
|
- **Option C (Feature Flag)**: Use feature flag to switch between approaches
|
|
|
|
**Recommendation:** **Option A** (clean break)
|
|
- Simpler implementation
|
|
- Less code to maintain
|
|
- Temp file approach is buggy anyway (why we're replacing it)
|
|
- Can rollback via git if needed
|
|
|
|
**Rollback Plan:**
|
|
- Keep temp file code in git history
|
|
- If plugin has issues, can revert commit
|
|
- Test thoroughly before removing temp file code
|
|
|
|
**Decision Needed:** ✅ Confirm Option A
|
|
|
|
---
|
|
|
|
### 8. Plugin Naming
|
|
|
|
#### Decision: What should the plugin be named?
|
|
|
|
**Options:**
|
|
- **Option A**: `SharedImage` (matches file/class names)
|
|
- **Option B**: `SharedImagePlugin` (more explicit)
|
|
- **Option C**: `NativeShare` (more generic, could handle other share types)
|
|
|
|
**Recommendation:** **Option A** (`SharedImage`)
|
|
- Matches Capacitor naming conventions (plugins are referenced without "Plugin" suffix)
|
|
- Examples: `Capacitor.Plugins.Camera`, `Capacitor.Plugins.Filesystem`
|
|
- TypeScript: `SharedImage.getSharedImage()`
|
|
|
|
**Decision Needed:** ✅ Confirm Option A
|
|
|
|
---
|
|
|
|
### 9. iOS: Reuse getSharedImageData() or Move to Plugin?
|
|
|
|
#### Decision: Should the plugin reuse AppDelegate's `getSharedImageData()` or implement its own?
|
|
|
|
**Current Code:**
|
|
- `AppDelegate.getSharedImageData()` exists and works
|
|
- Reads from App Group UserDefaults
|
|
- Clears data after reading
|
|
|
|
**Options:**
|
|
- **Option A**: Plugin calls `getSharedImageData()` from AppDelegate
|
|
- **Option B**: Plugin implements its own logic (duplicate code)
|
|
- **Option C**: Move `getSharedImageData()` to a shared utility, both use it
|
|
|
|
**Recommendation:** **Option C** (shared utility)
|
|
- DRY principle
|
|
- Single source of truth
|
|
- But: May be overkill for simple logic
|
|
|
|
**Alternative Recommendation:** **Option B** (plugin implements own logic)
|
|
- Plugin is self-contained
|
|
- No dependency on AppDelegate
|
|
- Logic is simple (just UserDefaults read/clear)
|
|
- Can remove `getSharedImageData()` from AppDelegate after migration
|
|
|
|
**Decision:** ✅ **Option C** (shared utility) - **CONFIRMED**
|
|
- Create shared utility for reading from App Group UserDefaults
|
|
- Both AppDelegate and plugin use the shared utility
|
|
- Single source of truth for shared image data access
|
|
|
|
---
|
|
|
|
### 10. Android: SharedPreferences Key Names
|
|
|
|
#### Decision: What keys should be used in SharedPreferences?
|
|
|
|
**Proposed Keys:**
|
|
- `shared_image_base64` - Base64 string
|
|
- `shared_image_file_name` - File name
|
|
- `shared_image_ready` - Boolean flag (optional, for quick checks)
|
|
|
|
**Alternative:**
|
|
- Use a single JSON object: `shared_image_data` = `{ base64: "...", fileName: "..." }`
|
|
|
|
**Recommendation:** Separate keys (first option)
|
|
- Simpler to read/write
|
|
- No JSON parsing needed
|
|
- Matches iOS pattern (separate UserDefaults keys)
|
|
- Flag is optional but useful for `hasSharedImage()`
|
|
|
|
**Decision Needed:** ✅ Confirm key naming or request changes
|
|
|
|
---
|
|
|
|
### 11. Testing Strategy
|
|
|
|
#### Decision: What testing approach should we use?
|
|
|
|
**Options:**
|
|
- **Option A**: Manual testing only
|
|
- **Option B**: Manual + automated unit tests for plugin methods
|
|
- **Option C**: Manual + integration tests
|
|
|
|
**Recommendation:** **Option A** (manual testing) for now
|
|
- Plugins are hard to unit test (require native environment)
|
|
- Manual testing is sufficient for initial implementation
|
|
- Can add automated tests later if needed
|
|
|
|
**Test Scenarios:**
|
|
1. Share image from Photos app → Verify appears in app
|
|
2. Share while app backgrounded → Verify appears when app becomes active
|
|
3. Share while app closed → Verify appears on app launch
|
|
4. Multiple rapid shares → Verify only latest is processed
|
|
5. Share then close app before processing → Verify data persists
|
|
6. Share then clear app data → Verify graceful handling
|
|
|
|
**Decision Needed:** ✅ Confirm testing approach
|
|
|
|
---
|
|
|
|
### 12. Documentation Updates
|
|
|
|
#### Decision: What documentation needs updating?
|
|
|
|
**Files to Update:**
|
|
- ✅ Implementation plan (this document)
|
|
- ⚠️ `doc/native-share-target-implementation.md` - Update to reflect plugin approach
|
|
- ⚠️ `doc/ios-share-implementation-status.md` - Mark plugin as implemented
|
|
- ⚠️ Code comments in `main.capacitor.ts` - Update to reflect plugin usage
|
|
|
|
**Decision Needed:** ✅ Confirm documentation update list
|
|
|
|
---
|
|
|
|
## Summary of Decisions Needed
|
|
|
|
| # | Decision | Recommendation | Status |
|
|
|---|----------|----------------|--------|
|
|
| 1 | Plugin Methods | Option B: `getSharedImage()` + `hasSharedImage()` | ✅ Confirmed |
|
|
| 2 | Error Handling | Option B: `null` for no data, `reject()` for errors | ✅ Confirmed |
|
|
| 3 | Data Clearing | Option A: Clear immediately after reading | ✅ Confirmed |
|
|
| 4 | iOS Registration | Option A: Auto-discovery | ✅ Confirmed |
|
|
| 5 | TypeScript Interface | Proposed interface (see above) | ✅ Confirmed |
|
|
| 6 | Android Storage Timing | Option A: Store immediately on share intent | ✅ Confirmed |
|
|
| 7 | Migration Strategy | Option A: Clean break, remove temp file code | ✅ Confirmed |
|
|
| 8 | Plugin Naming | Option A: `SharedImage` | ✅ Confirmed |
|
|
| 9 | iOS Code Reuse | Option C: Shared utility | ✅ Confirmed |
|
|
| 10 | Android Key Names | Separate keys: `shared_image_base64`, `shared_image_file_name` | ✅ Confirmed |
|
|
| 11 | Testing Strategy | Option A: Manual testing | ✅ Confirmed |
|
|
| 12 | Documentation | Update listed files | ✅ Confirmed |
|
|
| - | Multiple Images | Single image only (SharedPhotoView requirement) | ✅ Confirmed |
|
|
| - | Backward Compatibility | No temp file backward compatibility | ✅ Confirmed |
|
|
|
|
## Next Steps
|
|
|
|
1. **Review this checklist** and confirm or modify recommendations
|
|
2. **Make decisions** on all pending items
|
|
3. **Update implementation plan** with confirmed decisions
|
|
4. **Begin implementation** with clear specifications
|
|
|
|
## Questions to Consider
|
|
|
|
- Are there any edge cases not covered?
|
|
- Should we support multiple images (currently only first image)?
|
|
- Should we add image metadata (size, MIME type) in the future?
|
|
- Do we need backward compatibility with temp file approach?
|
|
- Should plugin methods be synchronous or async? (Capacitor plugins are async by default)
|
|
|