iOS6 Twitter integration - cocoa-touch

There seems to be a difference between the iPhone simulator and actual device when checking if Twitter is available.
I check if a Twitter account is setup by using this code: [SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter];
In the simulator there is a nice UIAlertView informing the user that there are no Twitter accounts setup and two buttons one for Settings and one for Cancel.
However when I run my app on my device it will not show the above UIAlertView. Why is that? And how can I catch what button is tapped in the above UIAlertView (since I did not instantiate it?)
This is what it looks like on the simulator:

To handle the result of the Twitter call, You can use this snippet :
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
SLComposeViewControllerCompletionHandler __block completionHandler=^(SLComposeViewControllerResult result){
[twitterController dismissViewControllerAnimated:YES completion:nil];
switch(result){
case SLComposeViewControllerResultCancelled:
default:
{
NSLog(#"Cancelled.....");
}
break;
case SLComposeViewControllerResultDone:
{
NSLog(#"Posted....");
}
break;
}};
[twitterController setCompletionHandler:completionHandler];
...
...
}

I also am having the same issue that the device doesn't display the UIAlert like the simulator does. Until Apple fixes it, this is what I'm doing (not as clean or nice as the simulator, and requires the user to manually go to the homescreen) Apple, please fix this!
Edit: the stock Apple apps show the UIAlert perfectly of course, example: sharing a photo from the photo app without having a Twitter/Facebook account will display the correct UIAlert.
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[mySLComposerSheet setInitialText:#"Check out the app:"];
[mySLComposerSheet addImage:[UIImage imageNamed:#"test.png"]];
[mySLComposerSheet addURL:[NSURL URLWithString:#"http://urlofyourapp.com"]];
[self presentViewController:mySLComposerSheet animated:YES completion:nil];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"You need to setup an account in the Settings app under Twitter to use this feature." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
[mySLComposerSheet setCompletionHandler:^(SLComposeViewControllerResult result) {
if (result == SLComposeViewControllerResultCancelled){[mySLComposerSheet dismissModalViewControllerAnimated:YES];}
else if (result == SLComposeViewControllerResultDone){[mySLComposerSheet dismissModalViewControllerAnimated:YES];}
}];

Related

setInitialText not work with IOS 8.3 (facebook,social,SLComposeViewController)

The functionality of SLComposeViewController no longer works as expected with the newest Facebook iPhone app update as of April 24th. Any initial text specified is ignored, though the setInitialText method returns true as if it was successful. The dialog then always returns "Done" whether you hit "Done" or "Cancel". I realize this is an Apple call and I'm not even using the Facebook SDK, but I have verified that everything works perfectly with the previous version of the Facebook App installed but when you upgrade the Facebook app on your iPhone, this functionality no longer works as expected.
Note that the result of the completion handler now always returns "Done" - even when you hit "Cancel" and also, the setInitialText: does nothing now. Verified that the same code worked pre-the april 24th release.
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[controller setInitialText:#"hiiiiiii"];
[controller setCompletionHandler:^(SLComposeViewControllerResult result)
{
if (result == SLComposeViewControllerResultCancelled)
{
NSLog(#"The user cancelled.");
}
else if (result == SLComposeViewControllerResultDone)
{
NSLog(#"The user posted to Facebook");
}
}];
[self presentViewController:controller animated:YES completion:nil];
}
else
{
SCLAlertView *alert = [[SCLAlertView alloc] init];
[alert showWarning:self title:#"alert" subTitle:#"facebook not installed" closeButtonTitle:#"ok" duration:0.0f];
}
At the time of this post, FB's still not allowing to set initial text, even using FB SDK.
A way I implemented to bypass the issue is to copy the content to clipboard and show a dialog to notify users that they can paste the preset content.
setInitialText: is not working anymore because Facebook has recently changed its policy about prefilling, but addURL: is still working and may be useful.
SLComposeViewController *mySLComposerSheet = [[SLComposeViewController alloc] init];
mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
NSURL *url = [[NSURL alloc] initWithString:linkString];
[mySLComposerSheet addURL:url];
[self presentViewController:mySLComposerSheet animated:YES completion:nil];
[mySLComposerSheet setCompletionHandler:^(SLComposeViewControllerResult result) {
NSString *output;
switch (result) {
case SLComposeViewControllerResultCancelled:
NSLog(#"SLComposeViewControllerResultCancelled");
break;
case SLComposeViewControllerResultDone:
NSLog(#"SLComposeViewControllerResultDone");
break;
}
}];
This way I can prefill Facebook post composer with the URL to my App.
I hope it to be useful.

AddressBook privacy settings not enforced

I'm doing some working getting an app to line up with the new privacy settings in iOS 8. I've completed the requirements satisfactorily for camera access and now I'm taking a look at how this app access the address book. I'm new to working with address book APIs so these questions may have obvious answers.
As with camera access, I was thinking that access to contacts would behave similarly with respect to the status of the privacy settings granted to the app. The thing is, so far, no matter what the value of ABAuthorizationStatus is - kABAuthorizationStatusNotDetermined or kABAuthorizationStatusDenied - access is always allowed.
Also, when the status is kABAuthorizationStatusNotDetermined, the 'Okay/Don't Allow' dialog is never displayed to the user (I've erased the phone and resinstalled the app to confirm). Furthermore, the app never shows up under the privacy settings. I assume this is because the status is always kABAuthorizationStatusNotDetermined.
The code to initiate access to contacts is below. The controller is shown (read only) and contacts' information can be obtained. Delegate code not shown.
// Debug - Value is always denied or not determined.
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
// Existing code since iOS 7 - always works despite status.
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
It's great that everything seems to be working but I'd like what, if anything, I'm doing wrong.
iPhone 6,
iOS 8.0.2,
Xcode 6
Thanks!
From what I've found (and I could be wrong) while dealing with updating a code base to correctly deal with the new privacy settings in iOS 8 is that such settings are not respected uniformly across features. For example, without changes to address privacy settings for locations, access to CLLocationManager won't work. Depending on the app, this may appear to the user as though nothing is happening or an error message may appear. However, if no such changes are made for address book privacy settings, access is always granted.
I figured out my error after stumbling upon some extremely helpful example code posted by Apple. Below is code I add/modified so that privacy settings are respected properly in the app I work on. When I find the link to the code example I will post it.
-(void) presentAddressBookPicker {
switch (ABAddressBookGetAuthorizationStatus()) {
case kABAuthorizationStatusAuthorized:
[self accessGrantedForAddressBook];
break;
case kABAuthorizationStatusNotDetermined:
[self requestAccessToAddressBook];
break;
case kABAuthorizationStatusRestricted:
case kABAuthorizationStatusDenied:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:#"Unable to access address book"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
break;
default:
// Unlikely but log it anyway.
DLog(#"Unknown address book status.");
break;
}
}
-(void) accessGrantedForAddressBook {
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
-(void) requestAccessToAddressBook {
__weak MyWebViewController* weakSelf = self;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf accessGrantedForAddressBook];
});
}
CFRelease(addressBook);
});
}

iOS facebook SDK: Login already authenticated

I have had the Facebook iOS SDK running in an app I've been working on for a few months. At this point I am mostly using the SDK for SSO purposes.
Since I have started using iOS 6.0 I have been seeing an issue where the "Login to use your FB account with MY_APP" modal is blocking my current view.
The odd thing is that the user is already authenticated and apparently authorized to use my app. This is proven by the fact that I can get the user's email address and such.
It is also important to note that if I click the "X" it will close and everything is fine (user is authenticated/authorized). If I login as it tells me, it shows the "MY_APP is already authorized" modal which I cannot click the "Okay" button but I can click the "X" which again drops me back into my view with the user authenticated and authorized.
Here you can see the it:
To authenticate I am calling the following method in the appdelegate:
[appDelegate openSessionWithAllowLoginUI:YES];
The following are the relevant FB methods in my appdelegate:
// FACEBOOK STUFF
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
NSArray *permissions = [NSArray arrayWithObjects: #"email", nil];
return [FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
[self sessionStateChanged:session state:status error:error];
}];
}
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState)state
error:(NSError *)error {
// FBSample logic
// Any time the session is closed, we want to display the login controller (the user
// cannot use the application unless they are logged in to Facebook). When the session
// is opened successfully, hide the login controller and show the main UI.
switch (state) {
case FBSessionStateOpen: {
FBCacheDescriptor *cacheDescriptor = [FBFriendPickerViewController cacheDescriptor];
[cacheDescriptor prefetchAndCacheForSession:session];
[self sendAuthenticationStatusChangedNotification];
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
// FBSample logic
// Once the user has logged in, we want them to be looking at the root view.
[FBSession.activeSession closeAndClearTokenInformation];
//[self showLoginView];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// FBSample logic
// We need to handle URLs by passing them to FBSession in order for SSO authentication
// to work.
return [FBSession.activeSession handleOpenURL:url];
}
EDIT 1
I noticed that this is happing on one of my phones but not the other. The phone that wasn't working had an older version of the FB app installed. Updating the FB app stopped this issue from happening. I am still interested in a fix to avoid others from experiencing the same issue.
EDIT 2
The issue went away for a little but now is back even with the new facebook app installed. Please help!
The problem for me actually turned out to be a silly one. I was calling
[appDelegate openSessionWithAllowLoginUI:YES];
two times due to a button click event that was wired up twice. As soon as it was only being called once, the issue went away!

Ask permission to access Camera Roll

I have a settings view where the user can choose switch on or off the feature 'Export to Camera Roll'
When the user switches it on for the first time (and not when he takes the first picture), I would like the app to ask him the permission to access the camera roll.
I've seen behavior in many app but can't find the way to do it.
I'm not sure if there is some build in method for this, but an easy way would be to use ALAssetsLibrary to pull some meaningless bit of information from the photo library when you turn the feature on. Then you can simply nullify what ever info you pulled, and you will have prompted the user for access to their photos.
The following code for example does nothing more than get the number of photos in the camera roll, but will be enough to trigger the permission prompt.
#import <AssetsLibrary/AssetsLibrary.h>
ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
[lib enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
NSLog(#"%zd", [group numberOfAssets]);
} failureBlock:^(NSError *error) {
if (error.code == ALAssetsLibraryAccessUserDeniedError) {
NSLog(#"user denied access, code: %zd", error.code);
} else {
NSLog(#"Other error code: %zd", error.code);
}
}];
EDIT: Just stumbled across this, below is how you can check the authorization status of your applications access to photo albums.
ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
if (status != ALAuthorizationStatusAuthorized) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Attention" message:#"Please give this app permission to access your photo library in your settings app!" delegate:nil cancelButtonTitle:#"Close" otherButtonTitles:nil, nil];
[alert show];
}
Since iOS 8 with Photos framework use:
Swift 3.0:
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
<#your code#>
case .restricted:
<#your code#>
case .denied:
<#your code#>
default:
// place for .notDetermined - in this callback status is already determined so should never get here
break
}
}
Objective-C:
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
switch (status) {
case PHAuthorizationStatusAuthorized:
<#your code#>
break;
case PHAuthorizationStatusRestricted:
<#your code#>
break;
case PHAuthorizationStatusDenied:
<#your code#>
break;
default:
break;
}
}];
Important note from documentation:
This method always returns immediately. If the user has previously granted or denied photo library access permission, it executes the handler block when called; otherwise, it displays an alert and executes the block only after the user has responded to the alert.
Since iOS 10, we also need to provide the photo library usage description in the info.plist file, which I described there. And then just use this code to make alert appear every time we need:
- (void)requestAuthorizationWithRedirectionToSettings {
dispatch_async(dispatch_get_main_queue(), ^{
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusAuthorized)
{
//We have permission. Do whatever is needed
}
else
{
//No permission. Trying to normally request it
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status != PHAuthorizationStatusAuthorized)
{
//User don't give us permission. Showing alert with redirection to settings
//Getting description string from info.plist file
NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"NSPhotoLibraryUsageDescription"];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:#"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:#"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}];
[alertController addAction:settingsAction];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
}];
}
});
}
Also, there are some common cases when the alert doesn't appear. To avoid copying I would like you to take a look at this answer.
The first time the user tries to write to camera roll on ios 6 he/she is automatically asked for permission. You don't have to add extra code (before that the authorisationstatus is ALAuthorizationStatusNotDetermined ).
If the user denies the first time you cannot ask again (as far as I know). The user has to manually change that app specific setting in the settings->privacy-> photos section.
There is one other option and that is that it the user cannot give permission due other restrictions like parental control, in that case the status is ALAuthorizationStatusRestricted
Swift:
import AssetsLibrary
var status:ALAuthorizationStatus = ALAssetsLibrary.authorizationStatus()
if status != ALAuthorizationStatus.Authorized{
println("User has not given authorization for the camera roll")
}
#import <AssetsLibrary/AssetsLibrary.h>
//////
ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
switch (status) {
case ALAuthorizationStatusRestricted:
{
//Tell user access to the photos are restricted
}
break;
case ALAuthorizationStatusDenied:
{
// Tell user access has previously been denied
}
break;
case ALAuthorizationStatusNotDetermined:
case ALAuthorizationStatusAuthorized:
// Try to show image picker
myPicker = [[UIImagePickerController alloc] init];
myPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
myPicker.delegate = self;
[self presentViewController: myPicker animated:YES completion:NULL];
break;
default:
break;
}
iOS 9.2.1, Xcode 7.2.1, ARC enabled
'ALAuthorizationStatus' is deprecated: first deprecated in iOS 9.0 -
Use PHAuthorizationStatus in the Photos framework instead
Please see this post for an updated solution:
Determine if the access to photo library is set or not - PHPhotoLibrary (iOS 8)
Key notes:
Most likely you are designing for iOS7.0+ as of todays date, because of this fact you will need to handle both ALAuthorizationStatus and PHAuthorizationStatus.
The easiest is to do...
if ([PHPhotoLibrary class])
{
//Use the Photos framework
}
else
{
//Use the Asset Library framework
}
You will need to decide which media collection you want to use as your source, this is dictated by the device that your app. will run on and which version of OS it is using.
You might want to direct the user to settings if the authorization is denied by user.

Prompt login alert with Twitter framework in iOS5?

As you all may know, since iOS5 there is a native Twitter framework which make it easy to post tweets from your app.
Is there a way to prompt an alert that forwards the user to the settings app and ask for username and password?
I know that i could solve the problem with the following code:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs:root=TWITTER"]];
But thats undocumented code..
Thanks in advance
Regards Billy
(My first post on SO)
In iOS5.1, we should use TWTweetComposeViewController to show the dialog since apple rejects apps using prefs:root=TWITTER.
But, I didn't like showing the tweet screen and keyboard
so I figured out the way to hide them, but show the pop up screen.
UPDATE:
Apple approved my app using this trick.
TWTweetComposeViewController *viewController = [[TWTweetComposeViewController alloc] init];
//hide the tweet screen
viewController.view.hidden = YES;
//fire tweetComposeView to show "No Twitter Accounts" alert view on iOS5.1
viewController.completionHandler = ^(TWTweetComposeViewControllerResult result) {
if (result == TWTweetComposeViewControllerResultCancelled) {
[self dismissModalViewControllerAnimated:NO];
}
};
[self presentModalViewController:viewController animated:NO];
//hide the keyboard
[viewController.view endEditing:YES];
//this approach doesn't work since you can't jump to settings
// [self dismissModalViewControllerAnimated:NO];
You don't need to implement this, if you set up your Twitter integration to make a post on Twitter and iOS detects that there is no Twitter account set up it will do this automatically for you!
This is a screenshot of one of my apps running on my iPhone 4S on iOS 5.1
The removal of Preferences links is in reference to custom actions by the developer, as in linking to your own preferences menu. This does not apply because not only is Twitter a built in function of iOS 5 but the UIAlertView that pops up to notify you isn't handled by the developer, it is an automatic function of iOS.
Here i found the way :
Display custom alert if no twitter account has been setup on your device settings:
if (![TWTweetComposeViewController canSendTweet]) {
UIAlertView *alertViewTwitter = [[[UIAlertView alloc]
initWithTitle:#"No Twitter Accounts"
message:#"There are no Twitter accounts configured. You can add or create a Twitter account in Settings."
delegate:self
cancelButtonTitle:#"Settings"
otherButtonTitles:#"Cancel",nil] autorelease];
[alertViewTwitter show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==0) {
TWTweetComposeViewController *ctrl = [[TWTweetComposeViewController alloc] init];
if ([ctrl respondsToSelector:#selector(alertView:clickedButtonAtIndex:)]) {
[(id <UIAlertViewDelegate>)ctrl alertView:alertView
clickedButtonAtIndex:0];
}
[ctrl release];
}
}
Hope this will make sense :)
It's not possible, although it should automatically ask the user to login, if the user isn't logged in already.
As of iOS 5.1 that feature has been removed, as seen here