So I'm trying to log-in/signup users using ACAccountStore. This happens using a view controller that presented modally. It works just fine that way, however, when I dismiss the view controller, the underlying/presenting view controller is still a black window. I assume this happens, because I do not wait for the completion block to finish.
So my question: How do I wait for the completion block to finish before calling [self dismissViewControllerAnimated:YES completion:nil];?
-(void)loginWithTwitter{
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:
ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType options:nil
completion:^(BOOL granted, NSError *error)
{
if (granted) {
//do something -> call function to handle the data and dismiss the modal controller.
}
else{
//fail and put our error message.
}
}];
}
Completion block is that thing that will be executed after the main process (access to accounts request in this case) is finished. So you can put [self dismissViewControllerAnimated:YES completion:nil] in it.
Another thing: it is bad to have reference to self in block because of retain cycles. You would modify your code to look like this:
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:
ACAccountTypeIdentifierTwitter];
__weak UIViewController *weakSelf = self;
[account requestAccessToAccountsWithType:accountType options:nil
completion:^(BOOL granted, NSError *error) {
[weakSelf dismissViewControllerAnimated:YES completion:nil];
if (granted) {
//do something -> call function to handle the data and dismiss the modal controller.
}
else {
//fail and put our error message.
}
}];
Related
I have this code
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[self.imageView.image] applicationActivities:nil];
__weak typeof(self) weakSelf = self;
UIActivityViewControllerCompletionWithItemsHandler completionHandlerBlock = ^void(NSString * __nullable activityType, BOOL completed, NSArray * __nullable returnedItems, NSError * __nullable activityError) {
if (activityType == UIActivityTypeSaveToCameraRoll && completed) {
[EkoAlertService presentAlertOnViewController:weakSelf
withTitle:NSLocalizedString(#"save_photo_dialog_title", nil)
message:NSLocalizedString(#"save_photo_dialog_message", nil)
completion:nil];
}
};
[activityViewController setCompletionWithItemsHandler:completionHandlerBlock];
[self presentViewController:activityViewController animated:YES completion:nil];
It works fine in the case when user click allow permission to Save Image, but when they click don't allow permission I expect it to return some error but activityError is nil and I have no way to check whether you successfully save image to camera roll. Both cases return completed = YES
Is there a way to check this?
Thanks
I am using following code to display a toast after Facebook authentication
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) // check Fb is configured in Settings or not
{
accountStore = [[ACAccountStore alloc] init]; // you have to retain ACAccountStore
ACAccountType *fbAcc = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSString *key = #"xxxxx";
NSDictionary *dictFB = [NSDictionary dictionaryWithObjectsAndKeys:key,ACFacebookAppIdKey,#[#"email"],ACFacebookPermissionsKey, nil];
[accountStore requestAccessToAccountsWithType:fbAcc options:dictFB completion:^(BOOL granted, NSError *error) {
if (granted) {
NSLog(#"Perform fb registration");
} else {
NSLog(#"Facebook 1”);
[[Toast shared] showToast:self.view withText:#"You disabled your app from settings."];
NSLog(#"Facebook 2”);
}
}];
}
NSLog(#"Facebook 1”); and NSLog(#"Facebook 2”); are executing and printing logs respectively. However, toast statement in between these two logs delays and displays after 15-20 seconds.
If I put toast statement [[Toast shared] showToast:self.view withText:#"You disabled your app from settings."]; out of following completion handler:
[accountStore requestAccessToAccountsWithType:fbAcc options:dictFB completion:^(BOOL granted, NSError *error) {
}];
It works fine and displays toast timely, never delays. Any solution to remove the delay?
I believe what EDUsta said is correct. Try calling the toast message on the main thread. All UI changes should be handled on the main thread to avoid weird bugs. Try this:
[accountStore requestAccessToAccountsWithType:fbAcc options:dictFB completion:^(BOOL granted, NSError *error) {
if (granted) {
NSLog(#"Perform fb registration");
} else {
NSLog(#"Facebook 1”);
dispatch_async(dispatch_get_main_queue(), ^{
[[Toast shared] showToast:self.view withText:#"You disabled your app from settings."];
});
NSLog(#"Facebook 2”);
}
}];
I'm working on adding the LocalAuthentication framework to allow users to authenticate with a fingerprint on comparable devices. Everything works properly, but when a user is authenticated with their finger print it takes between 10-15 seconds for the proper segue to be performed, is this typical, or is there something wrong with the way I'm authenticating a user?
Code:
-(void)viewDidLoad { [super viewDidLoad]; [self.view setBackgroundColor:[NPSColor NPSBackgroundColor]];
UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:#"Register" style:UIBarButtonItemStylePlain target:self action:#selector(onRegisterTapped)]; self.navigationItem.rightBarButtonItem = anotherButton;
LAContext *context = [[LAContext alloc] init];
NSError *error = nil; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Login?" reply:BOOL success, NSError *error {
if (error) {
}
if (success) {
[self performSegueWithIdentifier:#"loginSeg" sender:self];
} else {
}
}];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Your device cannot authenticate using TouchID."
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
} }
So it may be that performing a segue has to do with a view action which should mainly be performed on the main thread. I'm not too sure but it could very well be the evaluatePolicy method is performed on a background thread. If this is the case, you would want to throw your performSegue on the main thread.
To do so, use this:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"loginSeg" sender:self];
});
I have next simple code, for downloading info from service.
UIViewController *nextController;
[request authWithBlock:^(NSDictionary *result, NSError *error)
{
// Result of auth request
if (!nextController) nextController = [UIViewController alloc] init];
[navigationController pushViewController: nextController];
}];
And I need show next view when request return success.
I want to know - this code are correct, or I must create and show viewcontroller in another place, not in block?
Thanks
Your code format is correct. But you can improve your code by below
__weak typeof(self) weakSelf = self;
[request authWithBlock:^(NSDictionary *result, NSError *error)
{
// Result of auth request
if (!error)
{
UIViewController *nextController = [UIViewController alloc] init];
// pass result to nexview controller if you want..
[weakSelf.navigationController pushViewController: nextController];
}
}
If I access Twitter, I would prefer to use the automatic alert box asking that the user go to settings, yet can't get it to work and provide my own default dialog, which I really don't want to do.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"indexPath.row %i", indexPath.row);
if (indexPath.row == 0) {
store = [[ACAccountStore alloc] init]; // Long-lived
twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[store requestAccessToAccountsWithType:twitterType withCompletionHandler:^(BOOL granted, NSError *error) {
if (granted) {
// Access has been granted, now we can access the accounts
twitterAccounts = [store accountsWithAccountType:twitterType];
if (twitterAccounts.count == 1) {
[self getTwitterData:[twitterAccounts objectAtIndex:0]];
} else {
[self selectTwitterAccount];
}
} else {
// Prefer NOT to do this ************************************
[self performSelectorOnMainThread:#selector(showTwitterError) withObject:nil waitUntilDone:NO];
}
}];
}
It is little tricky , i get by the removing the subviews in *TWTWeetComposeViewController*, so it shows only alert when user is not loged in and by the clicking on setting button , we can open the setting page in my app.
+ (void)setAlertForSettingPage :(id)delegate
{
// Set up the built-in twitter composition view controller.
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
// Create the completion handler block.
[tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
[delegate dismissModalViewControllerAnimated:YES];
}];
// Present the tweet composition view controller modally.
[delegate presentModalViewController:tweetViewController animated:YES];
//tweetViewController.view.hidden = YES;
for (UIView *view in tweetViewController.view.subviews){
[view removeFromSuperview];
}
}
Here, delegate is your viewcontroller, if you are using this method inside your viewcontroller just use self instead of delegate.
I had to face the same problem and found this sollution
To my knowledge this was only possible in iOS versions 5.0 - 5.0.1.
It was then depreciated again in iOS 5.1
So NO solution at the moment...
In case you would like to use the iOS 5 part:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs://"]];
And some more reading on the same topic