I am using QuickBlox SDK from follwing refrence
https://quickblox.com/developers/SimpleSample-chat_users-ios
In Sample chat I am also making Group video chat . but When I Update particular tag With the help of below code. Than I am unable to see the updated value in my quickblox admin panel. If some one update the tag name than Please suggest me. I am using below code
QBUUser *qbUser = [QBUUser user];
qbUser.ID = 23429378;
NSString *roomName = #"ios group";
qbUser.tags = #[roomName].mutableCopy;
NSArray *arry = [[NSArray alloc]initWithObjects:qbUser, nil];
[QMUsersCache.instance insertOrUpdateUsers:users];
- (BFTask *)insertOrUpdateUsers:(NSArray *)users
{
__weak __typeof(self)weakSelf = self;
return [BFTask taskFromExecutor:[BFExecutor executorWithDispatchQueue:self.queue] withBlock:^id{
__typeof(self) strongSelf = weakSelf;
NSManagedObjectContext* context = [strongSelf backgroundContext];
NSMutableArray *toInsert = [NSMutableArray array];
NSMutableArray *toUpdate = [NSMutableArray array];
//To Insert / Update
for (QBUUser *user in users)
{
CDUser *cachedUser = [CDUser QM_findFirstWithPredicate:IS(#"id", #(user.ID)) inContext:context];
if (cachedUser) {
QBUUser *qbCachedUser = [cachedUser toQBUUser];
if (![user.updatedAt isEqualToDate:qbCachedUser.updatedAt]) {
[toUpdate addObject:user];
}
}
else {
[toInsert addObject:user];
}
}
if (toUpdate.count > 0) {
[strongSelf updateUsers:toUpdate inContext:context];
}
if (toInsert.count > 0) {
[strongSelf insertUsers:toInsert inContext:context];
}
if (toInsert.count + toUpdate.count > 0) {
[context QM_saveToPersistentStoreAndWait];
}
QMSLog(#"[%#] Users to insert %tu, update %tu", NSStringFromClass([weakSelf class]), toInsert.count, toUpdate.count);
return nil;
}];
}
Your code is just updating users in CoreData store.
You can update only the current user from the application:
+ (QBRequest *)updateCurrentUser:(QBUpdateUserParameters *)parameters
successBlock:(nullable void (^)(QBResponse *response, QBUUser * _Nullable user))successBlock
errorBlock:(nullable QBRequestErrorBlock)errorBlock;
If you want to update other users you should do it via Admin Panel.
Sample chat related to this question.
API Documentation related to this question.
Related
I am trying to integrate Hyperpay payment into React Native project and I have problems with objective-c, I followed an article and found many issues and with searching, I solve them, but still two issues I can't solve because I am not familiar with objective-c
Issue 1,
No known class method for selector 'presentCheckoutForSubmittingTransactionCompletionHandler:cancelHandler:'
Issue 2,
No known class method for selector 'dismissCheckoutAnimated:completion:'
I am sorry if my code is long but I don't to miss something
// RCTCalendarModule.m
#import "HyperPay.h"
#import "UIKit/UIKit.h"
#import <OPPWAMobile/OPPWAMobile.h>
#implementation HyperPay{
RCTResponseSenderBlock onDoneClick;
RCTResponseSenderBlock onCancelClick;
UIViewController *rootViewController;
NSString *isRedirect;
OPPPaymentProvider *provider;
}
// To export a module named RCTCalendarModule
RCT_EXPORT_METHOD(openHyperPay:(NSDictionary *)indic createDialog:(RCTResponseSenderBlock)doneCallback createDialog:(RCTResponseSenderBlock)cancelCallback) {
onDoneClick = doneCallback;
onCancelClick = cancelCallback;
NSArray *events = #[];
if ([indic[#"is_sandbox"] isEqualToString:#"1"]) {
provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeTest];
} else {
provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeLive];
}
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set available payment brands for your shop
checkoutSettings.paymentBrands = #[#"VISA", #"MASTER"];
// Set shopper result URL
checkoutSettings.shopperResultURL = #"com.simicart.enterprise.payments://result";
OPPCheckoutProvider *checkoutProvider = [OPPCheckoutProvider checkoutProviderWithPaymentProvider:provider checkoutID:indic[#"checkoutId"]
settings:checkoutSettings];
dispatch_async(dispatch_get_main_queue(), ^{
[OPPCheckoutProvider presentCheckoutForSubmittingTransactionCompletionHandler:^(OPPTransaction * _Nullable transaction, NSError * _Nullable error) {
if (error) {
// Executed in case of failure of the transaction for any reason
if (isRedirect && ![isRedirect isEqualToString:#"1"]) {
onCancelClick(#[#"cancel", events]);
}
} else if (transaction.type == OPPTransactionTypeSynchronous) {
// Send request to your server to obtain the status of the synchronous transaction
// You can use transaction.resourcePath or just checkout id to do it
NSDictionary *responeDic = #{#"resourcePath" : transaction.resourcePath};
onDoneClick(#[responeDic, events]);
NSLog(#"%#", transaction.resourcePath);
} else {
// The SDK opens transaction.redirectUrl in a browser
// See 'Asynchronous Payments' guide for more details
}
} cancelHandler:^{
onCancelClick(#[#"cancel", events]);
// Executed if the shopper closes the payment page prematurely
}];
});
}
- (instancetype)init{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getStatusOder:) name:#"getStatusOrder" object:nil];
}
return self;
}
- (void)getStatusOder:(NSNotification*)noti{
[OPPCheckoutProvider dismissCheckoutAnimated:YES completion:^{
isRedirect = #"1";
NSURL *url = noti.object;
NSString *urlString = [url absoluteString];
NSLog(#"%#", urlString);
if (![urlString isEqualToString:#"com.simicart.enterprise.payments://result"]) {
NSArray *events = #[];
NSDictionary *responeDic = #{#"url" : urlString};
onDoneClick(#[responeDic, events]);
}
}];
}
#end
While creating a chat dialog for a group.
For e.g User A is creating dialog and User B want to use that dialog.
But sometimes scenario is occurring that User A create one dialog and then User B create another dialog.
So they are not able to chat with each other because of two different dialogs.
Below is the code i am using to create dialog :-
-(void) moveToChatView:(QBChatDialog *)chatDialog ObjFriend:(Friend *)objFriend
{
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog)
{
// Success, do something
}
errorBlock:^(QBResponse *response)
{
}];
}
Edit :- Is there any method like createOrJoinRoomWithName??
To if you want to add user in groupchat then you need to update the group dialogue.
QBChatDialog *updateDialog = [[QBChatDialog alloc] initWithDialogID:#"53aac645535c12bd3b008a40" type:QBChatDialogTypeGroup];
updateDialog.pushOccupantsIDs = #[#"300", #"301", #"302"];
updateDialog.name = #"school friends";
[QBRequest updateDialog:updateDialog successBlock:^(QBResponse *responce, QBChatDialog *dialog) {
} errorBlock:^(QBResponse *response) {
}];
For more detail check this Update_group_dialog
And for chat in group dialogue check Chat_in_group_dialog
Don't forget to use delegate method.
pragma mark QBChatDelegate
- (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromDialogId:(NSString *)dialogId{
}
Edit 1:- you will get DialogId with retriving all dialog.
QBResponsePage *page = [QBResponsePage responsePageWithLimit:100 skip:0];
[QBRequest dialogsForPage:page extendedRequest:nil successBlock:^(QBResponse *response, NSArray *dialogObjects, NSSet *dialogsUsersIDs, QBResponsePage *page) {
} errorBlock:^(QBResponse *response) {
}];
Edit 2:- To know the dialogId when creating new Dialog use createChatNotificationForGroupChatCreation method.
- (QBChatMessage *)createChatNotificationForGroupChatCreation:(QBDialog *)dialog
{
// create message:
QBChatMessage *inviteMessage = [QBChatMessage message];
NSMutableDictionary *customParams = [NSMutableDictionary new];
customParams[#"xmpp_room_jid"] = dialog.roomJID;
customParams[#"name"] = dialog.name;
customParams[#"_id"] = dialog.ID;
customParams[#"type"] = #(dialog.type);
customParams[#"occupants_ids"] = [dialog.occupantIDs componentsJoinedByString:#","];
// Add notification_type=1 to extra params when you created a group chat
//
customParams[#"notification_type"] = #"1";
inviteMessage.customParameters = customParams;
return inviteMessage;
}
Trying to Authenticate with Azure Active Directory and fetch mail, calendar data, accessToken is returned successfully:
authority = #"https://login.windows.net/common/oauth2/authorize";
redirectUriString = #"http://xxxxxx.xxxxxxx.com/oauth";
resourceId = #"https://outlook.office365.com";
clientId = #"xxxxxxx-xxxxx-xxx";
-(void) getToken : (BOOL) clearCache completionHandler:(void (^) (NSString*))completionBlock;
{
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority
error:&error];
[authContext setValidateAuthority:YES];
NSURL *redirectUri = [NSURL URLWithString:redirectUriString];
if(clearCache){
[authContext.tokenCacheStore removeAllWithError:&error];
if (error) {
NSLog(#"Error: %#", error);
}
}
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
// display error on the screen
[self showError:result.error.errorDetails];
}
else{
completionBlock(result.accessToken);
}
}];
}
-(NSArray*)getEventsList
{
__block NSMutableArray * todoList;
[self getToken:YES completionHandler:^(NSString* accessToken){
NSURL *todoRestApiURL = [[NSURL alloc]initWithString:#"https://outlook.office365.com/api/v1.0/me/folders/inbox/messages?$top=2"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:todoRestApiURL];
NSString *authHeader = [NSString stringWithFormat:#"Bearer %#", #""];
[request addValue:authHeader forHTTPHeaderField:#"Authorization"];
[request addValue:#"application/json; odata.metadata=none" forHTTPHeaderField:#"accept"];
[request addValue:#"fbbadfe-9211-1234-9654-fe435986a1d6" forHTTPHeaderField:#"client-request-id"];
[request addValue:#"Presence-Propelics/1.0" forHTTPHeaderField:#"User-Agent"];
//[request addValue:#"true" forHTTPHeaderField:#"return-client-request-id"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil){
NSArray *scenarios = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
todoList = [[NSMutableArray alloc]initWithArray:scenarios];
//each object is a key value pair
NSDictionary *keyVauePairs;
for(int i =0; i < todoList.count; i++)
{
keyVauePairs = [todoList objectAtIndex:i];
NSLog(#"%#", keyVauePairs);
}
}
NSLog(#"Finished");
//[delegate updateTodoList:TodoList];
}];
}];
return nil; }
Error is returned in response object:
{
error = {
code = ErrorAccessDenied;
message = "Access is denied. Check credentials and try again.";
};
}
I know its late to answer this but it might be helpful for someone like me who was struggling to get the same thing done
I have done this using the office 365 SDK for iOS which has all the inbuilt classes to do your work.
If you download their sample code it will provide you all the details you require to do certain operations (mail, calendar, contacts, one drive).
Before using the SDK make sure you login to Azure AD and register your application and add permissions so that you do not get 403 error code or any access denied message.
I am using the below code to fetch my events details from outlook calendar
[self getClientEvents:^(MSOutlookClient *client) {
NSURLSessionDataTask *task = [[[client getMe] getEvents] read:^(NSArray<MSOutlookEvent> *events, MSODataException *error) {
if (error==nil) {
if (events.count!=0) {
dispatch_async(dispatch_get_main_queue(), ^{
for(MSOutlookEvent *calendarEvent in events){
NSLog(#"name = %#",calendarEvent.Subject);
}
});
}else{
NSLog(#"No events found for today");
}
}
}];
[task resume];
}];
getClientEvents is a method which gives call to the Office 365 SDK and fetches the event details of the user but it first fetches the token for the resource and then makes the call with the acquired token
-(void)getClientEvents : (void (^) (MSOutlookClient* ))callback{
[self getTokenWith : #"https://outlook.office365.com" :true completionHandler:^(NSString *token) {
MSODataDefaultDependencyResolver* resolver = [MSODataDefaultDependencyResolver alloc];
MSODataOAuthCredentials* credentials = [MSODataOAuthCredentials alloc];
[credentials addToken:token];
MSODataCredentialsImpl* credentialsImpl = [MSODataCredentialsImpl alloc];
[credentialsImpl setCredentials:credentials];
[resolver setCredentialsFactory:credentialsImpl];
[[resolver getLogger] log:#"Going to call client API" :(MSODataLogLevel *)INFO];
callback([[MSOutlookClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:resolver]);
}];
}
getTokenWith method fetches the token for a resource first and then with the acquired token makes the necessary calls to fetch the events, but before fetching the token it checks in the cache to see if there are any tokens available for the same resource.
// fetch tokens for resources
- (void) getTokenWith :(NSString *)resourceId : (BOOL) clearCache completionHandler:(void (^) (NSString *))completionBlock;
{
// first check if the token for the resource is present or not
if([self getCacheToken : resourceId completionHandler:completionBlock]) return;
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:[[NSUserDefaults standardUserDefaults] objectForKey:#"authority"] error:&error];
NSURL *redirectUri = [NSURL URLWithString:#"YOUR_REDIRECT_URI"];
[authContext acquireTokenWithResource:resourceId
clientId:[[NSUserDefaults standardUserDefaults] objectForKey:#"clientID"]
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
[self showError:result.error.errorDetails];
}
else{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(result.accessToken);
}
}];
}
getCacheToken method: Checks if there are any reusable token for any resources.
-(BOOL)getCacheToken : (NSString *)resourceId completionHandler:(void (^) (NSString *))completionBlock {
ADAuthenticationError * error;
id<ADTokenCacheStoring> cache = [ADAuthenticationSettings sharedInstance].defaultTokenCacheStore;
NSArray *array = [cache allItemsWithError:&error];
if([array count] == 0) return false;
ADTokenCacheStoreItem *cacheItem;
for (ADTokenCacheStoreItem *item in array) {
if([item.resource isEqualToString:resourceId]){
cacheItem = item;
break;
}
}
ADUserInformation *user = cacheItem.userInformation;
if(user == nil) return false;
if([cacheItem isExpired]){
return [self refreshToken:resourceId completionHandler:completionBlock];
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:user.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(cacheItem.accessToken);
return true;
}
}
Using this code and Office 365 SDK in place you can get the outlook events for a particular user, before that make sure you have full permissions in the Azure AD else you may get 0 events as response.
Please note all the methods are from the SDK example apart from the first method to view how to fetch the events i would recommend to download the exchange example from the github.
You can also use MSGraph SDK to fetch calendars and events:
Check this link: Configuration process is same, only fetching events is different(see given code for fetching events):
How to Fetch/Create calender by O365-iOS-Connect?
Note: Above link is used to fetch calendars from outlook the process is same for this but you should use this code after authentication and completed get events action look like this:
- (IBAction)getCalendarsEvents:(id)sender {
[NXOAuth2AuthenticationProvider setClientId:clientId
scopes:#[#"https://graph.microsoft.com/Files.ReadWrite",
#"https://graph.microsoft.com/Calendars.ReadWrite"]];
[[NXOAuth2AuthenticationProvider sharedAuthProvider] loginWithViewController:nil completion:^(NSError *error) {
if (!error) {
[MSGraphClient setAuthenticationProvider:[NXOAuth2AuthenticationProvider sharedAuthProvider]];
self.client = [MSGraphClient client];
// Authentication done
[[[[_client me] events] request] getWithCompletion:^(MSCollection *response, MSGraphUserEventsCollectionRequest *nextRequest, NSError *error){
NSArray *arr = response.value;
MSGraphEvent *event = arr.firstObject;
// Here you will getting outlook events
}];
}
}];
}
I need to display entire friend list's avatar images inside UITableView.
I could as well ask this into Quickblox forums but seems like their support is less responsive there.
I have read Quickblox documentation but can't find an efficient way of getting user's avatar images. All I see is [QBContent TDownloadFileWithBlobID], but I do not know how to use it.
I am fetching contacts using chatContactListDidChange delegate call like below. Now how can I also get all these users' avatar images also?
Note that friendsArray is my data source for the table view, so ideally I would want to store the avatar images as part of the same array.
- (void)chatContactListDidChange:(QBContactList *)contactList
{
NSLog(#"contact list changed");
NSLog(#"current contact list %#", [QBChat instance].contactList);
[self fetchContacts:[QBChat instance].contactList.contacts];
}
- (void) fetchContacts : (NSArray *) contactArray
{
NSMutableArray * userIDArray = [[NSMutableArray alloc] init];
for (QBContactListItem * contact in contactArray)
{
NSString * userIDString = [NSString stringWithFormat:#"%ld", (unsigned long)contact.userID];
[userIDArray addObject:userIDString];
}
if (!userIDArray.count)
{
return;
}
NSString * requestString = [userIDArray componentsJoinedByString:#","];
[QBUsers usersWithIDs:requestString delegate:self context:#"FetchFriends"];
}
- (void)completedWithResult:(Result *)result context:(void *)contextInfo
{
if([result isKindOfClass:[QBUUserPagedResult class]])
{
NSString * context = (__bridge NSString *)contextInfo;
// Success result
if(result.success)
{
QBUUserPagedResult * pagedResult = (QBUUserPagedResult *)result;
if ([context isEqualToString:#"FetchFriends"])
{
if (friendsArray)
{
[friendsArray removeAllObjects];
}
else
{
friendsArray = [[NSMutableArray alloc] init];
}
QBUUser * user = [pagedResult.users objectAtIndex:0];
//user.blobID
friendsArray addObjectsFromArray:pagedResult.users];
}
}
else
{
NSLog(#"Error getting users: %#", context);
}
}
}
Use QBUUser's fields - customData for public url of user's image and blobID for uploaded blob.
Image --> [QBContent TUploadFile...] --> Getting callback with result (QBCFileUploadTaskResult) --> getting blob (QBCBlob) --> save blob id to user.blobID --> get public url of your image: [currentBlob publicURL] --> save to user.customData --> update user: [QBUsers updateUser...]
Hope it helps you.
I have method called collectData in my app which is the most important part of my View Controller. In that method I do a couple of signicant things (downloading, parsing, saving to persistent store), so it would be easier for you to take a look:
-(void)collectData
{
// Downloading all groups and saving them to Core Data
[[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary* groups = [NSMutableDictionary new];
NSMutableArray* newIds = [NSMutableArray new];
NSError *error;
// Saving everything from response to MOC
for (id group in responseObject) {
Group *groupEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Group" inManagedObjectContext:self.moc];
groupEntity.name = [group valueForKey:#"name"];
groupEntity.cashID = [group valueForKey:#"id"];
groupEntity.caseInsensitiveName = [[group valueForKey:#"name"] lowercaseString];
groupEntity.selected = #NO;
// Filling up helping variables
groups[groupEntity.cashID] = groupEntity;
[newIds addObject:groupEntity.cashID];
}
// Fetching existing groups from Persistant store
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Group"];
[r setIncludesPendingChanges:NO];
r.predicate = [NSPredicate predicateWithFormat:#"cashID IN %#",newIds];
NSArray *existingGroups = [self.moc executeFetchRequest:r error:&error];
// Deleting groups which already are in database
for (Group* g in existingGroups) {
Group* newGroup = groups[g.cashID];
g.name = [newGroup valueForKey:#"name"];
g.cashID = [newGroup valueForKey:#"cashID"];
g.caseInsensitiveName = [[newGroup valueForKey:#"name"] lowercaseString];
[self.moc deleteObject:newGroup];
}
// Saving Entity modification date and setting it to pull to refresh
[self saveModificationDate:[NSDate date] forEntityNamed:#"Group"];
[self.pullToRefreshView.contentView setLastUpdatedAt:[self getModificationDateForEntityNamed:#"Group"]
withPullToRefreshView:self.pullToRefreshView];
// Save groups to presistant store
if (![self.moc save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[[self fetchedResultsController] performFetch:&error];
[self.pullToRefreshView finishLoading];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Show alert with info about internet connection
[self.pullToRefreshView finishLoading];
UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:#"Ups!" message:#"Wygląda na to, że nie masz połączenia z internetem" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[internetAlert show];
}];
}
So when I start collecting data (first run or push to refresh) this method is blocking UI.
I want to avoid this but when I put the success block into another dispatch_async and get back to main queue only for [self.tableView reloadData] I face problem with saving to persistent store or something with bad indexes.
How can I do this whole thing in background and leave UI responsive to the user?
Just an idea, give it a try using dispatch_sync. Have a look at this explanation here where log result something similar to your need. Put [yourTableView reloadData] after synchronous block.
Hope it helps!
It seems AFNetwork call is not async so just try to call your method via performselector.