Consider this simple miniature of my iOS Share Extension:
import SwiftUI
import Photos
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let itemProviders = (extensionContext?.inputItems.first as? NSExtensionItem)?.attachments {
let hostingView = UIHostingController(rootView: ShareView(extensionContext: extensionContext, itemProviders: itemProviders))
hostingView.view.frame = view.frame
view.addSubview(hostingView.view)
}
}
}
struct ShareView: View {
var extensionContext: NSExtensionContext?
var itemProviders: [NSItemProvider]
var body: some View {
VStack{}
.task{
await extractItems()
}
}
func extractItems() async {
guard let itemProvider = itemProviders.first else { return }
guard itemProvider.hasItemConformingToTypeIdentifier(UTType.url.identifier) else { return }
do {
guard let url = try await itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier) as? URL else { return }
try await downloadAndSaveMedia(reelURL: url.absoluteString)
extensionContext?.completeRequest(returningItems: [])
}
catch {}
}
}
On the line 34
guard let url = try await itemProvider.loadItem
... I get these warnings:
- Passing argument of non-sendable type '[AnyHashable : Any]?' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
1.1. Generic enum 'Optional' does not conform to the 'Sendable' protocol (Swift.Optional)
- Passing argument of non-sendable type 'NSItemProvider' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
2.2. Class 'NSItemProvider' does not conform to the 'Sendable' protocol (Foundation.NSItemProvider)
How to fix them in Xcode 16?
Please provide a solution which works, and not the one which might (meaning you run the same code in Xcode, add your solution and see no warnings).
I tried
- Decorating everything with
@MainActor
s - Using
@MainActor in
the.task
@preconcurrency import
- Decorating everything with
@preconcurrency
- Playing around with
nonisolated
I can't provide you with a full, completely working solution, but this doesn't cause any errors:
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
super.loadView()
if let inputItem = extensionContext!.inputItems.first as? NSExtensionItem {
if let itemProvider = inputItem.attachments?.first {
itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier as String) { [unowned self] (item, error) in
let contentView = ShareView(extensionContext: extensionContext, url: item as! URL)
DispatchQueue.main.async {
let hostingView = UIHostingController(rootView: contentView)
hostingView.view.frame = self.view.frame
self.view.addSubview(hostingView.view)
}
}
}
}
}
}
struct ShareView: View {
var extensionContext: NSExtensionContext?
var url: URL
var body: some View {
VStack{}
.task{
await extractItems()
}
}
func extractItems() async {
try await downloadAndSaveMedia(reelURL: url.absoluteString)
extensionContext?.completeRequest(returningItems: [])
}
}
In your code you get all the attachments here: if let itemProviders = (extensionContext?.inputItems.first as? NSExtensionItem)?.attachments {
and you send that array of NSItemProvider
to ShareView()
, but you then get just the first attachment here (first line of extractItems()
): guard let itemProvider = itemProviders.first else { return }
, so in my code I'm just using the first attachment.
The distinction is that I do all that stuff in the loadView()
method rather than in ShareView()
.
Might work for you, might not, but I don't think you've tried this. You can probably tidy it up a little.