I am working on Address book in My application,I just want to get an alertview for accessing Address.How Can i show System Generated Alert for accessing addressBook?
If you want to show the user a custom message when asking for permission to use their contacts or address book, instead of the default one shown above, you need to add this field to your Info.plist file:
NSContactsUsageDescription
Here is apple developer link for the same:
https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW14
// Check the authorization status of your application for Address Book
-(void)checkAddressBookAccess
{
switch (ABAddressBookGetAuthorizationStatus())
{
// Update our UI if the user has granted access to their Contacts
case kABAuthorizationStatusAuthorized:
[self showAddressBook];
break;
// Prompt the user for access to Contacts if there is no definitive answer
case kABAuthorizationStatusNotDetermined :
[self requestAddressBookAccess];
break;
// Display a message if the user has denied or restricted access to Contacts
case kABAuthorizationStatusDenied:
case kABAuthorizationStatusRestricted:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"You have not granted access to your contacts or you have revoked it earlier."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
break;
default:
break;
}
}
// Prompt the user for access to their Address Book data
-(void)requestAddressBookAccess
{
TF_Message_View_Controller * __weak weakSelf = self;
ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf showAddressBook];
});
}
});
}
If you wanted to show the alert asking for permission, you can use the following code
This code works with Contacts framework, which is available only for iOS 9.0+
- (void)checkContactStoreAccess {
self.contactsStrore = [[CNContactStore alloc] init];
switch ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]) {
case CNAuthorizationStatusAuthorized: {
[self fetchContactsFromContactStore];
break;
}
case CNAuthorizationStatusNotDetermined:{
[self requestContactsAccess];
break;
}
case CNAuthorizationStatusDenied:
case CNAuthorizationStatusRestricted:{
//show info that access denied for contact and redirect user to settings with
[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
}
}
- (void)requestContactsAccess {
typeof(self) __weak weakSelf = self;
[self.contactsStrore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
[weakSelf fetchContactsFromContactStore];
}];
}
Related
I'm using Cloud Kit in my app which makes use of the camera and only allows the user to submit photos if they're logged into iCloud. So when the user clicks the camera button, I call a CloudKit method to get the user's icloud status, which returns a CKAccountStatus value (0-3). I implemented this initially in the view controller and it worked perfectly. Then I did some refactoring and created a CKManager class to house all CK related methods. So now when the camera is clicked instead of calling the CK method off the container directly in the VC, I'm calling it via a method from my CKManager property (which is lazy instantiated). It should only return values 0-3, but it keeps returning 448 for some reason. However, in the CKManager logging, I can see it logging correctly that I'm logged into iCloud. So there's an issue of it translating from there back to the VC. I have feeling this is a threading/callback issue, which I'm not that well versed in.
Can someone take a look at the code and see if there's something obvious I'm doing wrong? Thanks in advance!
- (IBAction)cameraBarButtonPressed:(UIBarButtonItem *)sender {
NSLog(#"Entered cameraBarButtonPressed");
//CKContainer *container = [CKContainer defaultContainer];
dispatch_queue_t fetchQ = dispatch_queue_create("check user status", NULL);
__block CKAccountStatus userAccountStatus;
dispatch_async(fetchQ, ^{ // check user's CK status on different thread
userAccountStatus = [self.ckManager getUsersCKStatus];
NSLog(#"cameraBarButtonPressed userAccountStatus: %ld", userAccountStatus);
if (userAccountStatus == CKAccountStatusAvailable) {
//NSLog(#"User is logged into CK - user can upload pics!");
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.delegate = self; // set the deleage for the ImagePickerController
// check to see if the camera is available as source type, else check for photo album
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
} else if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
cameraUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
[cameraUI setAllowsEditing:YES]; // let the user edit the photo
// set the camera presentation style
//cameraUI.modalPresentationStyle = UIModalPresentationFullScreen;
cameraUI.modalPresentationStyle = UIModalPresentationCurrentContext;
dispatch_async(dispatch_get_main_queue(), ^{ // show the camera on main thread to avoid latency
[self presentViewController:cameraUI animated:YES completion:nil]; // show the camera with animation
});
} else if (userAccountStatus == CKAccountStatusNoAccount) {
//NSLog(#"User is not logged into CK - Camera not available!");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Not Available" message:#"You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
} else if (userAccountStatus == CKAccountStatusRestricted) {
NSLog(#"User CK account is RESTRICTED !");
} else if (userAccountStatus == CKAccountStatusCouldNotDetermine) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Status Undetermined" message:#"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
} else { // did not get back one of the above values so show the Could Not Determine message
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Status Undetermined" message:#"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
}
});
}
The above code is the code that does not work. Here is the code that does work. Just copying the beginning code as the rest is redundant from that point on...
CKContainer *container = [CKContainer defaultContainer];
dispatch_async(fetchQ, ^{ // check user's CK status on different thread
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (error) {...
Lastly, here is the code that gets called from CKManager for the code that does not work...
- (CKAccountStatus)getUsersCKStatus {
NSLog(#"Entered getUsersCKStatus...");
__block CKAccountStatus userAccountStatus;
[self.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (error) {
NSLog(#"Error: Error encountered while getting user CloudKit status: %#", error.localizedDescription);
} else {
if (accountStatus == CKAccountStatusAvailable) {
NSLog(#"Info: User is logged into CK - camera is available!");
userAccountStatus = CKAccountStatusAvailable;
} else if (accountStatus == CKAccountStatusNoAccount) {
NSLog(#"Info: User is not logged into CK - Camera not available!");
userAccountStatus = CKAccountStatusNoAccount;
} else if (accountStatus == CKAccountStatusRestricted) {
NSLog(#"Info: User CK account is RESTRICTED - what does that mean!?");
userAccountStatus = CKAccountStatusRestricted;
} else if (accountStatus == CKAccountStatusCouldNotDetermine) {
NSLog(#"Error: Could not determine user CK Account Status: %#", error.localizedDescription);
userAccountStatus = CKAccountStatusCouldNotDetermine;
}
}
}];
NSLog(#"CKAccountStatus: %ld", userAccountStatus);
return userAccountStatus;
}
In the getUsersCKStatus you are calling the accountStatusWithCompletionHandler. That is an asynchronous method. In your case it will return the userAccountStatus before it is set by its callback method.
You could solve this by making that method synchronous by implementing a semaphore. A better way would be passing on a callback block to that method and not returning a value.
I have special requirement of client that he want to be able to login to Facebook from within the app itself, i mean he don't want app to open an external browser where user enters his details, he just want to go with native iOS view having username/mobile and password field.
I have already tried googling but could not find any solution so just want to know is it possible to do and if yes then how?
Thanks in advance.
Regards,
Mahesh.
Use following method :
-(void)openFacebookAuthentication
{
NSArray *permission = [NSArray arrayWithObjects:kFBEmailPermission,kFBUserPhotosPermission, nil];
FBSession *session = [[FBSession alloc] initWithPermissions:permission];
[FBSession setActiveSession: [[FBSession alloc] initWithPermissions:permission] ];
[[FBSession activeSession] openWithBehavior:FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
switch (status) {
case FBSessionStateOpen:
[self getMyData];
break;
case FBSessionStateClosedLoginFailed: {
// prefer to keep decls near to their use
// unpack the error code and reason in order to compute cancel bool
NSString *errorCode = [[error userInfo] objectForKey:FBErrorLoginFailedOriginalErrorCode];
NSString *errorReason = [[error userInfo] objectForKey:FBErrorLoginFailedReason];
BOOL userDidCancel = !errorCode && (!errorReason || [errorReason isEqualToString:FBErrorLoginFailedReasonInlineCancelledValue]);
if(error.code == 2 && ![errorReason isEqualToString:kFBSdkUserLoginFail]) {
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:kFBAlertTitle
message:kFBAuthenticationErrorMessage
delegate:nil
cancelButtonTitle:kOk
otherButtonTitles:nil];
[errorMessage performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
errorMessage = nil;
}
}
break;
// presently extension, log-out and invalidation are being implemented in the Facebook class
default:
break; // so we do nothing in response to those state transitions
}
}];
permission = nil;
}
Here is my Post : Login with facebook in iPhone without redirecting to the web browser?
I am using below Code, My application don't ask permission on iOS 6 while on iOS 7 and above version it ask for Contact permission access. On iOS 6 it doesn't show app in privacy setting as well. I have read some other thread but not found any solutions.
App crashed in iOS 6 when user changes Contacts access permissions
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"6.0")) {
__block CDNDeviceContact *controller = self;
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef,
^(bool granted, CFErrorRef error) {
if (granted)
[controller loadContacts];
else [controller doAlertForContact];
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self loadContacts];
} else {
[controller doAlertForContact];
}
if (addressBookRef) CFRelease(addressBookRef);
}
If the user has previously been presented with the request to get permission, it will not show again. According to the documentation,
The user is only asked for permission the first time you request access. Later calls use the permission granted by the user.
If testing in the simulator, I recommend that you go to iOS Simulator -> Reset Content and Settings so that you are able to simulate the event.
You don't need SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO and you also don't need to create an ABAddressBookRef.
For me, this is works like a charm:
if (ABAddressBookGetAuthorizationStatus) {
switch (ABAddressBookGetAuthorizationStatus()) {
case kABAuthorizationStatusNotDetermined:{
ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error) {
self.addContactButton.enabled = granted;
if (granted) {
// granted
} else {
// User denied access
}});
} break;
case kABAuthorizationStatusDenied: break;
case kABAuthorizationStatusAuthorized: break;
default: break;
}
}
Recently I was assigned to survey how to use iOS framework ACAccount and Social to implement facebook post function. It is quite simple to gain access of the account configured in setting.
if(!_accountStore)
_accountStore = [[ACAccountStore alloc] init];
ACAccountType *facebookTypeAccount = [_accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Check if there is any faceboook account
NSArray *accounts = [_accountStore accountsWithAccountType:facebookTypeAccount];
if (![accounts count]) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"alert" message:#"No facebook account configured in setting." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
[_accountStore requestAccessToAccountsWithType:facebookTypeAccount
options:#{ACFacebookAppIdKey: #"FACEBOOK-APP-ID", ACFacebookPermissionsKey: #[#"email"]}
completion:^(BOOL granted, NSError *error) {
if(granted){
NSArray *accounts = [_accountStore accountsWithAccountType:facebookTypeAccount];
_facebookAccount = [accounts lastObject];
NSLog(#"Success");
}else{
// ouch
NSLog(#"Failed, Error: %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
NSString *message = [NSString stringWithFormat:#"App access denied, please grant access in Setting->Facebook. Error message::%#", error];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
});
}
}];
Once the app gain access to facebook, it can post message by using SLComposeViewController:
- (IBAction)postButtonPressed:(id)sender {
if (!_facebookAccount) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"alert" message:#"login first" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
SLComposeViewController *fbController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
SLComposeViewControllerCompletionHandler __block completionHandler=^(SLComposeViewControllerResult result){
[fbController dismissViewControllerAnimated:YES completion:nil];
switch(result){
case SLComposeViewControllerResultCancelled:
default:
{
NSLog(#"Cancelled.....");
}
break;
case SLComposeViewControllerResultDone:
{
NSLog(#"Posted....");
}
break;
}};
[fbController setInitialText:#"Test message"];
[fbController setCompletionHandler:completionHandler];
[self presentViewController:fbController animated:YES completion:nil];
} else {
NSLog(#"no facebook setup");
}
Here comes my question. I found that there is a method in ACAccountStore which is used to renew expired credential of an ACAccount:
- (void)renewCredentialsForAccount:(ACAccount *)account completion:(ACAccountStoreCredentialRenewalHandler)completionHandler;
But I don't even know how to check whether the credential is expired so that I can renew it. Anyone got an idea about this?
Oh, by the way, we just want to use the native Social framework to do simple work such as post some message or picture. So, if not needed, we are not going to use the facebook SDK.
If you know anything about how to check the credential is valid or not, please leave a comment or submit an answer, thank you:)
Updates 2013.11.20 11:10 AM
I learn something from my experiments to this issue..
One is not able to get certain type of accounts from account store before he gains access to them, so I should not check account count before request for access.
Renew notification called when the app using ACAccount is in background after facebook account changed. Currently, I only saw changes of access right triggers the notification.
If the user changes password, the system will pop out an alert when the user attempt to post something, which ask the user to change password.
I think monitor notifications of account change is enough to handle the changes. I'll accept the first answer.
You should renew the user acc everytime it is out of sync. This may happen if the user has changed his password or when the acc session has expired.
Yo can know you are in that scenario using the following notification:
ACAccountStoreDidChangeNotification
I don't think there is active way , best is to write renew function and call
renewCredentialsForAccount of the framework
Here is a simple scenario, I am using FBLoginView which show a login button and on click application go for fast switching but i want to check a bool value of terms and conditions before app go for fast switch. There is not way i am able to find to achieve this scenario.
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
// Upon login, transition to the main UI by pushing it onto the navigation stack.
LGAppDelegate *appDelegate = (LGAppDelegate *)[UIApplication sharedApplication].delegate;
[self.navigationController pushViewController:((UIViewController *)appDelegate.mainViewController) animated:YES];
}
- (void)acceptTermsAndConditions {
if (!_checkboxButton.checked) {
NSString *alertMessage, *alertTitle;
alertTitle = #"Terms to Use";
alertMessage = #"Please accept the terms & condition to use this application.";
UIAlertView* av = [[UIAlertView alloc] initWithTitle:alertTitle
message:alertMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
[av release];
}
}
- (void)loginView:(FBLoginView *)loginView
handleError:(NSError *)error{
NSString *alertMessage, *alertTitle;
// Facebook SDK * error handling *
// Error handling is an important part of providing a good user experience.
// Since this sample uses the FBLoginView, this delegate will respond to
// login failures, or other failures that have closed the session (such
// as a token becoming invalid). Please see the [- postOpenGraphAction:]
// and [- requestPermissionAndPost] on `SCViewController` for further
// error handling on other operations.
if (error.fberrorShouldNotifyUser) {
// If the SDK has a message for the user, surface it. This conveniently
// handles cases like password change or iOS6 app slider state.
alertTitle = #"Something Went Wrong";
alertMessage = error.fberrorUserMessage;
} else if (error.fberrorCategory == FBErrorCategoryAuthenticationReopenSession) {
// It is important to handle session closures as mentioned. You can inspect
// the error for more context but this sample generically notifies the user.
alertTitle = #"Session Error";
alertMessage = #"Your current session is no longer valid. Please log in again.";
} else if (error.fberrorCategory == FBErrorCategoryUserCancelled) {
// The user has cancelled a login. You can inspect the error
// for more context. For this sample, we will simply ignore it.
NSLog(#"user cancelled login");
} else {
// For simplicity, this sample treats other errors blindly, but you should
// refer to https://developers.facebook.com/docs/technical-guides/iossdk/errors/ for more information.
alertTitle = #"Unknown Error";
alertMessage = #"Error. Please try again later.";
NSLog(#"Unexpected error:%#", error);
}
if (alertMessage) {
[[[UIAlertView alloc] initWithTitle:alertTitle
message:alertMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
}
- (void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView {
// Facebook SDK * login flow *
// It is important to always handle session closure because it can happen
// externally; for example, if the current session's access token becomes
// invalid. For this sample, we simply pop back to the landing page.
LGAppDelegate *appDelegate = (LGAppDelegate *)[UIApplication sharedApplication].delegate;
if (appDelegate.isNavigating) {
// The delay is for the edge case where a session is immediately closed after
// logging in and our navigation controller is still animating a push.
[self performSelector:#selector(logOut) withObject:nil afterDelay:.5];
} else {
[self logOut];
}
}
- (void)logOut {
[self.navigationController popToRootViewControllerAnimated:YES];
}