I'm struggling with a problem, which was discussed in earlier threads too (e.g. UIActivityViewController - is there a way to know which activity was selected?), but in my understanding not fully solved yet.
I want to share different data types based on the different options that can be selected in the share dialogue of the UIActivityController: a) if "mail", "print" or "message" is selected, I want to share a NSAttributedString b) if "airdrop", "save to Files" is selected, I want to share data file (identified by an URL to a temporarily created file).
I tried to handle this via:
public func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return [NSAttributedString().self, URL.self] as [Any]
}
public func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if let activityType = activityType {
switch activityType {
case .airDrop, .copyToPasteboard: return [URL.self] as [Any]
case .mail, .message, .print: return [NSAttributedString().self] as [Any]
default: return [NSAttributedString().self, Array<EKLPosition>.self, URL.self] as [Any]
}
} else {
return [NSAttributedString().self, URL.self] as [Any]
}
}
Unfortunately, this doesn't work. When I select e.g. "mail", the generated e-mail contains the NSAttributedString (as intended) but also the data file as an attachment. If "Save to Files" is selected, a file with the text of the NSAttributedString and the data file is stored. If "airdrop" is selected, only the data file is shared as intended.
Hence I have two questions?
Thanks for your support.
Not sure why you are returning those arrays of types in itemForActivityType
. You should be returning the items for the given activity type.
For example:
class MyActivity: NSObject, UIActivityItemSource {
let attributedString: NSAttributedString = ...
let url = ...
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
// use one of the two things as a placeholder
url
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
// return one of the two things - URL or attributed string
if activityType == .copyToPasteboard {
return attributedString
} else {
return url
}
}
}
More generally:
class ActivityItemPair: NSObject, UIActivityItemSource {
let item1: Any
let item2: Any
let item1Types: [UIActivity.ActivityType]
let item2Types: [UIActivity.ActivityType]
let defaultItem: Any?
init(item1: Any, item2: Any, item1Types: [UIActivity.ActivityType], item2Types: [UIActivity.ActivityType], defaultItem: Any? = nil) {
self.item1 = item1
self.item2 = item2
self.item1Types = item1Types
self.item2Types = item2Types
self.defaultItem = defaultItem
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
defaultItem ?? item1
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if let activityType, item1Types.map(\.rawValue).contains(activityType.rawValue) {
return item1
} else if let activityType, item2Types.map(\.rawValue).contains(activityType.rawValue) {
return item2
} else {
return defaultItem ?? item2
}
}
}
As for how you can identify the activity type, you can print the rawValue
of the activity type in itemForActivityType
:
print(activityType?.rawValue)
Then, you can select "Save to Files" and see what gets printed. For "Save to Files", it is:
com.apple.DocumentManagerUICore.SaveToFiles
You can use this raw value to initialise an AcitivityType
too:
UIActivity.ActivityType("com.apple.DocumentManagerUICore.SaveToFiles")
Then you can pass this to the item1Types
and item2Types
parameters in ActivityItemPair
.