Hide String in UIActivityViewController from User - ios

I would like to pass a string to a UIActivityViewController but hide it from the user when they publish a tweet or share on Facebook.
In the free version of my app, the added hidden string will be 'Download my app now!', whereas in the paid version (via in-app purchase), this string will not be published.
In the free version, the user must not be able to delete the added string. But I can't find a way to hide it from them. So when they share, that pop-up appears and the string is included, editable. This is my code so far:
NSString *advertString = #"Download my app now!";
UIActivityViewController *shareController =
[[UIActivityViewController alloc]
initWithActivityItems:#[scoreString, advertString, url, image]
applicationActivities:nil];
How would I go about doing this? Or are there alternatives that would allow me to do this? Thanks for any help!

You can provide a subject for your email by implementing
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType;
in a class that conforms to theUIActivityItemSource protocol. See the documentation for more information.
Edited
You need to customize this by using different string on different sharing option.
Read this Tutorial, this will work.
Thanks.

Related

How to share files for open-in-place on iOS

We have an iOS application that manages documents via Core Data. The actual files reside in the app's shared container so that the app's file provider extension can also access them for Files.app support. We want to give the user the option to open these files in third-party apps so that they can edit them in-place instead of sending a copy to the other app.
We provide a UIActivityViewController for sharing files with other apps. We also provide a UIActivity that shows a UIDocumentInteractionController which seems to work better in some cases. We give the UIActivityViewController the document's file URL, the raw text content, and printable data.
This works but all third-party editors are shown as Copy to … instead of Open in …
We've also set the UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace properties to YES in the app's info.plist but they seem to be only relevant for open-in-place when sharing files residing in the app's Documents folder.
Now we've stumbled upon the NSItemProviderFileOptionOpenInPlace option for NSItemProvider. As we're already supporting a file provider extension and from Apple's documentation this seemed like a great place to accomplish just what we want.
Adding a "pure" NSItemProvider works, in a way, but shows fewer options than when also sharing the file URL and text in addition (which is expected). However, when we use -[NSItemProvider registerFileRepresentationForTypeIdentifier:fileOptions:visibility:loadHandler:] with the said option (or just zero, same result) and return the file URL in the loadHandler's completionHandler() nothing is shared anymore. E.g., Mail no longer attaches the file, Messages doesn't show the document for sending.
These are the relevant bits of the code:
NSMutableArray *items = [NSMutableArray array];
NSMutableArray <UIActivity *> *activities = [NSMutableArray array];
NSURL *fileURL = self.record.metadata.fileURL;
NSString *fileUTI = self.record.metadata.uti;
NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem: fileURL typeIdentifier: fileUTI];
[itemProvider registerFileRepresentationForTypeIdentifier:fileUTI fileOptions:NSItemProviderFileOptionOpenInPlace visibility:YES loadHandler:^NSProgress * _Nullable(void (^ _Nonnull completionHandler)(NSURL * _Nullable, BOOL, NSError * _Nullable))
{
if (fileURL)
completionHandler(fileURL, YES, nil);
else
completionHandler(nil, YES, [NSError errorWithDomain:NSCocoaErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]);
return nil;
}];
[items addObject:itemProvider];
self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:activities];
[UIAppDelegate.splitViewController presentViewController:self.activityViewController animated:YES completion:nil];
The using the Share menu the item provider's load handler is correctly called and the file's actual URL returned.
Is that not how NSItemProviderFileOptionOpenInPlace is intended to be used? Or are we using it simply wrong? Apple's description is extremely sparse and we couldn't find any information elsewhere on the internet except for the official documentation.
I've found out what my problem was: Not deep enough understanding of the relationship between the activity view controller and file providers.
As all my files reside in the shared container and are published also through the file provider extension, what I need to share through the activity view controller is the exact same URL that is shared through the file provider extension. Technically then the app that opens the file accesses it through there file provider mechanism.

Messenger not appearing in Share Sheet (UIActivityViewController) of branch.io

I am trying to share something via the branch.io share sheet:
let shareText = "Some Share Text"
let linkProperties = BranchLinkProperties()
linkProperties.feature = "Some"
linkProperties.addControlParam("$desktop_url", withValue: desktopURLString)
linkProperties.addControlParam("$android_url", withValue: androidURLString)
let object = BranchUniversalObject(canonicalIdentifier: "some.cannonical.identifier")
object.title = "Some Title"
object.imageUrl = someImageURL
object.contentDescription = "Some Content Description"
object.addMetadataKey("some_id", value: identifier)
object.showShareSheet(with: linkProperties,
andShareText: shareText,
from: self,
completion: completion)
All works great, except that the Facebook Messenger app does not show as option in the share sheet. Neither in the suggested options nor under 'More'. What is needed to achieve that?
I found the following question / answer for the default UIActivityViewController. How does that work with branch.io though? Facebook Messenger not showing up with UIActivityViewController
When you share via a share sheet on iOS - whether you use the Branch share sheet or UIActivityViewController - the choice of sharing options is not yours to define, it is defined by the user.
The interface for setting which apps will appear on the list can be accessed by opening a Share Sheet and then scrolling through the list of presented apps until you see the "..." (More) option. Tap on this button and you will be presented with the list of apps that can be shared to on the phone:
Each app will have a slider - if the slider is enabled for a particular app, that app will appear in the list.
Enabling Facebook Messenger for sharing on a user's phone is not something you can do from within your app as a developer.
You mention that the Messenger app is not even available when you tap the More button. This strikes me as odd; every device I check does have Facebook Messenger as an option if it is installed. Perhaps try removing and reinstalling Facebook Messenger.
I dived into the issue once more and I finally found the trouble maker. If I set feature of the branch link properties to a string value containing a space, Messenger disappears in the share sheet. The example:
let properties = BranchLinkProperties()
properties.feature = "Share News" //does not work, messenger does not appear in the share sheet
//properties.feature = "Share_News" //works, messenger appears in share sheet
object.showShareSheet(with: properties, andShareText: "Some Share Text", from: viewController, anchor: UIBarButtonItem()) { (activityType, completed) in
if (completed) {
print(String(format: "Branch TestBed: Completed sharing to %#", activityType!))
} else {
print("Branch TestBed: Link Sharing Cancelled\n")
}
}
feature is used as a parameter in the URL in Branch which is then given to the sharing extension. While this, I think, is an encoding issue in Branch, it seems that the Messenger sharing extension is not handling the URL in the same way as other apps. The 'broken url' does work with other sharing extensions. Hope this helps someone else! I will change the name of my feature to something without space for now.

Airdrop error message: "cannot receive all of these items at the same time"

I use the following code to share an image, some text, and a url using UIActivityViewController. Everything works fine except that when the use selects AirDrop, it gets a "cannot receive all of these items at the same time". If I only share the image, then AirDrop works. I need the text and the url for email, Facebook, twitter sharing methods.
Is there a way to keep the text and the URL and make AirDrop only share the image while Facebook, email, twitter methods of sharing continue to use the text and url together with the image that I'm trying to share?
NSString *text = [NSString stringWithFormat:#"I made this image using %# iOS app. Here is the link to download it:", [CloudHelper appName]];
NSURL *url = [NSURL URLWithString:APP_URL];
UIActivityViewController *activityController =
[[UIActivityViewController alloc]
initWithActivityItems:#[text, url, myImage]
applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
You'd want to create and share three objects that conform to the UIActivityItemSource, where one returns a string, one an image and one a URL. Then when the delegate callback requesting the item is called you check which activity type was selected (Facebook, Twitter, airdrop, etc) and have some return nil if that item doesn't apply.
So in the case of airdrop, only the item source for the image will return a non-nil value. You can take a look at the airdrop sample code to get some examples of how to implement UIActivityItemSource

How to implement Open In apps for plain text

I would like to provide the ability for users to tap the Action button and up pops the usual share sheet, which should include other apps to the right of the Messages, Facebook, etc icons - applications that can work with .txt files, or just an NSString.
I am currently displaying a Share sheet via UIActivityViewController, which is working great but it does not include other apps in the list. From reading other SO questions I concluded it's only possible to get those other apps to appear if you use UIDocumentInteractionController instead. I looked into creating a .txt file in a temp directory to share that file (instead of just sharing an NSString), but only Mail (no Copy) shows up when I tap the Share button. [Do note that if I run it on a real device not the simulator more apps other than Mail will appear and AirDrop too.] When I tap Mail, the app crashes: Unable to get data for URL: The operation couldn’t be completed. (Cocoa error 260.) Something is wrong with the way I'm creating/retrieving the .txt file.
My questions are:
Why is my code resulting in a crash when attempting to share the .txt file?
How can I get the Copy option to appear in the same Share sheet as the one that includes other apps?
To summarize: I need a share sheet that includes: Copy, AirDrop, Messages, Mail, Facebook, Twitter, Pages, Dropbox, etc for a simple string of text. Thanks!
The following lines of code lie inside my IBAction share button tap function:
UIActivityViewController approach:
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:#[self.myUITextField.text] applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
Result:
UIDocumentInteractionController approach:
NSString *fileName = [NSString stringWithFormat:#"%#mytextfile.txt", NSTemporaryDirectory()];
[self.myUITextField.text writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSURL *textFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"mytextfile.txt"]];
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:textFileURL];
[documentInteractionController presentOptionsMenuFromBarButtonItem:sender animated:YES];
Result (will show more apps and AirDrop if I run on a real device):
Example of what I want to obtain - minus the 3 extra options at the bottom:
If I cannot obtain the above screenshot with a string (instead of a photo) for some reason, I am willing to implement it how Dropbox has done it. They added an Open In button at the bottom that presents a different sheet that only shows additional apps. Note that I would still need a Copy option on the original sheet.
Question 1: Why is my code resulting in a crash
Cocoa error 260 is an NSFileReadNoSuchFileError according to the Foundation Constants Reference document. Looking at your code, the only way how I can see that file creation might fail is if self.myUITextField is nil.
I suggest that you check this first. If the property is not nil, then check whether writeToFile:atomically:encoding:error: returns an error.
Question 2: How can I get the Copy option to appear
First, assign a delegate to the controller:
documentInteractionController.delegate = self;
Then implement the following two delegate methods:
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller canPerformAction:(SEL)action
{
if (#selector(copy:) == action)
return YES;
else
return NO;
}
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller performAction:(SEL)action
{
if (#selector(copy:) != action)
return NO;
// Perform the copy: action
return YES;
}
Both methods are marked deprecated since iOS 6, but they still seem to work in iOS 7. Unfortunately, I have no idea how to implement the copy: action without those two methods - and neither does Apple, or so it seems to me, since they do not offer a replacement, and the official Document Interaction Programming Topics for iOS document still happily refers to the methods without indication that they are deprecated.
Anyway, here's a simple but complete implementation of the second delegate method:
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller performAction:(SEL)action
{
if (#selector(copy:) != action)
return NO;
NSStringEncoding usedEncoding;
NSError* error;
NSString* fileContent = [NSString stringWithContentsOfURL:controller.URL
usedEncoding:&usedEncoding
error:&error];
UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setString:fileContent];
return YES;
}

Can I send UIDocuments by email, as an attachment?

I'm working with Xcode.
In my app I save some UIdocuments at that location
[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
I'm searching for a way to share documents, my first option is by email.
Can I send those documents by email, as an attachment? Can I open then with another device with the same app?
You could do like the following.
Create a MFMailComposeViewController and use - (void)addAttachmentData:(NSData*)attachment mimeType:(NSString*)mimeType fileName:(NSString*)filename method to add your attachment.
For example.
MFMailComposeViewController *mailVC = [[MFMailComposeViewController alloc] init];
[mailVC setSubject:#"Shared documents"];
[mailVC setToRecipients:#[#"sample#example.com"]];
[mailVC setMessageBody:#"Here the docs I want to share" isHTML:NO];
[mailComposer addAttachmentData:pdfData mimeType:#"application/pdf" fileName:#"file.pdf"];
[mailVC setMailComposeDelegate:self];
[self presentViewController:mailVC animated:YES completion:nil];
where pdfData is of type NSData. So, you need to transform your document into a NSData.
From Apple doc.
addAttachmentData:mimeType:fileName:
This method attaches the specified data after the message body but
before the user’s signature. You may attach multiple files (using
different file names) but must do so prior to displaying the mail
composition interface. Do not call this method after presenting the
interface to the user.
About the second part of your question. Could you explain what type of document do you need to display?
In the meantime, take a look at Adding "Open In..." option to iOS app.
To send any attachment you need to get the contents into an NSData object. If the document is on disk then this is simple. You just need the path or file URL to the document. Then you can create the NSData object using the path or URL.
If the receiver of the email has the same app and the app is setup to appear in the "Open In" menu for documents of this type, then the user can open the app from the attachment. Your app then just needs to know what to do when it is asked to open a file of this type. There are plenty of existing documentation and questions here on SO that describe how to register an app to open certain file types.

Resources