I am using Cognito user pool to authenticate users in my system. A successful authentication gives an ID Token (JWT), Access Token (JWT).Every one hour the TokenExpiration . My question is once my Access Token expires, how do I use the stored refresh token to refresh my access token again?This is my code.
- (void)loginAWSMethod {
NSString *emailId = #"the email";
NSString *pwdTxt = #"the password";
NSLog(#"entered the login method %# %#",emailId,pwdTxt);
AWSCognitoIdentityUser *user = [pool getUser:emailId];
[[user getSession:emailId password:pwdTxt validationData:nil]
continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task)
{
if (task.error) {
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"ERROR CATCHED++++++");
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:#"Incorrect email or password."
message:#""
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yesButton = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)
{
}];
[alert addAction:yesButton];
[self presentViewController:alert animated:YES completion:nil];
});
[self removeWaitingProgress];
});
}else{
NSLog(#"the result is %#",task.result);
AWSCognitoIdentityUserSession *response1 = task.result;
token = response1.accessToken.tokenString;
NSLog(#"the token is %#",token);
[[user getDetails] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserGetDetailsResponse *> * _Nonnull task) {
AWSCognitoIdentityUserGetDetailsResponse *response = task.result;
for (AWSCognitoIdentityUserAttributeType *attribute in response.userAttributes) {
//print the user attributes
NSLog(#"Attribute: %# Value: %#", attribute.name, attribute.value);
if([attribute.name isEqualToString:#"sub"]){
cognitoID = attribute.value;
}
[defaults setValue:token forKey:#"token"];
[defaults setValue:#"yes" forKey:#"isLoggedIn"];
[defaults synchronize];
dispatch_async(dispatch_get_main_queue(), ^{
[self removeWaitingProgress];
[self gotoDashborad];
});
}
return nil;
}];
}
return nil;
}];
}
You should be able to simply invoke -[AWSCognitoIdentityUser getSession], which behind the scenes will either return the currently valid access token, or exchange the refresh token for new access token:
-(nullable NSString *)accessTokenStringForCurrentUser {
AWSCognitoIdentityUser *currentUser = [pool currentUser];
__block NSString *tokenString;
// `getSession` automatically exchanges the refresh token for a valid access token if needed
[[[currentUser getSession] continueWithBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
// (Error handling not shown)
if (task.result) {
AWSCognitoIdentityUserSessionToken *accessToken = task.result.accessToken;
tokenString = accessToken.tokenString;
}
return nil;
}] waitUntilFinished];
return tokenString;
}
You may also wish to look at the Cognito UserPools Sample app which has Objective C samples of using UserPools.
Related
In my app I want to integrate Azure loging and get the email id and send to my server. I have done this so far and from this I can get the access token.
- (void)acquireTokenInteractive:(id)sender
{
ADTestAppSettings* settings = [ADTestAppSettings settings];
NSString* authority = [settings authority];
NSString* resource = [settings resource];
NSString* clientId = [settings clientId];
NSURL* redirectUri = [settings redirectUri];
ADUserIdentifier* identifier = [self identifier];
ADCredentialsType credType = [self credType];
BOOL validateAuthority = _validateAuthority.selectedSegmentIndex == 0;
ADAuthenticationError* error = nil;
ADAuthenticationContext* context = [[ADAuthenticationContext alloc] initWithAuthority:authority
validateAuthority:validateAuthority
error:&error];
if (!context)
{
NSString* resultText = [NSString stringWithFormat:#"Failed to create AuthenticationContext:\n%#", error];
[_resultView setText:resultText];
return;
}
[context setCredentialsType:credType];
if ([self embeddedWebView])
{
[context setWebView:_webView];
//[_authView setFrame:self.view.frame];
[UIView animateWithDuration:0.5 animations:^{
[_acquireSettingsView setHidden:YES];
[_authView setHidden:NO];
}];
}
__block BOOL fBlockHit = NO;
[context acquireTokenWithResource:resource
clientId:clientId
redirectUri:redirectUri
promptBehavior:[self promptBehavior]
userIdentifier:identifier
extraQueryParameters:nil
completionBlock:^(ADAuthenticationResult *result)
{
if (fBlockHit)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Error!"
message:#"Completion block was hit multiple times!"
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:YES completion:nil];
});
return;
}
fBlockHit = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[self updateResultView:result];
[_webView loadHTMLString:#"<html><head></head><body>done!</body></html>" baseURL:nil];
[_authView setHidden:YES];
[self.view setNeedsDisplay];
[[NSNotificationCenter defaultCenter] postNotificationName:ADTestAppCacheChangeNotification object:self];
});
}];
}
But how can I retrieve the email id of logged user.
Please help me.
Thanks
What is the resource you want to access via the access token ? If resource is https://graph.windows.net/,you could use Azure AD Graph API to get the email information of the signed-in user :
GET https://graph.windows.net/me?api-version=1.6
Authorization: Bearer yourAccessToken
If resource is https://graph.microsoft.com/,you could use Microsoft Graph API to get the email information of the signed-in user:
Get https://graph.microsoft.com/v1.0/me
Authorization: Bearer yourAccessToken
In response you could check UserPrincipalName claim value which is an email address that can receive emails
I am using The Following code for getting iPhone Contacts but my App is not getting Permission For Allow Contacts in iOS 9 . I have found this code from stack and the other references are also same .
- (void)getPersonOutOfAddressBook
{
//1
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (addressBook != nil) {
NSLog(#"Succesful.");
//2
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
//3
NSUInteger i = 0; for (i = 0; i < [allContacts count]; i++)
{
NSMutableDictionary *persiondict =[[NSMutableDictionary alloc]init] ;
// Person *person = [[Person alloc] init];
ABRecordRef contactPerson = (__bridge ABRecordRef)allContacts[i];
//4
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson,
kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonLastNameProperty);
NSString *fullName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
// person.firstName = firstName;
// person.lastName = lastName;
// person.fullName = fullName;
[persiondict setValue:fullName forKey:#"fullName"] ;
//email
//5
ABMultiValueRef emails = ABRecordCopyValue(contactPerson, kABPersonEmailProperty);
//6
NSUInteger j = 0;
for (j = 0; j < ABMultiValueGetCount(emails); j++) {
NSString *email = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(emails, j);
if (j == 0) {
// person.homeEmail = email;
[persiondict setValue:email forKey:#"email"] ;
// NSLog(#"person.homeEmail = %# ", person.homeEmail);
}
else if (j==1)
[persiondict setValue:email forKey:#"email"] ;
}
//7
[ArrUserOfContacts addObject:persiondict];
}
//8
CFRelease(addressBook);
} else {
//9
NSLog(#"Error reading Address Book");
}
}
I am unable to find problem here , How can user get an permission for access contacts . Any Suggestion Will be helpfull .
ABAddressBookRequestAccessWithCompletion is deprecated in iOS 9. Now you should use Contacts framework. This is an example in Objective C:
CNContactStore * contactStore = [CNContactStore new];
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted){
//
}
}];
In Swift 3:
CNContactStore().requestAccess(for: .contacts, completionHandler: { granted, error in
if (granted){
//
}
})
This will only ask for permission if the user hasn't denied or approved permissions for contacts in your app. You can't ask for permissions that have already been denied by the user (At least now in iOS 10), what you can do is redirect the user to Settings.
You need request permissions using ABAddressBookRequestAccessWithCompletion()
ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
if (!granted){
NSLog(#"Just denied");
return;
}
NSLog(#"Just authorized");
});
If you want to check user given contacts permission or not and, if permission is not given then show alert to move user in settings to give permission.
Then use the following function checkContactsPermission as:
-(void)checkContactsPermission {
//Check permission status
switch (ABAddressBookGetAuthorizationStatus()) {
case kABAuthorizationStatusAuthorized:
// Already permission given
break;
case kABAuthorizationStatusDenied:{
// Permission not given so move user in settings page to app.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert!" message:#"his app requires access to your contacts." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* SettingsButton = [UIAlertAction actionWithTitle:#"Settings"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
NSURL * settingsURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:#"%#%#",UIApplicationOpenSettingsURLString,[[NSBundle mainBundle]bundleIdentifier]]];
if (settingsURL) {
[[UIApplication sharedApplication] openURL:settingsURL];
}
}];
UIAlertAction* DeniedButton = [UIAlertAction actionWithTitle:#"Denied"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
}];
[alert addAction:SettingsButton];
[alert addAction:DeniedButton];
[self presentViewController:alert animated:YES completion:nil];
}
case kABAuthorizationStatusRestricted: {
// Permission not given so move user in settings page to app.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert!" message:#"his app requires access to your contacts." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* SettingsButton = [UIAlertAction actionWithTitle:#"Settings"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
NSURL * settingsURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:#"%#%#",UIApplicationOpenSettingsURLString,[[NSBundle mainBundle]bundleIdentifier]]];
if (settingsURL) {
[[UIApplication sharedApplication] openURL:settingsURL];
}
}];
UIAlertAction* DeniedButton = [UIAlertAction actionWithTitle:#"Denied"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
}];
[alert addAction:SettingsButton];
[alert addAction:DeniedButton];
[self presentViewController:alert animated:YES completion:nil];
}
break;
case kABAuthorizationStatusNotDetermined:
// Permission not determin. so request for permission.
ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
if (granted){
// Already permission given
}
});
break;
}
}
for iOS 10 you can use Contacts framework for check permission.
I found many tutorials online how to share image on twitter from an iOS app. But i want to know 2 things about social sharing with twitter-
If i post an image on twitter via my app, Can i get image id from twitter in callback method/block? If yes then how?
If i fetch favourites of a user, is the response include text posted with that image? I checked for the same on twitter Rest API doc that there is a text property returned in the response.
Now my question is that if i post some text with image via my iOS app and later make this post favourite in twitter app and now i get my favourites list through twitter rest API in my app, does the text property in the response is same that i posted with my post?
Edit about #1 above:- from SLComposeViewControllerResult docs i found that completion handler return one of
typedef NS_ENUM (NSInteger,
SLComposeViewControllerResult ) {
SLComposeViewControllerResultCancelled,
SLComposeViewControllerResultDone
};
constant so there is no info about image just posted. Am i right? If not please give me some reference about how to get image id please.
Here I have customize alertView,NSLog,etc. You ignore that.
Here is the code to share to twitter by using STTwitter library
- (void)shareToTwitter
{
APP_DELEGATE.navController = self.navigationController;
NSString *strTwitterToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"TwitterToken"];
NSString *strTwitterTokenSecret = [[NSUserDefaults standardUserDefaults] objectForKey:#"TwitterTokenSecret"];
if (strTwitterToken && strTwitterTokenSecret)
{
self.twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:TwitterConsumerKey consumerSecret:TwitterSecretKey oauthToken:strTwitterToken oauthTokenSecret:strTwitterTokenSecret];
[self.twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
DLogs(#"Twitter User Name");
[self twitterMediaUpload];
} errorBlock:^(NSError *error) {
DLogs(#"-- error: %#", error);
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
[self safariLoginTwitter];
}];
}
else
{
[self safariLoginTwitter];
}
}
-(void)safariLoginTwitter
{
// [APP_CONSTANT getNativeTwitterAccountAccessToken:^(id result) {
//
// }];
self.twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:TwitterConsumerKey
consumerSecret:TwitterSecretKey];
[self.twitter postTokenRequest:^(NSURL *url, NSString *oauthToken) {
DLogs(#"-- url: %#", url);
DLogs(#"-- oauthToken: %#", oauthToken);
[[UIApplication sharedApplication] openURL:url];
} authenticateInsteadOfAuthorize:NO
forceLogin:#(YES)
screenName:nil
oauthCallback:#"myapp://twitter_access_tokens/"
errorBlock:^(NSError *error) {
DLogs(#"-- error: %#", error);
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
}];
}
- (void)setOAuthToken:(NSString *)token oauthVerifier:(NSString *)verifier {
[self.twitter postAccessTokenRequestWithPIN:verifier successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
DLogs(#"-- screenName: %#", screenName);
/*
At this point, the user can use the API and you can read his access tokens with:
_twitter.oauthAccessToken;
_twitter.oauthAccessTokenSecret;
You can store these tokens (in user default, or in keychain) so that the user doesn't need to authenticate again on next launches.
Next time, just instanciate STTwitter with the class method:
+[STTwitterAPI twitterAPIWithOAuthConsumerKey:consumerSecret:oauthToken:oauthTokenSecret:]
Don't forget to call the -[STTwitter verifyCredentialsWithSuccessBlock:errorBlock:] after that.
*/
[[NSUserDefaults standardUserDefaults] setObject:self.twitter.oauthAccessToken forKey:#"TwitterToken"];
[[NSUserDefaults standardUserDefaults] setObject:self.twitter.oauthAccessToken forKey:#"TwitterTokenSecret"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self twitterMediaUpload];
} errorBlock:^(NSError *error) {
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
DLogs(#"-- %#", [error localizedDescription]);
}];
}
-(void)twitterMediaUpload
{
// ProfileImageBO *objProfImg = nil;
//
// if ([self.objProfile.arrUserImages count]) {
// objProfImg = [self.objProfile.arrUserImages objectAtIndex:0];
// }
[APP_CONSTANT showLoaderWithTitle:#"posting" onView:self.view];
// NSURL *urlProfImg = [NSURL URLWithString:[objProfImg.imageUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURL *screenshotUrl = [self getScreenshotUrl];
[self.twitter postMediaUpload:screenshotUrl uploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
DLogs(#"uploading");
} successBlock:^(NSDictionary *imageDictionary, NSString *mediaID, NSString *size) {
DLogs(#"imageDictionary = %#, mediaID = %#, size %#",imageDictionary.description,mediaID,size);
[self postToTheTwitterWithMediaId:mediaID];
} errorBlock:^(NSError *error) {
DLogs(#"Error in uploading media, try again ...");
[APP_CONSTANT hideLoader];
[AppConstant showAutoDismissAlertWithMessage:error.localizedDescription onView:self.view];
}];
}
-(void)postToTheTwitterWithMediaId:(NSString *)mediaID
{
NSString *msg = [NSString stringWithFormat:#"Check out My Profile"];
[self.twitter postStatusUpdate:msg inReplyToStatusID:nil mediaIDs:[NSArray arrayWithObject:mediaID] latitude:nil longitude:nil placeID:nil displayCoordinates:nil trimUser:nil successBlock:^(NSDictionary *status) {
DLogs(#"Description %#",status.description);
[self showNotificationToastWithMessage:TwitterPostSuccess];
[APP_CONSTANT hideLoader];
} errorBlock:^(NSError *error) {
DLogs(#"Twitter posting error %#",error.description);
[APP_CONSTANT hideLoader];
[AppConstant showAutoDismissAlertWithMessage:error.localizedDescription onView:self.view];
}];
}
For your second question: Yes, you will get the same text in the response
And This is the code to get favorite list
-(void)getFavListTwitter
{
[self.twitter getFavoritesListWithSuccessBlock:^(NSArray *statuses) {
DLogs(#"%#",statuses.description);
} errorBlock:^(NSError *error) {
DLogs(#"%#",error.description);
}];
}
So I am using Parse to link a user with their twitter account. In the app delegate I have the following:
[PFTwitterUtils initializeWithConsumerKey:CONSUMER_KEY consumerSecret:CONSUMER_SECRET];
Then the button which the user clicks to link the user to facebook calls the following:
-(IBAction)twitterConnectPressed{
NSLog(#"twitter");
[PFTwitterUtils linkUser:[PFUser currentUser] block:^(BOOL succeeded, NSError* error){
NSLog(#"haha");
if(succeeded){
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Done!" message:#"Connected with Twitter!" delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
self.fbButton.backgroundColor = [TGAPublic grey];
}else{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Oops" message:error.userInfo[#"error"] delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
}
}];
}
However even though linkUser:block: is called it doesn't do anything at all. It doesn't create a pop up to log in to twitter like [PFFacebookUtils linkUser:] does and therefore doesn't end up calling the block either
PFTwitterUtils does not appear to handle all cases on iOS. In particular, if you do not have an account setup (Settings->Twitter) it does not fire up a web view and attempt to used web oauth. Conversely if you have multiple Twitter accounts configured (again in Settings) then it doesn't appear to fire up an action sheet to allow you to select which account you'd like to link.
There's a great tutorial on how to do these things which exposes an extension to PFFacebookUtils here: http://natashatherobot.com/ios-twitter-login-parse/
It does not do linking though, just login, but should be a good basis to add linking.
I've got similar problem with link/unlink methods for both PFFacebookUtils and PFTwitterUtils (v. 1.7.4).
The only way I managed to make it work was to replace them by, unfortunately, messing with internal Parse implementation of authData:
#import "TwitterAuthProvider.h"
#import "PFTwitterUtils.h"
#import "PFUser.h"
static NSString * const kTwitterKey = #"XXX";
static NSString * const kTwitterSecret = #"XXX";
#implementation TwitterAuthProvider
- (instancetype)init {
if ((self = [super init])) {
[PFTwitterUtils initializeWithConsumerKey:kTwitterKey consumerSecret:kTwitterSecret];
}
return self;
}
- (void)setAuthData:(id)twAuthData forUser:(PFUser *)user {
static NSString * const kParseAuthDataKey = #"authData";
static NSString * const kParseLinkedServiceNamesKey = #"linkedServiceNames";
static NSString * const kParseAuthProviderName = #"twitter";
NSMutableDictionary *authData = [[user valueForKey:kParseAuthDataKey] mutableCopy] ?: [NSMutableDictionary dictionary];
authData[kParseAuthProviderName] = twAuthData ?: [NSNull null];
[user setObject:authData forKey:kParseAuthDataKey];
[user setValue:authData forKey:kParseAuthDataKey];
NSMutableSet *linkedServices = [[user valueForKey:kParseLinkedServiceNamesKey] mutableCopy] ?: [NSMutableSet set];
if (twAuthData) {
[linkedServices addObject:kParseAuthProviderName];
} else {
[linkedServices removeObject:kParseAuthProviderName];
}
[user setValue:linkedServices forKey:kParseLinkedServiceNamesKey];
}
- (void)linkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
__weak typeof(self) weakSelf = self;
PF_Twitter *twitter = [PFTwitterUtils twitter];
[twitter authorizeWithSuccess:^(void) {
[weakSelf setAuthData:[self twitterAuthData] forUser:user];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
//revert
[weakSelf setAuthData:nil forUser:user];
}
completion(succeeded, error);
}];
} failure:^(NSError *error) {
completion(NO, error);
} cancel:^(void) {
completion(NO, nil);
}];
}
- (void)unlinkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
[self setAuthData:nil forUser:user];
[user saveInBackgroundWithBlock:completion];
}
- (NSDictionary *)twitterAuthData {
PF_Twitter *twitter = [PFTwitterUtils twitter];
return #{
#"auth_token" : twitter.authToken,
#"auth_token_secret": twitter.authTokenSecret,
#"consumer_key": kTwitterKey,
#"consumer_secret": kTwitterSecret,
#"id": twitter.userId,
#"screen_name": twitter.screenName,
};
}
#end
I've been trying to get a value from inside a block for a few hours now, I can't understand how to use the handlers on completion and literally everything.
Here's my code:
+ (void)downloadUserID:(void(^)(NSString *result))handler {
//Now redirect to assignments page
__block NSMutableString *returnString = [[NSMutableString alloc] init]; //'__block' so that it has a direct connection to both scopes, in the method AND in the block
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[requestHome setHTTPMethod:#"GET"]; // this looks like GET request, not POST
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError) {
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
//NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
returnString = (#"Response was not JSON (from home), it was = %#", [[NSMutableString alloc] initWithData:homeData encoding:NSUTF8StringEncoding]);
//NSLog(returnString);
}
}
else {
//NSLog(#"error: %#", homeError);
}
}];
//NSLog(#"myResult: %#", [[NSString alloc] initWithData:myResult encoding:NSUTF8StringEncoding]);
handler(returnString);
}
- (void)getUserID {
[TClient downloadUserID:^(NSString *getIt){
NSLog([NSString stringWithFormat:#"From get userID %#", getIt]);
}];
}
So I'm trying to NSLog the returnString from the downloadUserID method.
I first tried returning, then I realized you can't do a return from inside a block. So now I've been trying to do it with the :(void(^)(NSString *result))handler to try and access it from another class method.
So I'm calling downloadUserID from the getUserID method, and trying to log the returnString string. It just keeps going to nil. It just prints From get userID and nothing else.
How do I access the returnString that's inside the block of the downloadUserID method?
The problem is not the block itself, the problem is realizing that the block is executed asynchronously.
In your code, at the time you call handler(returnString); the block is probably still executing on another thread, so there's no way you can catch the value at this point.
Probably what you want to do is move that line inside the block (probably at the end, before the closing braces).
You can do this if you write such a wrapper.
In this situation, you need a while loop that will wait for a response from the block.
Method which shoud return value of enum
- (RXCM_TroubleTypes) logic_getEnumValueOfCurrentCacheProblem
{
RXCM_TroubleTypes result = RXCM_HaveNotTrouble;
NetworkStatus statusConnection = [self network_typeOfInternetConnection];
RXCM_TypesOfInternetConnection convertedNetStatus = [RXCM convertNetworkStatusTo_TypeOfInternetConnection:statusConnection];
BOOL isAllowed = [self someMethodWith:convertedNetStatus];
if (isAllowed){
return RXCM_HaveNotTrouble;
}else {
return RXCM_Trouble_NotSuitableTypeOfInternetConnection;
}
return result;
}
Method which calls delegate's method with block.
And waits answer from it.
Here I use while loop. Just check every 0.5sec answer from block
- (BOOL) isUserPermissioned:(RXCM_TypesOfInternetConnection)newType
{
__block BOOL isReceivedValueFromBlock = NO;
__block BOOL result = NO;
__block BOOL isCalledDelegateMethod = NO;
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_sync(aQueue,^{
while (!isReceivedValueFromBlock) {
NSLog(#"While");
if (!isCalledDelegateMethod){
[self.delegate rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:newType
completion:^(BOOL isContinueWorkOnNewTypeOfConnection) {
result = isContinueWorkOnNewTypeOfConnection;
isReceivedValueFromBlock = YES;
}];
isCalledDelegateMethod = YES;
}
[NSThread sleepForTimeInterval:0.5];
}
});
return result;
}
Delegate's method in ViewController
- (void) rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:(RXCM_TypesOfInternetConnection)newType
completion:(void(^)(BOOL isContinueWorkOnNewTypeOfConnection))completion
{
__weak ViewController* weak = self;
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert"
message:#"to continue download on the new type of connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completion(YES);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"NO" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completion(NO);
}];
[alert addAction:cancel];
[alert addAction:ok];
[weak presentViewController:alert animated:YES completion:nil];
});
}