I have a question regarding Quick blox API. Right now I am sign up an user using below code.
[QBRequest createSessionWithSuccessBlock:^(QBResponse *response, QBASession *session) {
// session created
QBUUser *user = [QBUUser user];
user.password = userPasswordTextField.text;
user.login = userNameTextField.text;
user.fullName = userRealNameTextField.text;
user.email = userEmailTextField.text;
// Registration sign up of User
[QBRequest signUp:user successBlock:^(QBResponse *response, QBUUser *user) {
[QBRequest createSessionWithSuccessBlock:^(QBResponse *response, QBASession *session) {
NSLog(#"checkingl registering");
[QBRequest userWithLogin:user.login successBlock:^(QBResponse *response, QBUUser *user) {
NSLog(#"checkingl updatingqb");
} errorBlock:^(QBResponse *response) {
// Handle error
}];
}errorBlock:^(QBResponse *response) {
// Handle error
}];
} errorBlock:^(QBResponse *response) {
// Handle error here
NSLog(#"error while signing up with QB");
NSLog(#"fail sign Up %#",response);;
[self showAlert:nil message:#"User with login that has already been taken" cancelButtonTitle:nil otherButtonTitle:#"OK"];
return ;
}];
} errorBlock:^(QBResponse *response) {
// handle errors
NSLog(#" error in creating session %#", response.error);
}];
In above code first I am creating a session and sign up an new user, then I am login user. At sign up time I did't log in user for QuickBlox Chat at signup time, But later when I will log in into Quick blox chat in another module, still I have to create new session or I have to maintain new session.
Any session will remain valid for 2 hours after the last request to QuickBlox. To check a session's expiration date use this next snippet of code:
NSDate *sessionExpiratioDate = [QBBaseModule sharedModule].tokenExpirationDate;
NSDate *currentDate = [NSDate date];
NSTimeInterval interval = [currentDate timeIntervalSinceDate:tokenExpirationDate];
if(interval > 0){
// recreate session here
}
Check this guide. This feature is available since 1.8 iOS SDK.
Reference: Igor Khomenko
Related
I have a requirement,Lets say there are 3-Users for my app. I want to add an image(Profile pic purpose) to each user so that this image can be visible to other two users who are using the same app. Just like whatsapp profile pics. So for this purpose I did the following things.
Step 1 : Logged in as User1
then I added this code to upload image file. After I logged in with User1 credentials and I made Public property as YES while uploading file.
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:#"arrow.png"]);
[QBRequest TUploadFile:imageData fileName:#"arrow.png" contentType:#"image/png" isPublic:YES successBlock:^(QBResponse *response, QBCBlob *blob) {
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
} errorBlock:^(QBResponse *response) {
}];
Step 2 : Logged in as User2
Then I did like this I'm fetching all the users belongs to my app. using
QBGeneralResponsePage *responsePage = [QBGeneralResponsePage responsePageWithCurrentPage:currentPage perPage:perPage];
[QBRequest usersForPage:responsePage successBlock:^(QBResponse *response, QBGeneralResponsePage *page, NSArray *users) {
weakSelf.allUsers = [users mutablecopy]
[weakSelf.tableView reloadData];
} errorBlock:^(QBResponse *response) {
}];
Step 3 :
Now I have all the users so I'm doing iteration to find the users blob id .
QBUUser *user = (QBUUser *)self.allUsers[indexPath.row];
if (user.blobID != 0) {
[QBRequest downloadFileWithID:user.blobID successBlock:^(QBResponse *response, NSData *fileData) {
UIImage *img=[UIImage imageWithData:fileData];
[cell.imageView setImage:img];
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
} errorBlock:^(QBResponse *response) {
}];
}
But the problem is here I'm not getting any blobID with the User1 means blobID of user1 as 0(all users blobID as 0), But while uploading it was success and I got blobID after uploading the file using User1 as logged in.
But when I logged in as user2 or User3 it is not showing any blobID associated with user1. And I made Public as Yes while uploading.
Is this correct way of doing or any mistake I'm doing please let me know ?
You should update user's blobID using request with QBUpdateUserParameters after blob's uploading:
[QBRequest TUploadFile:imageData fileName:#"arrow.png" contentType:#"image/png" isPublic:YES successBlock:^(QBResponse *response, QBCBlob *blob) {
QBUpdateUserParameters *userParams = [QBUpdateUserParameters new];
userParams.blobID = blob.ID;
[QBRequest updateCurrentUser:userParams successBlock:^(QBResponse * _Nonnull __unused response, QBUUser * _Nullable user) {
} errorBlock:^(QBResponse * _Nonnull response) {
}];
} statusBlock:^(QBRequest *request, QBRequestStatus *status) {
} errorBlock:^(QBResponse *response) {
}];
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'm trying to make login in Quickblox app via Facebook SDK
But I get 422 error.
I've done all things that need in Quikblox, Facebook and iOS app
Created Facebook app and integrated SDK to iOS app.
I Filled up all required fields in Quickblox admin
I verified token here
-(IBAction)test:(id)sender {
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"email"]
fromViewController:self handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
// Process error
}
else if (result.isCancelled) {
// Handle cancellations
}
else {
if ([result.grantedPermissions containsObject:#"email"])
{
NSLog(#"result is:%#",result.token.tokenString);
[QBRequest logInWithSocialProvider:#"facebook" accessToken:result.token.tokenString
accessTokenSecret:nil
successBlock:^(QBResponse *response, QBUUser *user) {
// Login succeded
} errorBlock:^(QBResponse *response) {
// Handle error
}];
}
}
}];
}
also I tried with QMServicesManager
[[[QMServicesManager instance]authService]logInWithFacebookSessionToken:[FBSDKAccessToken currentAccessToken].tokenString completion:^(QBResponse *response, QBUUser *userProfile) {
}];
Here my logs
Your facebook token does not return email address, this is required for quickblox to successfully register user and log it in.
I am able to create group dialog box as I found this code:
chatDialog = [[QBChatDialog alloc] initWithDialogID:#"dialogueid" type:QBChatDialogTypeGroup];
chatDialog.name = #" Bob, Sam, Garry";
chatDialog.occupantIDs = #[#(1)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
} errorBlock:^(QBResponse *response) {
}];
but when I try to change type to QBChatDialogTypePrivate.
Dialog is not creating and can you please tell me what is dialog id and where to find it?
Just init your QBChatDialog with ID nil, server will set it for you and createdDialog that is returned by QBRequest will have correct dialogID.
QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:nil type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(opponentID)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
NSLog(#"Created dialog: %#", createdDialog);
} errorBlock:^(QBResponse *response) {
NSLog(#"Failed to create dialog with error: %#", response.error);
}];
Here is an example of how I retrieve objects of a custom class from Quickblox:
[QBRequest objectsWithClassName:#"My_Custom_Class" extendedRequest:getRequest successBlock:^(QBResponse *response, NSArray *objects, QBResponsePage *page) {
// Success
} errorBlock:^(QBResponse *response) {
// Error
}];
Here is an example of how I create new objects for a custom class in Quickblox:
QBCOCustomObject *object = [QBCOCustomObject customObject];
object.className = #"My_Custom_Class";
// Object fields
[object.fields setObject:self.titleTextView.text forKey:#"Title"];
[object.fields setObject:self.bodyTextView.text forKey:#"Body"];
[object.fields setObject:self.userSession.fullName forKey:#"Author_Name"];
[object.fields setObject:#"self.username" forKey:#"Author_Username"];
[QBRequest createObject:object successBlock:^(QBResponse *response, QBCOCustomObject *object) {
// Response
} errorBlock:^(QBResponse *response) {
// Error
}];
I need to be able to get the status/progress of these, so that I can show a loading/progress bar to my users based on actual progress made.
How can I do this?
There is no Statusblock callback for these two methods. You can just show an activity indicator to show the progress.