feat(ios): improve share extension UX and fix image reload issues
Improve iOS share extension implementation to skip interstitial UI and fix issues with subsequent image shares not updating the view. iOS Share Extension Improvements: - Replace SLComposeServiceViewController with custom UIViewController to eliminate interstitial "Post" button UI - Use minimal URL (timesafari://) instead of deep link for app launch - Implement app lifecycle detection via Capacitor appStateChange listener instead of relying solely on deep links Deep Link and Navigation Fixes: - Remove "shared-photo" from deep link schemas (no longer needed) - Add empty path URL handling for share extension launches - Implement processing lock to prevent duplicate image processing - Add retry mechanism (300ms delay) to handle race conditions with AppDelegate writing temp files - Use router.replace() when already on /shared-photo route to force refresh - Clear old images from temp DB before storing new ones - Delete temp file immediately after reading to prevent stale data SharedPhotoView Component: - Add route watcher (@Watch) to reload image when fileName query parameter changes - Extract image loading logic into reusable loadSharedImage() method - Improve error handling to clear image state on failures This fixes the issue where sharing a second image while already on SharedPhotoView would display the previous image instead of the new one.
This commit is contained in:
@@ -6,10 +6,9 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Social
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class ShareViewController: SLComposeServiceViewController {
|
||||
class ShareViewController: UIViewController {
|
||||
|
||||
private let appGroupIdentifier = "group.app.timesafari"
|
||||
private let sharedPhotoBase64Key = "sharedPhotoBase64"
|
||||
@@ -18,64 +17,46 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Set placeholder text (required for SLComposeServiceViewController)
|
||||
self.placeholder = "Share image to TimeSafari"
|
||||
// Set a minimal background (transparent or loading indicator)
|
||||
view.backgroundColor = .systemBackground
|
||||
|
||||
// Validate content on load
|
||||
self.validateContent()
|
||||
// Process image immediately without showing UI
|
||||
processAndOpenApp()
|
||||
}
|
||||
|
||||
override func isContentValid() -> Bool {
|
||||
// Validate that we have image attachments
|
||||
guard let extensionContext = extensionContext else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let inputItems = extensionContext.inputItems as? [NSExtensionItem] else {
|
||||
return false
|
||||
}
|
||||
|
||||
for item in inputItems {
|
||||
if let attachments = item.attachments {
|
||||
for attachment in attachments {
|
||||
if attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override func didSelectPost() {
|
||||
// Extract and process the shared image
|
||||
guard let extensionContext = extensionContext else {
|
||||
private func processAndOpenApp() {
|
||||
// extensionContext is automatically available on UIViewController when used as extension principal class
|
||||
guard let context = extensionContext,
|
||||
let inputItems = context.inputItems as? [NSExtensionItem] else {
|
||||
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
return
|
||||
}
|
||||
|
||||
guard let inputItems = extensionContext.inputItems as? [NSExtensionItem] else {
|
||||
extensionContext.completeRequest(returningItems: [], completionHandler: nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Process the first image found
|
||||
processSharedImage(from: inputItems) { [weak self] success in
|
||||
guard let self = self else {
|
||||
extensionContext.completeRequest(returningItems: [], completionHandler: nil)
|
||||
guard let self = self, let context = self.extensionContext else {
|
||||
return
|
||||
}
|
||||
|
||||
if success {
|
||||
// Open the main app via deep link
|
||||
// Set flag that shared photo is ready
|
||||
self.setSharedPhotoReadyFlag()
|
||||
// Open the main app (using minimal URL - app will detect shared data on activation)
|
||||
self.openMainApp()
|
||||
}
|
||||
|
||||
// Complete the extension context
|
||||
extensionContext.completeRequest(returningItems: [], completionHandler: nil)
|
||||
// Complete immediately - no UI shown
|
||||
context.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func setSharedPhotoReadyFlag() {
|
||||
guard let userDefaults = UserDefaults(suiteName: appGroupIdentifier) else {
|
||||
return
|
||||
}
|
||||
userDefaults.set(true, forKey: "sharedPhotoReady")
|
||||
userDefaults.synchronize()
|
||||
}
|
||||
|
||||
private func processSharedImage(from items: [NSExtensionItem], completion: @escaping (Bool) -> Void) {
|
||||
// Find the first image attachment
|
||||
for item in items {
|
||||
@@ -144,8 +125,8 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
}
|
||||
|
||||
private func openMainApp() {
|
||||
// Open the main app via deep link
|
||||
guard let url = URL(string: "timesafari://shared-photo") else {
|
||||
// Open the main app with minimal URL - app will detect shared data on activation
|
||||
guard let url = URL(string: "timesafari://") else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -162,9 +143,4 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
extensionContext?.open(url, completionHandler: nil)
|
||||
}
|
||||
|
||||
override func configurationItems() -> [Any]! {
|
||||
// No additional configuration options needed
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user