Derrick Hunt Derrick Hunt - 3 years ago 186
iOS Question

Share image with hashtag via UIActivityViewController (Twitter, Facebook, Instagram)

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)


Scenario 2: Init controller with only image

In this scenario, all networks show up, but I (obviously) lose the automatic hashtag feature:

let activityVC = UIActivityViewController(activityItems: [myImage], applicationActivities: nil)


Scenario 3: UIActivityItemSource subclass

If I make my own UIActivityItemSource subclass, I can almost get everything to work. However, and this is what I cannot figure out, using the protocol methods as I have below results in the automatic hashtag working for Facebook, but not Twitter. How can this be possible -- is there a special key needed for Twitter? There must be a way for it to work if it works in Scenario #1...

Interestingly, this method works for both Twitter and Facebook if I insert a URL (commented out). So why on earth won't the text work for Twitter!?

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]
}
}

}

Answer Source

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: ...)
    }
}

Explaination

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

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download