Add Share Extension for importing recipes via URL
Adds a Share Extension so users can share URLs from Safari (or any app) to open the main app with the ImportURLSheet pre-filled. Uses a custom URL scheme (nextcloud-cookbook://) as the bridge between the extension and the main app. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
21
ShareExtension/Info.plist
Normal file
21
ShareExtension/Info.plist
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
79
ShareExtension/ShareViewController.swift
Normal file
79
ShareExtension/ShareViewController.swift
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// ShareViewController.swift
|
||||
// ShareExtension
|
||||
//
|
||||
// Created by Hendrik Hogertz on 15.02.26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class ShareViewController: UIViewController {
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
handleSharedItems()
|
||||
}
|
||||
|
||||
private func handleSharedItems() {
|
||||
guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else {
|
||||
completeRequest()
|
||||
return
|
||||
}
|
||||
|
||||
for item in extensionItems {
|
||||
guard let attachments = item.attachments else { continue }
|
||||
for provider in attachments {
|
||||
if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
|
||||
provider.loadItem(forTypeIdentifier: UTType.url.identifier) { [weak self] item, _ in
|
||||
if let url = item as? URL {
|
||||
self?.openMainApp(with: url.absoluteString)
|
||||
} else {
|
||||
self?.completeRequest()
|
||||
}
|
||||
}
|
||||
return
|
||||
} else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
|
||||
provider.loadItem(forTypeIdentifier: UTType.plainText.identifier) { [weak self] item, _ in
|
||||
if let text = item as? String, let url = URL(string: text), url.scheme?.hasPrefix("http") == true {
|
||||
self?.openMainApp(with: url.absoluteString)
|
||||
} else {
|
||||
self?.completeRequest()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completeRequest()
|
||||
}
|
||||
|
||||
private func openMainApp(with urlString: String) {
|
||||
guard let encoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
|
||||
let appURL = URL(string: "nextcloud-cookbook://import?url=\(encoded)")
|
||||
else {
|
||||
completeRequest()
|
||||
return
|
||||
}
|
||||
|
||||
// Use the responder chain to open the URL
|
||||
var responder: UIResponder? = self
|
||||
while let r = responder {
|
||||
if let application = r as? UIApplication {
|
||||
application.open(appURL, options: [:], completionHandler: nil)
|
||||
break
|
||||
}
|
||||
responder = r.next
|
||||
}
|
||||
|
||||
// Give the system a moment to process the URL before dismissing
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
|
||||
self?.completeRequest()
|
||||
}
|
||||
}
|
||||
|
||||
private func completeRequest() {
|
||||
extensionContext?.completeRequest(returningItems: nil)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user