84 lines
2.8 KiB
Swift
84 lines
2.8 KiB
Swift
import UIKit
|
|
import SwiftUI
|
|
import UniformTypeIdentifiers
|
|
|
|
@objc(ShareViewController)
|
|
class ShareViewController: UIViewController {
|
|
private let supportedTypeIdentifiers = [
|
|
UTType.url.identifier,
|
|
UTType.html.identifier,
|
|
UTType.rtf.identifier,
|
|
UTType.plainText.identifier,
|
|
UTType.text.identifier
|
|
]
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
|
|
blur.frame = view.bounds
|
|
blur.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
view.addSubview(blur)
|
|
extractPayload()
|
|
}
|
|
|
|
private func extractPayload() {
|
|
guard let item = extensionContext?.inputItems.first as? NSExtensionItem,
|
|
let attachments = item.attachments else {
|
|
present(payload: nil)
|
|
return
|
|
}
|
|
|
|
let candidates = attachments.flatMap { attachment in
|
|
supportedTypeIdentifiers
|
|
.filter { attachment.hasItemConformingToTypeIdentifier($0) }
|
|
.map { (attachment, $0) }
|
|
}
|
|
|
|
loadNext(candidates, index: 0)
|
|
}
|
|
|
|
private func loadNext(_ candidates: [(NSItemProvider, String)], index: Int) {
|
|
guard index < candidates.count else {
|
|
present(payload: nil)
|
|
return
|
|
}
|
|
|
|
let (attachment, typeIdentifier) = candidates[index]
|
|
attachment.loadItem(forTypeIdentifier: typeIdentifier, options: nil) { [weak self] item, _ in
|
|
guard let self else { return }
|
|
if let payload = IngestPayloadParser.parse(item: item, typeIdentifier: typeIdentifier) {
|
|
DispatchQueue.main.async { self.present(payload: payload) }
|
|
} else {
|
|
DispatchQueue.main.async { self.loadNext(candidates, index: index + 1) }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Hosts the SwiftUI save card. A nil URL surfaces an error state in the card.
|
|
private func present(payload: IngestPayload?) {
|
|
let root = ShareView(
|
|
url: payload?.url ?? "",
|
|
initialTitle: payload?.title,
|
|
initialNotes: payload?.notes ?? ""
|
|
) { [weak self] _ in
|
|
self?.close()
|
|
}
|
|
let host = UIHostingController(rootView: root)
|
|
host.view.backgroundColor = .clear
|
|
addChild(host)
|
|
host.view.frame = view.bounds
|
|
host.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
view.addSubview(host.view)
|
|
host.didMove(toParent: self)
|
|
|
|
if payload == nil {
|
|
// No URL to work with — let the card render its failure state briefly.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.6) { [weak self] in self?.close() }
|
|
}
|
|
}
|
|
|
|
private func close() {
|
|
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
|
}
|
|
}
|