I want to call the signUp method first, once I got the userID, I need to call the another method normalSignupMethod.
[ConnectionObj signUp:user];
[helper normalSignupMethod:dict];
signUp Method:
[MYRequest signUp:user successBlock:^(QBResponse *response, QBUUser *user) {
// Sign up was successful
// Store user id
[SingletonClass sharedMySingleton].userID = [NSString stringWithFormat:#"%#",response.data[#"id"]];
} errorBlock:^(QBResponse *response) {
// Handle error here
NSLog(#" error in creating session %#", response.error);
[SVProgressHUD showErrorWithStatus:NSLocalizedString(#"SignUp to Chat error!", nil)];
}];
This I how I have called:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
NSLog(#"Block1");
[ConnectionObj signUp:user];
});
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
NSLog(#"Group notify");
[helper normalSignupMethod:dict];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
});
Block 1 executed first, and then group notify called. But I'm getting the userID after the normalSignupMethod is finished. How to wait for a signUp method to get userID before calling the normalSignupMethod?
You can create a block with your signUp method like this and pass the Bool completion value to check is it called successfully or not. So change your method declaration like this.
-(void)signUp:(QBUser*)user andHandler:(void (^)(BOOL result))completionHandler;
And its definition
-(void)signUp:(QBUser*)user andHandler:(void (^)(BOOL result))completionHandler {
[MYRequest signUp:user successBlock:^(QBResponse *response, QBUUser *user) {
[SingletonClass sharedMySingleton].userID = [NSString stringWithFormat:#"%#",response.data[#"id"]];
completionHandler(YES);
} errorBlock:^(QBResponse *response) {
// Handle error here
NSLog(#" error in creating session %#", response.error);
[SVProgressHUD showErrorWithStatus:NSLocalizedString(#"SignUp to Chat error!", nil)];
completionHandler(NO);
}];
}
Now call this method like this.
[ConnectionObj signUp:user andHandler:^(BOOL result) {
if(result) {
[helper normalSignupMethod:dict];
}
}];
You can call the normalSignupMethod once the signUp:successBlock request returns to successBlock
[MYRequest signUp:user successBlock:^(QBResponse *response, QBUUser *user) {
// Sign up was successful
// Store user id
[SingletonClass sharedMySingleton].userID = [NSString stringWithFormat:#"%#",response.data[#"id"]];
//call the signup method
[helper normalSignupMethod:dict];
} errorBlock:^(QBResponse *response) {
// Handle error here
NSLog(#" error in creating session %#", response.error);
[SVProgressHUD showErrorWithStatus:NSLocalizedString(#"SignUp to Chat error!", nil)];
}];
Related
I am calling languageConvertor function that returns string type value but i want to return that string type value after completion block executed please check the below code
-(NSString *)languageConvertor:(NSString *)str
{
[self.translator translateText:str completion:^(NSError *error, NSString *translated, NSString *sourceLanguage)
{
if (error)
{
[SVProgressHUD dismiss];
}
else
{
NSString *fromLanguage = [[self currentLocale] displayNameForKey:NSLocaleIdentifier value:sourceLanguage];
[SVProgressHUD dismiss];
}
}];
return translated;
}
Now in the above code i want to return translated string but after executed completion block so any one plz suggest me?
No, You can not return from the Block. You have to find a different way to do it.
Like
-(NSString *)languageConvertorForString:(NSString*)str withComletionBlock:(void (^)(NSString *translated, NSError *error))block
{
[self.translator translateText:str completion:^(NSError *error, NSString *translated, NSString *sourceLanguage)
{
[SVProgressHUD dismiss];
block(translated, error);
}];
}
Block takes your process asynchronous so its next line is executed just as you call that block function.
So you have to create your own block get the result of existing block.
- (void)languageConvertor:(NSString *)str withCompletion:(void (^)(NSString *string, NSError *error))completion
{
[self.translator translateText:str completion:^(NSError *error, NSString *translated, NSString *sourceLanguage)
{
if (error)
{
completion(nil, error);
[SVProgressHUD dismiss];
}
else
{ NSString *fromLanguage = [[self currentLocale] displayNameForKey:NSLocaleIdentifier value:sourceLanguage];
completion(yourReturnString, nil);
[SVProgressHUD dismiss];
}
}];
}
And call the method like,
[yourObject languageConvertor:yourString withCompletion:^(NSString *string, NSError *error) {
if(error) {
} else {
}
}];
If you want to do some UI change on return then please call the completion block from main queue,
dispatch_async(dispatch_get_main_queue(), ^{
completion(yourString, nil);
});
As you are using completion block for translating the text. That block is called asynchronous. That why the return statement is called before translating the text.
So to return translating text you need to add a block parameter in your method. As per shown below.
-(void)languageConvertor:(NSString *)str completionBlock:(void(^)(NSString *strText, NSError *error)) completion {
[self.translator translateText:str completion:^(NSError *error, NSString *translated, NSString *sourceLanguage) {
if (error) {
[SVProgressHUD dismiss];
}
else {
NSString *fromLanguage = [[self currentLocale] displayNameForKey:NSLocaleIdentifier value:sourceLanguage];
[SVProgressHUD dismiss];
}
if (completion) {
completion(translated, error);
}
}];
}
Let me know if you have any query.
Thanks.
I know this question has been asked multiple time but none of them solved my problem.
Error Domain=com.quickblox.chat Code=401 "Password not verified"
actually I have tried this:
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"bdsfbd %#",chatuserobj.fullName);
NSLog(#"Chat Id %lu",(unsigned long)chatuserobj.ID);
NSLog(#"Current User %#",[QBSession currentSession].currentUser);
QBUUser *currentUserr = [QBUUser user];
currentUserr.ID = appDelegate.loginUserId;
currentUserr.password = appDelegate.loginUserPassword;
// connect to Chat
[QBRequest logInWithUserLogin:appDelegate.loginUser password:appDelegate.loginUserPassword successBlock:^(QBResponse *response, QBUUser *user)
{
chatDialog = [[QBChatDialog alloc] initWithDialogID:NULL type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(chatuserobj.ID)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog)
{
NSLog(#"Created Dialog %#",createdDialog);
} errorBlock:^(QBResponse *response)
{
NSLog(#"Error %#",response);
}];
} errorBlock:^(QBResponse *response) {
}];
[[QBChat instance] connectWithUser:chatuserobj completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
[self startChat];
}];
[QBSettings setKeepAliveInterval:30];
[QBSettings setAutoReconnectEnabled:YES];
}
and this
-(void)startChat
{
[[QBChat instance] addDelegate:self];
QBChatMessage *message = [QBChatMessage message];
[message setText:#"Hey there"];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"save_to_history"] = #YES;
[message setCustomParameters:params];
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error)
{
NSLog(#"Completed: %#",error.description);
}];
}
I don't know where I am wrong. So point out my mistake.
EDIT: again in did load
- (void)viewDidLoad
{
[super viewDidLoad];
// connect to Chat
[[QBChat instance] connectWithUser:currentUserr completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
}];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self chat];
});
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self startChat];
});
}
chat is method for creating dialog for private group or one to one connection
-(void)chat
{
chatDialog = [[QBChatDialog alloc] initWithDialogID:NULL type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(chatuserobj.ID)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
} errorBlock:^(QBResponse *response) {
}];
}
and start chat actual communication occur
-(void)startChat
{
[[QBChat instance] addDelegate:self];
QBChatMessage *message = [QBChatMessage message];
[message setText:#"Hey there"];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"save_to_history"] = #YES;
[message setCustomParameters:params];
[chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error)
{
NSLog(#"Completed: %#",error.description);
}];
}
now this error occur
UserInfo={NSLocalizedRecoverySuggestion = You are not connected to chat.
- (void)viewDidLoad
{
[super viewDidLoad];
// connect to Chat
[[QBChat instance] connectWithUser:currentUserr completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self chat];
});
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self startChat];
});
}];
}
and same method of start chat and chat. it works well
[[QBChat instance] connectWithUser:chatuserobj completion:^(NSError * _Nullable error)
{
NSLog(#"USer is Connected %#",error.description);
}];
This method is used to connect yourself to the chat and not your opponents. Furthermore, in order to connect with this method your QBUUser instance must have valid password set as password property.
Basically you need to connect yourself to the chat and then just start creating dialogs and sending messages.
In my app, 2-4 API calls to my server can be happening at the same time (asynchronously) within my API class's NSURLSession. In order to make API requests to my server, I must supply the authentication token in the HTTPHeaderField of each NSURLRequest. The token is valid for one day, and if it becomes invalid after one day, I need to refresh the token.
I do this in the following code in my API class:
/*!
* #brief sends a request as an NSHTTPURLResponse. This method is private.
* #param request The request to send.
* #param success A block to be called if the request is successful.
* #param error A block to be called if the request fails.
*/
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
[self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
{
//if auth token expired and getting "not authenticated" error (status 401)
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 401) {
[self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {
self.authToken = response[#"token"];
//attempt to re-try the request that failed due to token expiration
[self sendTask:request successCallback:success errorCallback:errorCallback];
} errorCallback:^(NSString *error) {
//two weeks have passed and the token is no longer refreshable
NSLog(#"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");
}];
}
}];
}];
[task resume];
}
This sendTask method gets executed with every API request I make in the app, so I just realized this is a bad way of doing it. If 3 API requests fail due to the token being invalid (one day passed), then all 3 of these API requests are going to attempt to make the API call to refresh the authentication token.
Is there a way for me to, in case ONE of the API requests fail, refresh the authentication token only ONCE and then re-attempt the failed API calls?
EDIT
I edited the title of the question to indicate that I'm working with NSURLSession
PROGRESS
So far, to prevent several failed API requests from trying to refresh the authentication token at the same time, I have an NSArray for all the failed requests and an NSNumber that serves as a lock to make sure that the authentication token is only trying to be refreshed once. I do this in the following code:
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
MyAPIInterface *__weak weakSelf = self;
[self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
{
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 401) {
if ([error isEqualToString:#"invalid_credentials"]) {
errorCallback(#"Invalid username and/or password");
}
else if ([error isEqualToString:#"Unknown error"]) {
errorCallback(error);
}
else {
if (!weakSelf.alreadyRefreshingToken.boolValue) {
//lock alreadyRefreshingToken boolean
weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:YES];
NSLog(#"NOT REFRESHING TOKEN");
// add failed request to failedRequests array
NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy];
[mutableFailedRequests addObject:request];
weakSelf.failedRequests = [mutableFailedRequests copy];
// refresh auth token
[weakSelf refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {
//store authToken
weakSelf.authToken = response[#"token"];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:weakSelf.authToken forKey:#"authToken"];
[defaults synchronize];
//attempt to re-try all requests that failed due to token expiration
for (NSURLRequest *failedRequest in weakSelf.failedRequests) {
[weakSelf sendTask:failedRequest successCallback:success errorCallback:errorCallback];
}
//clear failedRequests array and unlock alreadyRefreshingToken boolean
[weakSelf clearFailedRequests];
weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:NO];
NSLog(#"TOKEN REFRESHING SUCCESSFUL THO");
} errorCallback:^(NSString *error) {
NSLog(#"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");
//clear failedRequests array
[weakSelf clearFailedRequests];
errorCallback(#"Your login session has expired");
}];
}
else {
NSLog(#"ALREADY REFRESHING TOKEN. JUST ADD TO FAILED LIST");
NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy];
[mutableFailedRequests addObject:request];
weakSelf.failedRequests = [mutableFailedRequests copy];
}
}
}
else {
NSLog(#"ERROR STRING THO: %#", error);
errorCallback(error);
}
}];
}];
[task resume];
}
#pragma mark Custom Methods
-(void)clearFailedRequests {
NSMutableArray *mutableFailedRequests = [self.failedRequests mutableCopy];
[mutableFailedRequests removeAllObjects];
self.failedRequests = [mutableFailedRequests copy];
}
Am I going about this correctly? One part that I'm paranoid about is that I'm not really calling the success or error callback at certain points. Can this lead to problems?
Instead of using [self sendTask:], try with [weakSelf sendTask]. Check below code:
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
__weak __typeof(self)weakSelf = self;
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
[self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
{
//if auth token expired and getting "not authenticated" error (status 401)
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 401) {
[self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {
self.authToken = response[#"token"];
//attempt to re-try the request that failed due to token expiration
[weakSelf sendTask:request successCallback:success errorCallback:errorCallback];
} errorCallback:^(NSString *error) {
//two weeks have passed and the token is no longer refreshable
NSLog(#"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");
}];
}
}];
}];
[task resume];
}
I am making a simple GET request using AFNetworking 2, but I am getting a NSURLErrorDomain error.
I created a manager class which subclasses AFHTTPRequestOperationManager and creates a singleton instance so that I can use a shared manager.
+ (id)manager {
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
NSURL *baseURL = [ZSSAuthentication baseURL];
self = [super initWithBaseURL:baseURL];
if (self) {
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
[self.requestSerializer setAuthorizationHeaderFieldWithUsername:[ZSSAuthentication username] password:[ZSSAuthentication password]];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
}
return self;
}
- (void)getData:(NSString *)pubID parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *results))completion failure:(void (^)(NSError *error))failure {
NSString *url = [NSString stringWithFormat:#"data/all/%#", pubID];
[self GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Check to see if there are errors
ZSSError *error = [self errorForAPICall:responseObject status:[operation.response statusCode]];
if (error) {
[self logMessage:error.localizedDescription];
failure(error);
return;
}
NSDictionary *data = [responseObject objectForKey:#"data"];
completion(data);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
}
Then, in my viewController's viewDidLoad method I make a call to that method:
[[ZSSManager manager] getData:self.pubID parameters:nil completion:^(NSDictionary *results) {
self.items = results;
[self dataWillReload];
NSLog(#"%#", results);
[self.tableView reloadData];
} failure:^(NSError *error) {
NSLog(#"Error: %# %li", error, (long)error.code);
}];
Then I get this error:
Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)" UserInfo=0x7ff952306610 {NSErrorFailingURLKey=http://test.mysite.com/v1/data/all/5}
The strange thing is, on a previous viewController, I make a different call to the manager, and it completes and returns data correctly. But, when I make this second call, I get the error. AND, if I move that getData call out of the viewDidLoad method, and invoke it with a button press, it DOES WORK. What the heck?
What could be causing this?
I am having problems in saving a response coming from a POST request.
Based on AFnetworking documentation and NSScreencast tutorial I created my own subclass of AFHTTPRequestOperationManager, but I am not sure why the response is not saved.
How do I know, the response is not saved?
Because there is an error:(null) message in console and the my method does not perform segue.
I know that I am getting the values, because of the breakpoint that I put NSURLSessionDataTask
But I do not know why the values are not saved and I am getting an error message. I appreciate any help.
The APIClient/Manager
AuthAPIManager.h
#import "AFHTTPSessionManager.h"
#interface AuthAPIManager : AFHTTPSessionManager
+(AuthAPIManager *)sharedManager;
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void(^)(NSDictionary *results, NSError *error))completion;
//for login
#property (nonatomic,readonly,retain)NSString *StoreIdentifierForVendor;
#property(nonatomic,copy)NSString *devicetype;
#end
AuthAPIManager.m
#import "AuthAPIManager.h"
#import "LoginInfo.h"
#import "CredentialStore.h"
static AuthAPIManager *sharedManager =nil;
static dispatch_once_t onceToken;
#implementation AuthAPIManager
+(AuthAPIManager *)sharedManager
{
dispatch_once(&onceToken, ^{
sharedManager = [[AuthAPIManager alloc] initWithBaseURL:[NSURL URLWithString:BASE_URL]];
sharedManager.responseSerializer=[AFJSONResponseSerializer serializer];
sharedManager.requestSerializer = [AFJSONRequestSerializer serializer];
});
return sharedManager;
}
-(id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
}
return self;
}
-(NSURLSessionDataTask *)initializeLogin:(NSString *)username completion:(void (^)(NSDictionary *, NSError *))completion
{
_devicetype = #"ios";
_StoreIdentifierForVendor = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
id loginParameters =#{#"AccountId":username,
#"DeviceType":_devicetype
};
NSURLSessionDataTask *task =[self POST:#"/Accn" parameters:loginParameters
success:^(NSURLSessionDataTask *task, id responseObject)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
LoginInfo *loginInfo =[[LoginInfo alloc]initWithDictionary:responseObject];
CredentialStore *credStore =[CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
completion(responseObject,nil);
loginInfo = responseObject;
} else {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, nil);
});
NSLog(#"Received: %#", responseObject);
NSLog(#"Received HTTP %d", httpResponse.statusCode);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}];
return task;
}
#end
And this is how I am calling my method in my view controller
- (IBAction)login:(id)sender
{
[_usernameTextField resignFirstResponder];
[SVProgressHUD show];
NSURLSessionDataTask *task = [[AuthAPIManager sharedManager] initializeLogin:self.usernameTextField.text completion:^(NSDictionary *results, NSError *error)
{
if (results) {
LoginInfo *loginInfo = [[LoginInfo alloc]initWithDictionary:results];
CredentialStore *credStore =[ CredentialStore sharedStore];
credStore.loginInfo =loginInfo;
[self performSegueWithIdentifier:#"welcomeViewSegue" sender:self];
}
else
{
NSLog(#"there is an error:%#",error);
}
}];
[SVProgressHUD dismiss];
}
Your success block is being called, but your status code is not 200.
You are calling your completion block like this:
completion(nil, nil);
So this code:
if (results) {
[snip]
}
else
{
NSLog(#"there is an error:%#",error);
}
is passed a nil results object.
Set a breakpoint on if (httpResponse.statusCode == 200) and inspect httpResponse in your debugger to see why it's not what you expect. (You may get a different success code, such as 204.)
Call your completion block with results instead of nil.