feat(ios): add markProcessed share-completion API (Phase 1D)

Add a native iOS markProcessed(shareId) method to the SharedImage plugin
that records an explicit completion signal in App Group UserDefaults
(sharedPhotoProcessedShareId) and logs "[ShareTarget] shareId=<id> marked
processed". The API is exposed to TypeScript via the plugin definitions and
a web no-op stub.

Deliberately does not delete files or clear share metadata (cleanup is
deferred to a later phase), is not yet called from application code, and
does not touch Android. Documents the share-target lifecycle and plugin API
in doc/share-target-ios-audit.md.
This commit is contained in:
Jose Olarte III
2026-06-30 11:00:52 +08:00
parent 125406a54c
commit 981f8b77d0
5 changed files with 152 additions and 1 deletions

View File

@@ -24,7 +24,8 @@ public class SharedImagePlugin: CAPPlugin, CAPBridgedPlugin {
public var pluginMethods: [CAPPluginMethod] {
return [
CAPPluginMethod(#selector(getSharedImage(_:)), returnType: .promise),
CAPPluginMethod(#selector(hasSharedImage(_:)), returnType: .promise)
CAPPluginMethod(#selector(hasSharedImage(_:)), returnType: .promise),
CAPPluginMethod(#selector(markProcessed(_:)), returnType: .promise)
]
}
@@ -62,5 +63,22 @@ public class SharedImagePlugin: CAPPlugin, CAPBridgedPlugin {
"hasImage": hasImage
])
}
/**
* Mark a share as processed by the application layer (Phase 1D)
* Records an explicit completion signal for the given shareId.
* Does NOT delete the stored image file - file cleanup is deferred to a later phase.
*/
@objc public func markProcessed(_ call: CAPPluginCall) {
guard let shareId = call.getString("shareId"), !shareId.isEmpty else {
call.reject("markProcessed requires a non-empty 'shareId'")
return
}
let success = SharedImageUtility.markProcessed(shareId: shareId)
call.resolve([
"success": success
])
}
}

View File

@@ -15,6 +15,7 @@ public class SharedImageUtility {
private static let sharedPhotoFilePathKey = "sharedPhotoFilePath"
private static let sharedPhotoShareIdKey = "sharedPhotoShareId"
private static let sharedPhotoReadyKey = "sharedPhotoReady"
private static let sharedPhotoProcessedShareIdKey = "sharedPhotoProcessedShareId"
/// Get the App Group container URL for accessing shared files
private static var appGroupContainerURL: URL? {
@@ -78,6 +79,32 @@ public class SharedImageUtility {
return ["base64": base64String, "fileName": fileName]
}
/**
* Mark a share as processed by the web/application layer (Phase 1D).
*
* Records the processed shareId in the App Group UserDefaults so that
* later phases can decide whether retrieval/cleanup should be skipped.
* This is an explicit completion signal: it does NOT delete the stored
* image file or clear the share metadata. File cleanup is deferred to a
* later phase.
*
* @param shareId The share identifier reported by getSharedImage()
* @returns true if the processed marker was recorded, false otherwise
*/
@discardableResult
static func markProcessed(shareId: String) -> Bool {
guard let userDefaults = UserDefaults(suiteName: appGroupIdentifier) else {
print("[ShareTarget] shareId=\(shareId) markProcessed failed: UserDefaults unavailable for app group")
return false
}
userDefaults.set(shareId, forKey: sharedPhotoProcessedShareIdKey)
userDefaults.synchronize()
print("[ShareTarget] shareId=\(shareId) marked processed")
return true
}
/**
* Check if shared image exists without reading it
*