I am attempting to share an image with a hashtag using UIActivityViewController and I am encountering some strange behavior when attempting to share to Twitter, Facebook and Instagram. There does not seem to be a lot of documentation about these service's share extensions.
Scenario 1: Init controller with activity item array with image and text
If I initialize the controller like so, Twitter and Facebook will show up in the controller (no Instagram as it does not support text items), and both will programmatically pre-populate the hashtag in the text entry field:
let activityVC = UIActivityViewController(activityItems: [myHashtagString, myImage], applicationActivities: nil)
let activityVC = UIActivityViewController(activityItems: [myImage], applicationActivities: nil)
let activityItem = CustomItemSource(image: image, message: "#TestTag")
let activityVC = UIActivityViewController(activityItems: [activityItem], applicationActivities: nil)
...
class CustomItemSource: NSObject, UIActivityItemSource {
private var image: UIImage!
private var message: String!
// MARK: Init
init(image: UIImage, message: String) {
super.init()
self.image = image
self.message = message
}
// MARK: Item Source Protocol
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return image
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
if activityType == .postToTwitter || activityType == .postToFacebook {
//return ["url": URL(string: "https://www.google.com")!, "image": image]
return ["text": message, "image": image]
}
else {
return ["image": image]
}
}
}
Define two UIActivityItemSource
classes, one for Image and one for Text.
In first one only return the image.
In second one return NSObject()
for placeHolder, and return Text
or nil
depending on activity. By returning NSObject()
, UIActivity will allow all services to be available.
UIActivityViewController(activityItems: [ImageProvider(), TextProvider()], applicationActivities: nil)
and providers:
class TextProvider: NSObject, UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return NSObject()
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
if activityType == .postToTwitter || activityType == .postToFacebook {
return "Tweet with #Hashtag"
}
return nil
}
}
class ImageProvider: NSObject, UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return UIImage(named: ...)
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
return UIImage(named: ...)
}
}
First of all, Keys are not really sensitive, The only sensitive-key was "subject" for email and apps supporting it, which is implemented in UIActivityController
's API and we can set it directly. It doesn't matter if you provide UIImage
with key "image" or "1".
As it turns out, Twitter activity will not work if it's text is not returned directly in ...itemForActivity...
method. So the solution is to separate item sources.
Twitter activity also will not work, if placeholder
receives anything other than String
, but by returning String
Instagram activity will not work, So by returning NSObject()
Type will be ignored and all services will be available.
if you want to limit some services use UIActivityViewController.excludedActivityTypes