I am working on an iOS app with a chat function. I want to know if there is any resource to configure the XMPPFramework in order to connect my iOS app with Openfire server.
I am new to XMPP Protocol.
I am currently learning about XMPP Stream and Rosters but I need to at least get the connection to work.
Please help.
To get yourself started, dive into the sample iOS project in XMPPFramework-master > Xcode > iPhoneXMPP.
Preferably, begin tweaking in the project itself, and get your understanding from there before moving on to create your own XMPP project.
Basically to connect XMPP to OpenFire server, most of the configurations lie in AppDelegate.
Set your OpenFire server's details in the XMPP setup:
- (void)setupStream
{
...
// Specify your server's IP address
[xmppStream setHostName:#"123.12.123.12"];
// Specify your host port
[xmppStream setHostPort:5222];
}
Assuming you have already created a contact in your OpenFire's roster, set a contact's credentials in the XMPP connection method:
- (BOOL)connect
{
/**
* Of course, do not hardcode in an actual implementation
* Appending the server name at the back of user ID is necessary
*/
myJID = #"user#openfire";
myPassword = #"password goes here";
}
Ensure you call the connect method in app launch method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self connect];
}
Ensure you are connected here:
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
NSLog(#"User Connected");
// You are connected to the server at this point.
}
Ensure you are authenticated here:
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
NSLog(#"User Authenticated");
/**
* Once you've reached this point,
* Check your server for the online users.
* You should now be seen as "available".
* Cheers!
*/
}
Related
I am using Slack with SupportKit v2.9.0. I want to be able to retrieve messages and media files from other users in other channels.
(BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[SupportKit initWithSettings:
[SKTSettings settingsWithAppToken:#"eknlh3uw8lrdveknlh3uw8lrdvs49xj29fqahcs49xj29fqahc"]];
[SupportKit setUserFirstName:#"Han" lastName:#"Solo"];
return YES;
}
I am calling SupportKit in my app with:
[SupportKit show];
When the view loads I am able to send and retrieve messages but the name of the user doesn't appear. I would also like to fetch messages from other users of my app into this same conversation.
Update:
I have made the following change
(void) viewDidLoad {
[super viewDidLoad];
[SKTUser currentUser].firstName = #"Doctor";
[SKTUser currentUser].lastName = #"Who";
[[SKTUser currentUser] addProperties:#{ #"nickname" : #"Lil Big Daddy Slim", #"weight" : #650, #"premiumUser" : #YES }];
[SupportKit show];
}
This is what I see in my email:
and this in my app:
But I am still unable to see the name of the user appear in the conversation.
To set the user name and profile you can use
[SKTUser currentUser].firstName = #"Doctor";
[SKTUser currentUser].lastName = #"Who";
[[SKTUser currentUser] addProperties:#{ #"nickname" : #"Lil Big Daddy Slim", #"weight" : #650, #"premiumUser" : #YES }];
See this doc for more details http://docs.supportkit.io/#identifying-your-users
Note that SupportKit conversations are meant to be between an app user and an app maker. Having conversation between two app users is not something that is supported at the moment.
SupportKit cannot be used as a chat client between App users, if that's what you mean. I think what you might be looking for is something along the lines of ChatSecure. However, that won't let you as an App Maker contribute to the conversation between users.
According to the Apple documentation :
If that service name conflicts with an existing service on the network, Bonjour chooses a new name. ... your service is automatically renamed if it conflicts with the name of an existing service on the network
How can I achieve this function?
My implementation:
self.publishService = [[NSNetService alloc] initWithDomain:#"local." type:#"_http._tcp." name:#"MyName" port:80];
self.publishService.delegate = self;
[self.publishService publish];
- (void)netServiceDidPublish:(NSNetService *)sender {
NSLog(#"did publish: %#", sender.name);
}
- (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict {
NSLog(#"did not publish: %#", errorDict);
}
When this service is already published the didNotPublish delegate method will be called. I assume the service won't be published?
I thought that the netServiceDidPublish: delegate method should be called again for the service with the new name, but it isn't.
The problem here is the port, not the name.
If there is a name collision, it automatically renames the service by appending " (2)" to the name, increasing the number as far as it needs to. In this case it will publish successfully and call netServiceDidPublish:.
If the port is already used by another published service it won't publish it, and will call netService:didNotPublish: with the error dictionary set to NSNetServicesErrorCode = 48; NSNetServicesErrorDomain = 1;.
All the codes I use are from https://github.com/robbiehanson/XMPPFramework. Inside the sample code.
In my iOS7 messaging app, I invoke the "connect" function inside XMPP Framework after the user has entered their login credential and clicked the "login" button. The connect function works fine if they entered the correct credentials the first time, but would not work if the user entered the wrong credential. Because this very first line inside connect would simply return TRUE:
if (![_xmppStream isDisconnected]) {
return YES;
}
Which means any further presses on the login button would do nothing.
Should I manually invoke authenticateWithPassword? Is this the right practice assuming a connection between the client and the server has been setup?
Thank you.
You need to use the methods in the delegate to handle authentication. First you need to connect to the server if it's not already connected:
[_xmppStream connectWithTimeout:10 error:&error];
Once the stream is connected to the server the delegate method will be invoked:
- (void)xmppStreamDidConnect:(XMPPStream *)sender;
Inside that method, you can call authenticateWithPassword. If the stream was previously connected (would be the else part of the if you posted) you can just call authenticateWithPassword.
If authentication fails, the following delegate method is called:
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error;
There you can decide to show a message to the user and start over. If authentication succeeds, the following method is called:
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender;
I think the title is illustrating enough, but here's the story:
I'm new to XMPPFramework for iOS, and I want to set my client to automatically accept any subscription request it receives. So that other clients can see this client's presence status, when they request it.
According to developer comments In XMPPRoster.h file, there's this method which is called when a subscription request is received:
/**
* Sent when a presence subscription request is received.
* That is, another user has added you to their roster,
* and is requesting permission to receive presence broadcasts that you send.
*
* The entire presence packet is provided for proper extensibility.
* You can use [presence from] to get the JID of the user who sent the request.
*
* The methods acceptPresenceSubscriptionRequestFrom: and rejectPresenceSubscriptionRequestFrom: can
* be used to respond to the request.
**/
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence;
But it is not implemented in XMPPRoster.m. So I implemented it as following :
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
{
[self acceptPresenceSubscriptionRequestFrom:[presence from] andAddToRoster:YES];
}
Since I'm new to XMPPFramework I dunno if I have done anything wrong, but I still cannot get this client's presence in other clients.
I also have seen similar topics like Accept buddy request in xmpp client iphone or Xmpp Accepting buddy request but the solution does not seem to be even related !
Any suggestions is really appreciated.
Thanks.
You did it wrong. You do not have to implement something in XMPPRoster.m or other library files.
This function
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence;
is a callback fired when your xmpp client receives presence subscription request. If you want to execute some code when this callback fired you have to implement a protocol called XMPPRosterDelegate. Protocol is a feature like interface in Java and C# or like abstract class in C++. You have to have a class that inherits from this XMPPRosterDelegate and finally implements this function (and other functions if you want so).
If you want to autoaccept all requests you have to implement your protocol function implementation like this:
-(void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
[sender acceptPresenceSubscriptionRequestFrom:[presence from] andAddToRoster:YES];
}
Also roster object got to know who is its delegate (an object who implements XMPPRosterDelegate), cause if you want to send someone a message you have to know two things: target and selector. Selector is specified in protocol. Target is a delegate property. You have to set roster's delegate during its initialization. In my code I added line
[xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
before line
[xmppRoster activate:xmppStream];
Of course self implements XMPPRosterDelegate and especially has this piece of code
-(void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
[sender acceptPresenceSubscriptionRequestFrom:[presence from] andAddToRoster:YES];
}
Good luck and sorry for long post.
XMPPFramework provides an extension named XMPPReconnect for "accidental disconnections" and automatically reconnects the stream.
This works well on the setting of the normal connection:
[xmppStream connect:&error]
[xmppStream setHostPort:5222];
allowSelfSignedCertificates = NO;
allowSSLHostNameMismatch = NO;
but not for this Old School SSL connection:
[xmppStream oldSchoolSecureConnect:&error]
[xmppStream setHostPort:5223];
allowSelfSignedCertificates = YES;
allowSSLHostNameMismatch = YES;
The error libxmlErrorDomain as error code 4 with the description Document is empty was thrown continuously,
sometimes, the error GCDAsyncSocketErrorDomain as error code 4 with description Read operation timed out also thrown.
Please suggest me the way to make the XMPPReconnect work on the Old School SSL connection.
P.S. The XMPP server is openfire and the PLAIN mechanism for authentication.
I don't think the Reconnect Extension of the XMPPFramework knows about old school ssl connection. IMHO you will have to modify 'XMPPReconnect.m' function 'maybeAttemptReconnectWithReachabilityFlags:' to do something like:
if(self.usesLegacyConnect)
[xmppStream oldSchoolSecureConnect:nil];
} else {
[xmppStream connect:nil];
}