Detect which App was launched using UIActivityViewController - ios

When an external app is launched using UIActivityViewController, can we detect which app was used in the completionHandler?
In Apple's documentation, there is mention of a property UIActivity.ActivityType, but it seems this is used only to detect "built-in activities". How can I detect if, eg. WhatsApp Messenger was launched?

When setting up your UIActivityViewController, use the completionWithItemsHandler to setup a completion closure that is called when the user makes a selection.
This completion block takes four parameters: the (optional) selected activity, a completion indicator, an optional array of returned items, and an error.
The selected activity is of type UIActivity.ActivityType. Its rawValue is a String representing the activity. If the activity type isn't one of the provided constants, compare its rawValue against a string you determine by running some tests to see its value for a given activity such as WhatsApp.

In Objective-C
You can use this code to know which activity was used
shareSheetVC.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *error){
if (completed) {
NSLog(#"Shared by activity %#", activityType);
} else {
NSLog(#"ShareSheet was closed");
}
if (error) {
NSLog(#"An Error occured: %#, %#", error.localizedDescription, error.localizedFailureReason);
}
};
Output when I shared using WhatsApp

Related

How to return error from share extension

if i close my extension with
[self.extensionContext cancelRequestWithError:error];
the error is not returned to the app via UIActivityViewController completion blocks activityError
[vc setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
// activityError is nil, even when cancelRequestWithError: called with error
}
Create a userDefaults Suite with app groups and set a boolean value indicating error if it happened in the Extension / save it's string description and read it in the app , also don't forget to make it false / nil after process . . .

UIActivityViewController completion handler still calls action even if user presses cancel

This is really annoying! Completed is always returned yes even if the user cancels the share option. e.g. I tap on the facebook share and dont share, just tap cancel. Is there a way to track this?
[activityVC setCompletionWithItemsHandler:
^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
if (completed)
{
[GoogleAnalytics trackShareEvents:activityType];
}else
{
[GoogleAnalytics trackEventWithCategory:#"Share" action:#"Failed/Canceled" eventLabel:activityType];
}
}];

iPhone - requesting Motion activity

I'm working on an iOS application in which I use Motion Activity Manager (in more detail - pedometer). When application launches I need to check is Motion Activity is allowed by user. I do this by doing
_motionActivityManager = [[CMMotionActivityManager alloc] init];
_pedometer = [[CMPedometer alloc] init];
[_pedometer queryPedometerDataFromDate : [NSDate date]
toDate : [NSDate date]
withHandler : ^(CMPedometerData *pedometerData, NSError *error) {
// BP1
if (error != nil) {
// BP2
}
else {
// BP3
}
}];
As discussed here ☛ iOS - is Motion Activity Enabled in Settings > Privacy > Motion Activity
In my understanding this code will trigger "alert window" asking user to opt-in/out.
What happens in my case is that when I run application first time (aka. all warnings are reset), application hangs before 'BP1' (callback is never executed) and then if I stop application with xCode or press home button "alert window" appears. And if I opt-in everything is good, on second run 'BP3' is reached (or 'BP2' if I opt-out).
What I tried do far:
I implemented another way of checking using async execution
[_pedometer queryPedometerDataFromDate : [NSDate date]
toDate : [NSDate date]
withHandler : ^(CMPedometerData *pedometerData, NSError *error) {
// Because CMPedometer dispatches to an arbitrary queue, it's very important
// to dispatch any handler block that modifies the UI back to the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
authorizationCheckCompletedHandler(!error || error.code != CMErrorMotionActivityNotAuthorized);
});
}];
This doesn't hang the application, but "alert window" is never showed
I executed this "checking snippet" in later time in code - but again - application hangs
Essentially, use can first be sure that the alert view will not block your App, when the first view has appeared, ie. in onViewDidAppear.
For example do:
-(void) viewDidAppear:(BOOL)animated {
if ([MyActivityManager checkAvailability]) { // motion and activity availability checks
[myDataManager checkAuthorization:^(BOOL authorized) { // is authorized
dispatch_async(dispatch_get_main_queue(), ^{
if (authorized) {
// do your UI update etc...
}
else {
// maybe tell the user that this App requires motion and tell him about activating it in settings...
}
});
}];
}
}
This is what I do myself. I based my App on the Apple example code as well and noticed, that the example also has the problem you are describing.

EAAccessory showBluetoothAccessoryPickerWithNameFilter method always shows me empty and unable to list device

I am trying to use the method showBluetoothAccessoryPickerWithNameFilter:nil completion:nil to show a picker in my app but it always shows an empty list. I already included the protocol string in my app. If I use the device's setting page, I am able to see my MFI device and pair and connect to it. But it refuses to show it in the alert box that the picker generates. Any help? I am using iOS 7.0.
[[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:nil completion:^(NSError *error) {
if (error) {
NSLog(#"error :%#", error);
}
else{
NSLog(#"You make it! Well done!!!");
}
}];

Facebook iOS SDK - Post Custom Story

I'm trying to do something that seems to be an order of magnitude more difficult than necessary.
I've got an iOS app that is written against a Parse backend. Our app's users can create items to store and they can sometimes generate alerts for these items.
I want to post a simple alert for a user to Facebook.
I've read quite a bit on the Facebook website about integrating Facebook into iOS, but their documentation is confusing and I've seen more than one person on StackOverflow mention that their iOS code is at least sometimes flawed.
Basically, the custom story will include the following items:
Story Title
Story Description (usually not very much text in here)
One or more pictures (I'd settle for 1, but I'd love to be able to post multiples)
If possible, a user-supplied GPS location.
I will admit up front that I'm a developer who is learning Facebook for work purposes, and I am far from being an expert on Facebook.
When I play around in the Facebook app, I can add a status update that can include:
Text
Picture(s)
Friends
What I'm doing
Where I am (or some location)
However, in the Facebook SDK, there appears to be only the following 2 options for posting a status update:
startForPostStatusUpdate:completionHandler:
startForPostStatusUpdate:place:tags:completionHandler:
If I'm missing something there, please let me know.
Secondly, I'm trying to follow Facebook's documentation and sample code to do the following sequence of events:
Upload and stage an image resource
Create an object (in my app, I've registered an "alert" object)
Create an action (in my app, I've registered a "post" action)
Here's the code that I've pieced together from Facebook's website (comments are mostly from other people's code, not my own):
- (void)postToFacebookWithAlert:(PFObject *)alert image:(UIImage *)image {
// Stage the image
[FBRequestConnection startForUploadStagingResourceWithImage:image completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (error != nil) {
// See: https://developers.facebook.com/docs/ios/errors
} else {
// Package image inside a dictionary, inside an array like we'll need it for the object
NSArray *image = #[#{#"url": [result objectForKey:#"uri"], #"user_generated" : #"true" }];
// Create an object
NSString *caption = // user-generate content
NSMutableDictionary<FBOpenGraphObject> *alert;
alert = [FBGraphObject openGraphObjectForPostWithType:#"<myapp>:alert"
title:caption
image:image
url:nil
description:alert[#"subtitle"]];
alert.provisionedForPost = YES;
// Post custom object
[FBRequestConnection startForPostOpenGraphObject:alert completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (error != nil) {
// An error occurred
} else {
NSMutableDictionary<FBGraphObject> *post = [FBGraphObject graphObject];
post[#"alert"] = result[#"id"];
[FBRequestConnection startForPostWithGraphPath:#"me/<myapp>:post" graphObject:post completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(#"startForPostWithGraphPath:graphObject:completionHandler:");
if (error != nil) {
// An error occurred
NSLog(#"Encountered an error posting to Open Graph: %#", error);
} else {
NSLog(#"OG story posted, story id: %#", result[#"id"]);
}
}];
}
}];
}
}];
}
I've actually gotten this code to work one time. I'm logged in as my own personal Facebook account. If I go to my activity log, I can see the items that I'm trying to post. If I go to my photos, I can see the photos that I'm trying to upload. If I view my profile from my wife's app/account, it doesn't show up.
Does anyone have working code that does what I want?
Maybe you need to set "fb:explicitly_shared" to "true" and it should be reviewed by Facebook.
After your review pass, you can add the option like below.
[post setObject:#"true" forKey:#"fb:explicitly_shared"];

Resources