User list for pubnub-chat objective c - ios

I have application where pubnub chat feature is been used.
I want to perform below feature
1)register / login user in pubnub programatically for chat
2)Get list of the all the users
3)Make friend and send 1st message to the user
I am aware about how to create the channel. I had created channel by below code :
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:#"pub-c-XXXXXXXXXXXX-a2bf-XXXX-XXXX-XXXXXXXXXXXX"subscribeKey:#"sub-c-XXXXXXXXXXXX-02d0-XXXX-XXXX-XXXXXXXXXXXX"];
self.client = [PubNub clientWithConfiguration:configuration];
//[self.client addListener:self];
[self.client subscribeToChannels: #[#"my_channel"] withPresence:YES];
I get the channel architecture by : http://pubnub.github.io/pubnub-design-patterns/2015/03/05/Inbound-Channel-Pattern.html
But how do i get the list of the users and their channel and send the message.
I also found this : https://www.pubnub.com/docs/ios-objective-c/presence-sdk-v4
but this only show the friends status. whether they are online / offline by
Join
leave
timeout
Please advice and help
Code to find the list of the users are below but still is show the ZERO users:
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:#"pub-c-XXXXXXXXXXXX-a2bf-XXXX-XXXX-XXXXXXXXXXXX"subscribeKey:#"sub-c-XXXXXXXXXXXX-02d0-XXXX-XXXX-XXXXXXXXXXXX"];
self.client = [PubNub clientWithConfiguration:configuration];
//Subscription process results arrive to listener which should adopt to PNObjectEventListener protocol and registered using:
[self.client addListener:self];
//Listeners callbacks:
[self.client subscribeToChannels: #[#"XXX"] withPresence:YES];
configuration.uuid = #"XXX";
[self.client hereNowForChannel:#"XXX" withVerbosity:PNHereNowState
completion:^(PNPresenceChannelHereNowResult *result,
PNErrorStatus *status) {
// Check whether request successfully completed or not.
if (!status.isError) {
NSLog(#"list of users %#", result.data.uuids);
NSLog(#"list of result.data.occupancy %#", result.data.occupancy);
// result.data.uuids - dictionary with active subscriber. Each entry will have next
// fields: "uuid" - identifier and "state" if it has been provided.
// result.data.occupancy - total number of active subscribers.
}
// Request processing failed.
else {
NSLog(#"FAIL");
// Handle presence audit error. Check 'category' property to find
// out possible issue because of which request did fail.
//
// Request can be resent using: [status retry];
}
}];
}

PubNub Here Now with State
You need to call hereNowForChannel with withVerbosity:PNHereNowState to get the user's state along with all the subscribed users.
The Objective-C SDK API Reference Docs for hereNowForChannel has more details.

Looking at the code, if you always set the UUID to #"XXX" you will only ever have 1 user. The Presence add-on for PubNub uses the UUID property to track active users on a channel, and if you only use 1 UUID PubNub will treat every client as the same client.
Here is our knowledge base article on setting the UUID
https://www.pubnub.com/knowledge-base/discussion/138/how-do-i-set-the-uuid
Also, the iOS PubNub SDK will create and reuse the UUID automatically. This is a new feature as of the 4.2 sdk.
https://www.pubnub.com/docs/ios-objective-c/api-reference-sdk-v4#uuid_example_2

Related

iOS Firebase CrashlyticsKit not setting user ID

I am trying to set a user ID for Firebase Crashlytics reports.
Currently I send a user ID only if it's not sent or is changed (very rare event). And there is no user ID in crash reports.
My code:
+ (void)setCrashlyticsUserData:(User *)user
{
if (user == nil) { return; }
NSString *userIdKey = #"CRASHLYTICS_SENT_USER_ID";
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
NSInteger sentUserId = [userDefaults integerForKey:userIdKey];
if (sentUserId == user.userId) { return; }
[CrashlyticsKit setUserIdentifier:[NSString stringWithFormat:#"%i", user.userId]];
[userDefaults setInteger:user.userId forKey:userIdKey];
}
If this line is commented if (sentUserId == user.userId) { return; } I receive a user ID in crash reports.
Should I call [CrashlyticsKit setUserIdentifier:] every app launch? I can't find any information about it in the documentation.
Custom attributes of Crashlytics (like custom keys or user identifier) works in log-style in per-session basis.
So, you should call setUserIdentifier in each app session as early as possible.
See this link for code example:
https://fabric.io/kits/ios/crashlytics/features
You can use [Crashlytics setUserIdentifier:] to provide an id number, token, or hashed value that uniquely identifies the end-user of your application without disclosing or transmitting any of their personal information. You can also clear the value by setting it to a blank string. This value is displayed right in the Crashlytics dashboard.
[CrashlyticsKit setUserIdentifier:#"123456789"]; //User ID
Or another option is setthe custom key using [CrashlyticsKit setObjectValue:forKey:], See the following example.
[CrashlyticsKit setIntValue:3 forKey:#"userid"];
[CrashlyticsKit setObjectValue:#"logged_in" forKey:#"last_UI_action"];
See this document for more information.
https://docs.fabric.io/apple/crashlytics/enhanced-reports.html

XMPP I need to send Typing state via XMPP Chat on specific chat Dialogue

My Requirement is that two users have 2 or more than 2 different dialogue but user are same. so when one can type at that time another one get typing state in all chat dialogue associated with that. bellow is my code on the Sent method
-(void)xmpp_SendTypingNotification:(BOOL)isTyping toFriendID:(NSString*)recieverId{
XMPPMessage * xMessage = [[XMPPMessage alloc] initWithType:#"chat" to:[XMPPJID jidWithString:strAddDomain(recieverId)]];
isTyping?[xMessage addComposingChatState]:[xMessage addInactiveChatState];
[self.xmppStream sendElement:xMessage];
}
or receive code is
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
if ([message isErrorMessage]) {
return;
}
if ([message hasChatState]){
if (!_arrTypingUsersIDs) {
_arrTypingUsersIDs=[NSMutableArray new];
}
NSString *otherUserID=[NSString stringWithFormat:#"%#",[message.fromStr componentsSeparatedByString:#"#"][0]];
NSInteger typingStatus=[message hasComposingChatState]?1:0;
[self xmpp_setTypingStatus:typingStatus ofUser:otherUserID];
}
}
you have to use XEP-0085 for sending and receiving chat states(like is typing or pause typing).
In XMPPFramework if you want to send is typing chat state just send a message and use message.addaddComposingChatState() to add composing state to a message.
When you receive it in your second client you can check if the message is chat state or not using message.hasChatState or message.message.hasComposingChatState or hasPausedChatState
The reason why it works this way is because you send this status every time to other user:
[XMPPJID jidWithString:strAddDomain(recieverId)]]
and not in context of particular dialog.
If you have a 1-1 chat then your solution is right for it.
If you have a group chat (by using XEP-0045), then you need to send the typing statuses to group room JID:
XMPPMessage *xMessage = [[XMPPMessage alloc] init];
isTyping ? [xMessage addComposingChatState] : [xMessage addInactiveChatState];
[someXMPPRoom sendMessage:xMessage];
So only users in this particular chat room will receive your statuses
Here is a link to XEP-0045 implementation at XMPPFramework

How to identify opponent user enable or disable video in quickblox ios

How to identify opponent user enable or disable video in Quickblox SDK. Please give the solution or delegate methods
You can check this with the WebRTC Stats reporting.
To start collecting report information do the following:
[QBRTCConfig setStatsReportTimeInterval:5]; // 5 seconds
And classes that adopt QBRTCClientDelegate protocol will be notified with
- (void)session:(QBRTCSession *)session updatedStatsReport:(QBRTCStatsReport *)report forUserID:(NSNumber *)userID {
double audioReceivedBitrate = report.audioReceivedBitrateTracker.bitrate;
double videoReceivedBitrate = report.videoReceivedBitrateTracker.bitrate;
//You can check Bitrate of the received video.
NSMutableString *result = [NSMutableString string];
// Video receive stats.
NSString *videoReceiveFormat = #"VR (recv) %#x%##%#fps | (decoded)%# | (output)%#fps | %#/%# | %#ms\n";
[result appendString:[NSString stringWithFormat:videoReceiveFormat,
report.videoReceivedWidth, report.videoReceivedHeight, report.videoReceivedFps,
report.videoReceivedDecodedFps,
report.videoReceivedOutputFps,
report.videoReceivedBitrate, report.availableReceiveBandwidth,
report.videoReceivedDecodeMs]];
NSLog(#"%#", result);
}
For reference : Quickblox API
Also you can use System Notifications feature from QuickBlox SDK. For example, user will send system message with this event (disable camera) and other users will listen this event and do something needed action (guide for Android)

Upload Youtube video with "fixed" token in Google APIs Client Library for Objective-C for REST

I'm trying to create an app that upload video on a specified channel, without prompt a login page. I'll try to explain better what i need.
I'm using Google APIs Client Library for Objective-C for REST, with this library i can use a "standard" upload flow :
user record a video -> he press an upload button -> Safari open the login google page -> user login in his own account and give permission to the app -> Safari redirect back to the ios app -> the upload process begin -> the video will be uploaded on the personal user channel.
Instead this is the desired workflow of my ios app:
user record a video -> he press an upload button -> the video will be uploaded in the app's youtube channel.
The only help i had find is this article ,it explain a way to obtain a refreshable app token to upload video in an app channel. It is exactly what i need. Anyway this is a web example, it uploads videos that are in a server. My videos are in the phone, so i think that i have to modify the flow of this article in this way:
obtain token the first time by login as channel owner -> create a token.txt and save it in my server -> create a page called get_token.php that print the content of token.txt and refresh it if the token expire.
With this flow in my app i need this other flow:
user record a video -> press an upload button -> i made a call to get_token.php and retrive the actual token -> i made a call by library with the token retrived to upload the video on the app's youtube channel.
Here i have found some problems, this is my authentication methods :
#pragma mark - Sign In
- (void)authNoCodeExchange {
[self verifyConfig];
NSURL *issuer = [NSURL URLWithString:kIssuer];
[self logMessage:#"Fetching configuration for issuer: %#", issuer];
// discovers endpoints
[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {
if (!configuration) {
[self logMessage:#"Error retrieving discovery document: %#", [error localizedDescription]];
return;
}
[self logMessage:#"Got configuration: %#", configuration];
if (!kClientID) {
[self doClientRegistration:configuration
callback:^(OIDServiceConfiguration *configuration,
OIDRegistrationResponse *registrationResponse) {
[self doAuthWithoutCodeExchange:configuration
clientID:registrationResponse.clientID
clientSecret:registrationResponse.clientSecret];
}];
} else {
[self doAuthWithoutCodeExchangeCri:configuration clientID:kClientID clientSecret:nil];
}
}];
}
/////////////////
- (void)doAuthWithoutCodeExchangeCri:(OIDServiceConfiguration *)configuration
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret {
NSURL *redirectURI = [NSURL URLWithString:kRedirectURI];
OIDTokenRequest *tokenExchangeRequest =
[_authState.lastAuthorizationResponse tokenExchangeRequest];
[OIDAuthorizationService performTokenRequest:tokenExchangeRequest
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable error) {
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
[_authState updateWithTokenResponse:tokenResponse error:error];
GTMAppAuthFetcherAuthorization *gtmAuthorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
// Sets the authorizer on the GTLRYouTubeService object so API calls will be authenticated.
self.youTubeService.authorizer = gtmAuthorization;
// Serializes authorization to keychain in GTMAppAuth format.
[GTMAppAuthFetcherAuthorization saveAuthorization:gtmAuthorization
toKeychainForName:kGTMAppAuthKeychainItemName];
[self uploadVideoFile];
}];
}
i have also something like:
NSString * RetrivedToken = #"ya29xxxxxxx3nJxxxxxxx6qqQ-FxxxxxxxdGH";
How i can modify those methods to accept my retrivedtoken instead the one they retrive from the standard auth workflow?
This isn't really an answer but its going to be to big for a comment.
So you want to create an app that will allow others to upload to your youtube channel. Normally i would say use a service account which would allow you to do this much easier. However the YouTube api does not support service accounts.
You are going to need to authenticate the app once save your refresh token then embed this refresh token in your app.
Why this is not a good idea with a mobile app.
Refresh tokens can expire so if it does expire or break then your going to have to authenticate the app again and embed the new refresh token into your app and release a new version to your users.
An alternative would be to set up a web service with the refresh token on that and have your app access the web service for upload. Then if the refresh token breaks you will only have to fix it on your web service.
I have done this before its messy but there is really no other way of doing it.
You can see from your code
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
your are using OIDTokenResponse object tokenResponse in your further code.
First make an object of Class OIDTokenResponse like *oidTokenResponse and assign it your access token from your server like
oidTokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
and then use
[_authState updateWithTokenResponse:oidTokenResponse error:error];
In other way you can also do that and you won't need to change your code much you can do it with one line of code but i'm not sure you are allow to change to access token here but you can try it.
Leave the other code and just add one more line in your else
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
tokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
}

Google Play Games not allowing user to join turn-based match

I create a turn-based match and proceed to invite a single opponent as follows:
GPGMultiplayerConfig *config = [[GPGMultiplayerConfig alloc] init];
// We will automatically match with one other player
config.invitedPlayerIds = #[self.opponent.googlePlayID];
config.minAutoMatchingPlayers = 0;
config.maxAutoMatchingPlayers = 0;
[GPGTurnBasedMatch
createMatchWithConfig:config
completionHandler:^(GPGTurnBasedMatch *match, NSError *error) {
if (error) {
completion(NO);
return;
}
}];
After this device places the first move and passes the next turn to my opponent device, my opponent device receives the push notification to join the match. I respond by joining. At this point my self.match.userMatchStatus for this invited device is invited:
[self.match joinWithCompletionHandler:^(NSError *error) {
if (error) {
completion(NO);
return;
}
}];
This doesn't give an error. Upon calling self.match.isMyTurn, I get back YES. A call to self.match.userMatchStatus gives the status of invited; not joined. The documentation (which is incredibly poor, by the way) states that this joinWithCompletionHandler: method:
Joins a turn-based match that the player has been invited to.
Even when adding a dispatch time delay in of 3 seconds after this, to give it a chance, I find that it's still set to invited. Calling further methods, such as takeTurnWithNextParticipantId:data:results:completionHandler:, fails with an entirely undocumented error:
Error Domain=com.google.GooglePlayGames Code=3 "The operation couldn’t
be completed. (com.google.GooglePlayGames error 3.)"
Here's a link to Google's documentation:
https://developers.google.com/games/services/ios/api/interface_g_p_g_turn_based_match
I guess you are passing the player id instead of participant id to takeTurnWithNextParticipantId. The error code 3 (and http response code 400) means that something in passed parameters is invalid, in my case it was the participant id which i had set wrong.

Resources