Using the FBSDK mentioned in the title of this question, I present a simple share dialog in a view controller:
// Setup the content for the share
FBSDKShareLinkContent *content = [[FBSDKShareLinkContent alloc] init];
content.contentURL = linkUrl;
content.contentDescription = description;
content.contentTitle = title;
content.imageURL = imageUrl;
// Show the share dialog
[FBSDKShareDialog showFromViewController:controller
withContent:content
delegate:someDelegate];
And implement the delegate method...
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
NSLog(#"Sweet, they shared.");
}
So far so good, as long as the user has the Facebook application installed on their device. The issue arises when the user does not have Facebook installed. If this is the case, Safari opens up to a web version of Facebook's login flow (this is fine), but if you then switch back to the original application without logging into Facebook / performing any additional tasks, the completion delegate method shown above is called.
Does anyone have any experience with a workaround for this? It seems like there should be a reliable way for determining whether or not the post did indeed occur.
Note: The above code is pretty pseudo-ish. In the actual implementation I have indeed implemented all of the delegate call backs (didComplete, didCancel, and didFail).
Edit: It turns out, the results dictionary is empty when the completion method is called if the Facebook app is installed on the device. To overcome this, a check needs to be done to see if Facebook is installed first.
Of course after posting I stumbled upon the answer. The results dictionary returned in the didCompleteWithResults method contains a postId key if the share actually occurred. So the logic is as simple as:
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
NSURL *fbURL = [NSURL URLWithString:#"fb://"];
if (![[UIApplication sharedApplication] canOpenURL:fbURL])
if (results[#"postId"]) {
NSLog(#"Sweet, they shared, and Facebook isn't installed.");
} else {
NSLog(#"The post didn't complete, they probably switched back to the app");
}
} else {
NSLog(#"Sweet, they shared, and Facebook is installed.");
}
}
Although this works, it doesn't seem to be a very safe way of going about things (what if Facebook changes the key from "postId" to something else in the future? Unlikely but you get my point).
Related
I integrated OneSignal to my iOS-App (WKWebview). But its automatically opening another webview window. I just want to load the given URL into my Webview, not another one. Universal Links/Deep Linking is also integrated.
I read that I can give "additional data" to every send notification, but I do not really know how I can catch that inside my AppDelegate (or somewhere else).
I added OneSignal this way:
https://documentation.onesignal.com/docs/ios-sdk-setup
You will want to do this in the AppDelegate.m
The payload will look like this
// (Optional) - Create block that will fire when a notification is tapped on.
id notificationOpenedBlock = ^(OSNotificationOpenedResult *result) {
OSNotificationPayload* payload = result.notification.payload;
NSString* messageTitle = #"OneSignal Example";
NSString* fullMessage = [payload.body copy];
if (payload.additionalData) {
if (payload.additionalData[#"OpenURL"])
redVC.receivedUrl = [NSURL URLWithString:(NSString *)payload.additionalData[#"OpenURL"]];
I created my iMessage extension, when I try to open it, the first screen appears but it is totally frozen, and it does not react in any way.
I've put logs in the viewDidLoad of that first view and nothing appears there, after a few seconds I can already see those logs.
To make the application freezing lose that status, user has to slide screen left or right and back again.
I've tried looking all over the web for someone who happens to be the same, but I could not find anything.
It does not come to mind more screenshots or portions of code add, if you think I should provide some additional information, just let me know
Any help would be appreciated.
Thank you.
UPDATE:
This is my Project Structure.
This is my viewDidLoad code.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"here viewDidLoad iMessage ext~~~!");
[self applyCornerRadiusToBtn];
[self registerPresentationAction];
NSDictionary *user = [self getUserInfoFromHostApp];
if (user) {
NSLog(#"Here != null user info");
//It is assumed that when you enter this point and run this log, the app should navigate to the next screen, but it does not.
[self performSegueWithIdentifier:#"goToYoutubeListIm" sender:nil];
} else {
NSLog(#"Here userInfo null");
}
}
- (NSDictionary *)getUserInfoFromHostApp
{
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.xxxxx"];
NSDictionary *userNameSaved = [myDefaults objectForKey:#"userInfoExt"];;
NSLog(#"userNameSaved in xxxx Ext ==> %#",userNameSaved);
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxx"];
NSLog(#"groupURL ==> %#",groupURL);
return userNameSaved;
}
For all concerned I have found the problem or problems to be accurate.
1) I was creating my controllers type MSMessagesAppViewController. Apparently there should only be one controller of this type.
2) I had logic in the viewDidAppear in my MSMessagesAppViewController. For some strange reason this also caused the problem, I had to get the logic out there and force the user to interact with a button to execute the logic that was in the didAppear
I would like to provide the option to share a picture to Facebook. When I have the FB app installed, I have no problem posting to my feed. However, when no app is installed, I get the following error:
-canOpenURL: failed for URL: "fbauth2:/" - error: "(null)"
Facebook says in the docs I don't need to do anything:
Now the SDK automatically checks for the native Facebook app. If it isn't installed, the SDK switches people to their default browser and opens the Feed Dialog. If someone wants to share an Open Graph story, the SDK opens the Web Share Dialog.
One hack I found to check is the following:
BOOL isInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"fbauth2:/"]];
if (isInstalled) {....}
Is there a better more 'correct' way?
The method I use to share that throws the error is the following:
- (void)showFBShare {
FBSDKSharePhoto *photo = [[FBSDKSharePhoto alloc] init];
photo.image = self.shareImage;
photo.userGenerated = YES;
FBSDKSharePhotoContent *content = [[FBSDKSharePhotoContent alloc] init];
content.photos = #[photo];
[FBSDKShareDialog showFromViewController:self
withContent:content
delegate:nil];
}
tl;dr: Instantiate and configure FBSDKShareDialog as dialog and set dialog.mode = FBSDKShareDialogModeNative or FBSDKShareDialogModeShareSheet. if [dialog canShow] then [dialog show].
FBSDK version used for testing solution:
- FBSDKCoreKit (4.12.0):
- FBSDKShareKit (4.12.0):
It does not seem like the method throws any exceptions when calling the showFromViewController method so a try / catch fix is ruled out. However if you instantiate the FBSDKShareDialog you can call the canShow method to check if the app is able to show the dialog in its current mode (in this case FBSDKShareDialogModeShareSheet).
Method description from source (FBSDKSharing.h:64):
#abstract A Boolean value that indicates whether the receiver can initiate a share.
#discussion May return NO if the appropriate Facebook app is not installed and is required or an access token is
required but not available. This method does not validate the content on the receiver, so this can be checked before
building up the content.
#see [FBSDKSharing validateWithError:]
#result YES if the receiver can share, otherwise NO.
- (BOOL)canShow;
Code example:
...
FBSDKShareDialog *dialog = [[FBSDKShareDialog alloc] init];
dialog.fromViewController = self;
dialog.shareContent = content;
/**
* !important: Only mode FBSDKShareDialogModeNative and
* FBSDKShareDialogModeShareSheet do the appropriate checks.
* Otherwise canShow returns yes regardless.
*/
dialog.mode = FBSDKShareDialogModeShareSheet;
if ([dialog canShow]) {
[dialog show];
} else {
// handle exception case.
}
Issues when running on the simulator (from the docs):
iOS Simulator and Testing
If you are using Simulator to test sharing in your application, you will see errors if you try to share videos, Photos or Open Graph Actions. This is because you need Facebook for iOS installed which provides the Share Dialog. We do not support this for Simulator.
In the case of link shares, you do not need Facebook for iOS installed so this test case is possible. To test other Sharing scenarios, set up an actual test device with with Facebook for iOS installed.
As mentioned above, only link content is supported on the simulator. I.e.:
FBSDKShareLinkContent *linkContent = [[FBSDKShareLinkContent alloc] init];
linkContent.contentURL = [NSURL URLWithString:#"somelink"];
linkContent.quote = #"Very interesting, shareworthy link.";
[FBSDKShareDialog showFromViewController:self
withContent:linkContent
delegate:nil];
I have read allot in previous threads about FacebookSDK vs SocialFramework but I cant find anything regarding the more specific area of FriendPicker.
I have a picture in my application. Right now I use SLcomposeView to post to Facebook which works great.
I now want to add the possibility to tag a facebookfriend in that picture and post the picture with the friend included.
I first followed the: FriendPicker example - FacebookDev
My LoadData function will however not load any data probably because it doesnt use the Facebook Account from settings.
I then tried the code from the provided Facebook FreindPicker example (From the FacebookSDK). I then get bounced to facebook sign in but I receive sign in error (Error: 2) probably because I am already signed in through settings?
How do I best design the application for handling this?
Can I include "friendpicking" in the SlComposeView or do I need to remove it and only use Facebook SDK?
Will be better if you can post some of your code... Im using the following example to invoke the FBFriendPickerViewController
- (IBAction)addTAG:(id)sender {
if (self.friendPickerController == nil) {
// Create friend picker, and get data loaded into it.
self.friendPickerController = [[FBFriendPickerViewController alloc] init];
self.friendPickerController.title = #"Select Friends";
self.friendPickerController.delegate = self;
}
[self.friendPickerController loadData];
[self.friendPickerController clearSelection];
// Present the friend picker
[self.friendPickerController presentModallyFromViewController:self
animated:YES
handler:^(FBViewController *sender, BOOL donePressed) {
if (donePressed) {
self.selectedFriends = self.friendPickerController.selection;
NSLog(#"list of friends: %#", self.selectedFriends);
}
}];
}
Regards
Edited// the Scrumptious example app contains all what you need. Download the las facebokk SDK for iOS, go to samples.
I've been struggling with this issue for a while now and I just can't seem to reproduce it accurately enough to describe the exact use-case. Essentially, what I'm doing is issuing a request for opening a native iOS 6.0 Facebook share dialog (using the Facebook iOS SDK 3.1.1):
if ([[SocialManager sharedManager] isNativeFacebookShareDialogAvailable]) {
if (!url) {
url = [NSURL URLWithString:#""];
}
if (!imageUrl) {
imageUrl = [NSURL URLWithString:#""];
}
dispatch_async(backgroundQueue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
UIImage *image = [UIImage imageWithData:imageData];
if (!image) {
image = [[UIImage alloc] init];
}
if ([FBNativeDialogs canPresentShareDialogWithSession:[FBSession activeSession]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[FBNativeDialogs presentShareDialogModallyFrom:sender initialText:initialText images:#[image] urls:#[url] handler:^(FBNativeDialogResult result, NSError *error) {
if (error) {
failBlock([[error userInfo] description]);
} else {
if (result == FBNativeDialogResultSucceeded) {
completionBlock();
} else if (result == FBNativeDialogResultCancelled) {
failBlock(#"User cancelled");
} else if (result == FBNativeDialogResultError) {
failBlock(#"Unknown error");
}
}
}];
});
} else {
LogErr(#"Can't display native share dialog for active session");
}
});
}
Right after presentShareDialogModallyFrom:sender is called, I either get the following crash log:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x1d161490> was mutated while being enumerated.'
*** First throw call stack:
(0x32ede2a3 0x326b097f 0x32eddd85 0x35da094d 0x32edb62f 0x35da07f5 0x35e7e5e5 0x35e0ccd7 0x35e0cb6d 0x372c490f 0x35e0ca61 0x35e160d5 0x372b783b 0x35e160b1 0x372b711f 0x372b699b 0x372b6895 0x372c5215 0x372c53b9 0x36f5fa11 0x36f5f8a4)
libc++abi.dylib: terminate called throwing an exception
OR I get no crash and the native share dialog appears as it should.
The stack implies a call on a thread called UIRemoteViewControllerCreationRequest at this point, here are 2 examples for two different crashes:
Thanks for your help
After a lot of experimenting with my application and looking into Facebook SDK source I realized 3 things:
Creating a SLComposeViewController by yourself doesn't help. Facebook SDK is pretty simple in this, it just creates the controller exactly like the code in the answer with bonus.
When you are authorizing the FB session, your application is deactivated once or more times. This is caused by the permission confirmation alerts appearing.
The UIRemoteViewController is actually the SLComposeViewController which is run in a different process.
What caused my error?
User confirms FB permissions
This triggers applicationDidBecomeActive:
It also triggers FB callback to present the dialog.
My applicationDidBecomeActive: was doing something with the UI what was not supposed to be done when the FB dialogs were appearing (tirggering a table reload).
Also, there is another thing to be careful of - the handler of presentShareDialogModallyFrom... is not called on any particular thread (see SLComposeViewController docs). That means that you should use dispatch_async(dispatch_get_main_queue(), ...) from the handler if you are updating UI from it.
EDIT:
Obviously, the previous steps fixed some crashes but one of the crashes was not solved. After a lot of googling and searching Apple Developer forums, I think there is a bug in iOS 6 connected with Remote Controllers and using UIAppearance, especially the appearance of UINavigationBar. I am currently removing the use of UIApperance from my app.
This is a very strange way to post a post to Facebook. Here is a much simpler way that never crashes.
ViewController.h
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#import <Accounts/Accounts.h>
#interface ViewController : UIViewController {
SLComposeViewController *mySLComposerSheet;
}
- (IBAction)PostToFacebook:(id)sender;
#end
ViewController.m
#implementation ViewController
- (IBAction)PostToFacebook:(id)sender {
mySLComposerSheet = [[SLComposeViewController alloc] init];
mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[mySLComposerSheet setInitialText:#"Place Text Here"];
[self presentViewController:mySLComposerSheet animated:YES completion:nil];
}
#end
If needed, there is a video here.
Sorry some of this is rather guessing, but I thought I would try:
Are you sure that canPresentShareDialogWithSession is safe to call from a non-UI thread?
You have a line in both stacks of _NSDictionaryEnumerate. It looks like from higher functions, that something is calling enumerateKeysAndObjectsUsingBlock:.
Based on your note of things crashing just after [presentShareDialogModallyFrom:sender]. Is there something being released when sender's view disappears?
Variable "image" is either retained or autoreleased depending on which code path it took.
I think the problem is what Walt already said. In your code, something is done outside the main thread.
In the crash log you can see that someone is setting the appearance (UIAppearance) on some UI element from a non-UI thread. That's the problem. This operation MUST be done ONLY in the UI (main) thread.
I believe this has to do with the combination of the UIAppearance methods and launching a UIRemoteViewController from a background thread. We're having the same issue in our app. I'm going to change our presenter class to present all our remote viewControllers from the main thread and see if that helps.
In your case, I guess something in the Facebook SDK is presenting something from a background thread.
I'll update after I've verified that my fix works.
This is a bug in iOS 6 and Social Framework, check answer here UINavigationBar appearance and Facebook-Sheet Bug
How to solve this?
Simply instead of using [UINavigationBar appearance] use [UINavigationBar appearanceWhenContainedIn:...]
You can use custom class for your navigation controller (eg. CustomNavigationController) and then apply it in appearance:
[UINavigationBar appearanceWhenContainedIn:[CustomNavigationController class], nil]
From my experiments it should be used on all appearance methods, not only UINavigationBar but also UIBarButtonItem etc. (on every object you send appearance message to)
I think you know what is obvious but I met this error once and it was an NSMutableArray that was mutated while being enumerated inside a for..in statement.
Look at the NSMutableArray, you'll find your error.
Byt the way, if you target ios6, why don't you use the social framework with native facebook implementation ?