Chat Application Using Xmpp Swift Tutorial

The final project is available for download from the Github repository

What is XMPP?

Extensible Messaging and Presence Protocol (XMPP) is an open XML technology for real-time communication as explained in the official site info section.

Main Benefits of using it is its feature such as Presence to notify user state as well as being extensible therefore allowing you to extend its capibility to send Images and Video too. Other solutions do not provide such advance features and to add user states such as typing/away is very hard to implement yet core to a chat app. Therefore, XMPP is a good choice for chat apps.


Roadmap of Tutorial

Before we move on to explaining things and to code, a little review of what this tutorial will cover will be good.

1) How To Run A Ejabberd Server On Your Mac/Localhost

We will be running a ejabberd xmpp server for our chat app. Steps to install and configure it will be given.

2) Registering A User Through Terminal

It’s good to get familiar with the working of server using terminal for future use. You can read and explore more on ejabberd site.

3) Using/Registering Account On Adium (A Jabber Client)

To be able to chat between two user, we need to have our app (first client) and another client for user2. Adium will act as the second user

4) Log In The User On The Server With The iOS App

Connecting and Login with the server plus handling errors will be explained.

5) Sending To/Fro Message To The Other User Through iOS App.

This part will have us code for messages coming from other user as well as sending message from the app.


Step 1. Downloading ejabbered

We will be using ejabbered server for our app.
There are several xmpp servers available out there, Openfire being a popular alternative.

Go ahead and download the ejabbered community server installer from here.

• Run the Setup

ejabbered setup

• Install it to the Default Path which will be something like Applications/ejabbered/ on the Mac OS X.

• For the server name - you can use localhost

Server Name

• Use Admin as the admin name and 12345 for the password for now

admin name

password

• ejabbered will install now. Click done at the final screen.

ejabber installing


Step 2. Registering A User Through Terminal

Steps are also explained here for your reference https://docs.ejabberd.im/developer/install-osx/.

• Open a Terminal folder and cd into the ejabberd path.

For version 16.06 in Mac OS X. The command will be cd /Applications/ejabberd-16.06

ejabbered path in terminal

• To run the server use the command. sbin/ejabberdctl live

ejabbered path in terminal

• To register the user -> use the syntax register username servername pass.
The one i have used bin/ejabberdctl register user1 localhost pass

ejabbered path in terminal


Step 3. Using/Registering Account On Adium (A Jabber Client)

Since our server is setup using a XMPP based protocol, we will be using a jabber client to connect to our server and check out how it performs. Go ahead and download Adium from https://adium.im/.

Install it and launch it, close any account assitant setup if it comes. Now you will see a screen something like this :

Adium

• Now open Adium preferences from the top menu.

Adium Preference

• In Account click the add button at the bottom left and select xmpp jabber account

Adium Xabber account

• Enter the username@host and then a pass - i kept 123

Username pass

• A alert will come with the text “Requested URL was not found on this server”: Click ok. Now in the server - enter the host name i.e localhost in our case

Username pass

Clicking request new account will now register this and you will have the adium client setup for this.


Step 4. Log In The User On The Server With The iOS App

Now with our server setup and adium running with the second user, time to move to Xcode to build the chat app.

Open up Xcode and create a new project named XMPP. We will be using swift and cocoapods in the project so make sure to be versed in them before progressing.

Setup Pods

Init the pod for the project. Below is the podfile that i used

use_frameworks!
target 'XMPP' do

pod 'XMPPFramework', :git => "https://github.com/robbiehanson/XMPPFramework.git", :branch => 'master'

end

Open up the workspace xcodeProj now.

ViewController Code

First import the XMPPFramework.

    
import XMPPFramework

class ViewController: UIViewController, XMPPStreamDelegate {

    var stream:XMPPStream!
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        stream = XMPPStream()
        stream.addDelegate(self, delegateQueue: dispatch_get_main_queue())
      
        stream.myJID = XMPPJID.jidWithString("user1@localhost")
        
        do {
            try stream.connectWithTimeout(30)
        }
        catch {
            print("error occured in connecting")
        } 
    }

The code is very basic. We first declare a variable stream of type XMPPStream in global scope.

Further, we initialize the stream in viewDidLoad method, add the delegate so that we can receive event callbacks for connection successful or any failure.

We will set a JID so that we can uniquely identify our user. Since we have already registered one user using terminal, we will use the user1@localhost here.

Next we call the try stream.connectWithTimeout(30) method to actually connect to our ejabberd server.

Adding the Delegate methods

    func xmppStreamWillConnect(sender: XMPPStream!) {
        print("will connect")
    }
    
    func xmppStreamConnectDidTimeout(sender: XMPPStream!) {
        print("timeout:")
    }
    
    func xmppStreamDidConnect(sender: XMPPStream!) {
        print("connected")
        
        do {
            try sender.authenticateWithPassword("123")
        }
        catch {
            print("catch")
            
        }

    }
    
    
    func xmppStreamDidAuthenticate(sender: XMPPStream!) {
        print("auth done")
        sender.sendElement(XMPPPresence())
    }
    

    func xmppStream(sender: XMPPStream!, didNotAuthenticate error: DDXMLElement!) {
        print("dint not auth")
        print(error)
    }

Most of the above methods are self explanatory. In the xmppStreamDidConnect method, we try to authorize our user with the password. Here we use 123 as the password which we had set when registering the user.

Run the app and if everything has been followed correctly, you will see the auth Done log in the console.


Step 5. Sending To/Fro Message To The Other User Through iOS App.

Declare a few more variables

  let xmppRosterStorage = XMPPRosterCoreDataStorage()
  var xmppRoster: XMPPRoster!

These will be used to maintain the roster of our user account. That way you can maintain which users you have contacted with previously and many more things.

   // new code
   xmppRoster = XMPPRoster(rosterStorage: xmppRosterStorage)
   
   // previous code
   stream = XMPPStream()
   stream.addDelegate(self, delegateQueue: dispatch_get_main_queue())
   
   // new code
   xmppRoster.activate(stream)
   
   let button = UIButton()
   button.backgroundColor = UIColor.redColor()
   button.setTitle("SendMessage", forState: .Normal)
   button.frame = CGRectMake(90, 100, 300, 40)
   button.addTarget(self, action: #selector(self.sendMessage), forControlEvents: .TouchUpInside)
  
   self.view.addSubview(button)

Now in viewDidLoad add the new code which is highlighted above using //new code comments.
We initialize our xmppRoster, then we activate the stream on it. A button is added which will simply be used to send message to other user.

sendMessage function is shown below:

    func sendMessage() {
        let message = "Yo!"
        let senderJID = XMPPJID.jidWithString("user2@localhost")
        let msg = XMPPMessage(type: "chat", to: senderJID)
        
        msg.addBody(message)
        stream.sendElement(msg)
    }

here we create a instance of XMPPMessage class and the message type to be of chat (there are many other types of message types like subscribe which you will see shortly) and for the senderJID, we use the user2 JID which we have running in Adium. Using the stream object we send the message.

Implementing Presence

Last important thing to do is to have the presence implemented so that we can recieve messages too. XMPP is based on presence and stanza concept which you can read further on wiki or its official site.

For user2 to contact us, we need to detect the presence events it send and act accordingly. Implement the following delegate method

func xmppStream(sender: XMPPStream!, didReceivePresence presence: XMPPPresence!) {
        print(presence)
        let presenceType = presence.type()
        let username = sender.myJID.user
        let presenceFromUser = presence.from().user
        
        if presenceFromUser != username  {
            if presenceType == "available" {
                print("available")
            }
            else if presenceType == "subscribe" {
                self.xmppRoster.subscribePresenceToUser(presence.from())
            }
            else {
                print("presence type"); print(presenceType)
            }
        }
   
    }

Here we filter out the presenceType, stream JID username and then the presence sender username. This is done to filter out any presence from the same user to itself. Next we detect few individual presenceType such as available and subscribe. First time the user2 tries to contact us: we will recieve a subscribe presence and until we add that user to our roster, the message will not be recieved. Therefore, in the subscribe presence we add the presence sender to our roster and then the presence can be worked upon to retrieve messages.

Conclusion

Thank you for reading this far. For any questions you are welcome to ask in our chat room.

For further info

XMPPFramework

XMPP Wikipedia

Next Steps

Please take a look at our other tutorials :)