Files
edgeever/apps/ios/ShareExtension/ShareViewController.swift
T

119 lines
5.2 KiB
Swift

import UIKit
import UniformTypeIdentifiers
/// Lightweight share extension: text, web URLs, and images → App Group → open main app.
class ShareViewController: UIViewController {
private let appGroupId = "group.org.edgeever.mobile"
private let hostScheme = "edgeever"
private let payloadKey = "edgeever.share.payload"
private let imageDirectoryName = "incoming-share-images"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
Task { await handleShare() }
}
private func handleShare() async {
guard let items = extensionContext?.inputItems as? [NSExtensionItem] else {
extensionContext?.completeRequest(returningItems: nil)
return
}
var payload: [[String: Any]] = []
for item in items {
let title = item.attributedContentText?.string
for provider in item.attachments ?? [] {
if provider.hasItemConformingToTypeIdentifier(UTType.image.identifier),
let image = try? await persistImage(from: provider)
{
payload.append([
"imageFilename": image.storageFilename,
"imageMimeType": image.mimeType,
"imageOriginalName": image.originalName,
"title": title as Any,
])
} else if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier),
let url = try? await provider.loadItem(forTypeIdentifier: UTType.url.identifier) as? URL
{
payload.append(["url": url.absoluteString, "title": title as Any])
} else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier),
let str = try? await provider.loadItem(forTypeIdentifier: UTType.plainText.identifier) as? String
{
payload.append(["text": str, "title": title as Any])
}
}
}
if let data = try? JSONSerialization.data(withJSONObject: payload),
let defaults = UserDefaults(suiteName: appGroupId)
{
defaults.set(data, forKey: payloadKey)
}
if let url = URL(string: "\(hostScheme)://share") {
extensionContext?.open(url) { [weak self] _ in
self?.extensionContext?.completeRequest(returningItems: nil)
}
return
}
extensionContext?.completeRequest(returningItems: nil)
}
private func persistImage(from provider: NSItemProvider) async throws -> (
storageFilename: String,
originalName: String,
mimeType: String
) {
guard let container = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: appGroupId
) else {
throw CocoaError(.fileNoSuchFile)
}
let directory = container.appendingPathComponent(imageDirectoryName, isDirectory: true)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
let typeIdentifier = provider.registeredTypeIdentifiers.first(where: {
UTType($0)?.conforms(to: .image) == true
}) ?? UTType.image.identifier
let type = UTType(typeIdentifier)
let preferredExtension = type?.preferredFilenameExtension ?? "jpg"
let mimeType = type?.preferredMIMEType ?? "image/jpeg"
let suggested = ((provider.suggestedName ?? "shared-image") as NSString).lastPathComponent
let originalName = (suggested as NSString).pathExtension.isEmpty
? "\(suggested).\(preferredExtension)"
: suggested
let storageFilename = "\(UUID().uuidString).\(preferredExtension)"
let destination = directory.appendingPathComponent(storageFilename, isDirectory: false)
do {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { source, error in
guard let source else {
continuation.resume(throwing: error ?? CocoaError(.fileReadUnknown))
return
}
do {
try FileManager.default.copyItem(at: source, to: destination)
continuation.resume(returning: ())
} catch {
continuation.resume(throwing: error)
}
}
}
} catch {
let data = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Data, Error>) in
provider.loadDataRepresentation(forTypeIdentifier: typeIdentifier) { data, error in
if let data, !data.isEmpty {
continuation.resume(returning: data)
} else {
continuation.resume(throwing: error ?? CocoaError(.fileReadUnknown))
}
}
}
try data.write(to: destination, options: .atomic)
}
return (storageFilename, originalName, mimeType)
}
}