{"id":13903147,"url":"https://github.com/processone/xmpp-messenger-ios","last_synced_at":"2025-04-09T17:24:05.030Z","repository":{"id":33167598,"uuid":"36808622","full_name":"processone/xmpp-messenger-ios","owner":"processone","description":"iOS XMPP Messenger Framework","archived":false,"fork":false,"pushed_at":"2018-04-21T19:09:32.000Z","size":4421,"stargazers_count":220,"open_issues_count":27,"forks_count":93,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-04-02T11:53:53.637Z","etag":null,"topics":["xmpp","xmpp-client","xmppframework"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/processone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-06-03T14:21:21.000Z","updated_at":"2025-03-22T21:22:29.000Z","dependencies_parsed_at":"2022-08-17T21:15:20.977Z","dependency_job_id":null,"html_url":"https://github.com/processone/xmpp-messenger-ios","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/processone%2Fxmpp-messenger-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/processone%2Fxmpp-messenger-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/processone%2Fxmpp-messenger-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/processone%2Fxmpp-messenger-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/processone","download_url":"https://codeload.github.com/processone/xmpp-messenger-ios/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248075519,"owners_count":21043602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["xmpp","xmpp-client","xmppframework"],"created_at":"2024-08-06T22:01:39.730Z","updated_at":"2025-04-09T17:24:05.014Z","avatar_url":"https://github.com/processone.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"# XMPP-MESSENGER-IOS\n\n[![Version](https://img.shields.io/cocoapods/v/xmpp-messenger-ios.svg?style=flat)](http://cocoapods.org/pods/xmpp-messenger-ios)\n[![License](https://img.shields.io/cocoapods/l/xmpp-messenger-ios.svg?style=flat)](http://cocoapods.org/pods/xmpp-messenger-ios)\n[![Platform](https://img.shields.io/cocoapods/p/xmpp-messenger-ios.svg?style=flat)](http://cocoapods.org/pods/xmpp-messenger-ios)\n\n## Installation\n\nxmpp-messenger-ios is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your ```Podfile```:\n\n```ruby\npod \"xmpp-messenger-ios\"\n```\n\nIf you can't use cocoapod, you will have to download the files and add it to your ```Xcode```project.\n\n### UPDATE 04/19/16\n\nIf you encounter compilation errors after the installation, you may change the following file:\n\n\n1. In `XMPPFramework/Core/XMPPStream.h`, change `#import \"CocoaAsyncSocket/GCDAsyncSocket.h\"` to `@import CocoaAsyncSocket;`\n\n\n2. In `XMPPFramework/Core/XMPPLogging.h`, change `#import \"CocoaLumberjack/DDLog.h\"` to `@import CocoaLumberjack;`\n\n\n3. In `JSQMessagesViewController/JSQSystemSoundPlayer+JSQMessages.h`, change to `@import JSQSystemSoundPlayer;`\n\n\nA pull request will be made to the XMPPFramework to include thoses changes.\n\n### Disclaimer\nxmpp-messenger-ios was build for ```Xcode 7``` and ```Swift 2```, if you haven't upgraded yet, you should.\n\n## Author\n\nMade in Paris by [ProcessOne](https://www.process-one.net/en/)\n\n## License\n\nxmpp-messenger-ios is available under the MIT license. See the LICENSE file for more info.\n\n# Tutorial example: OneChat\nWe will build a Swift XMPP client built on [XMPP Framework](https://github.com/processone/XMPPFramework), using [xmpp-messenger-ios](https://github.com/processone/xmpp-messenger-ios) and [JSQMessageViewControllerSwift](https://github.com/jessesquires/JSQMessagesViewController)\n\n## Project setup \n1. Open ```Xcode``` and select ```create a new project```, you are free to choose wich kind of template you want, for this example, we will use the ```single view application```. Fill up the required information, select ```Swift``` language and choose the folder location.\n\n![Xcode setup screenshot](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/Xcode setup project.png)\n\n2. Now quit ```Xcode``` and launch the terminal app (terminal.app)\n![terminal screenshot](https://github.com/processone/demo-xmpp-ios/blob/master/Setup%20resources/Capture%20d’écran%202015-07-22%20à%2011.41.50.png?raw=true%20=350x)\n\n3. Navigate to your project directory, and type ```pod init``` like so:\n![terminal podinit](https://github.com/processone/demo-xmpp-ios/blob/master/Setup%20resources/Capture%20d’écran%202015-07-22%20à%2011.42.02.png?raw=true%20=350x)\n\n```pod init``` will setup [cocoapods](https://cocoapods.org) for this project, so make sure that you're using the latest version.\n\n4. Edit the newly created *Podfile* by taping ```emacs Podfile``` (Feel free to use ```vim``` :)). It should look like this:\n\n![Podfile screenshot](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/podfile scree.png)\n\n*Press ctrl+x, ctrl+s to save, then ctrl+x, ctrl+c to end editing*\n\n5. Almost good to go, type ```pod install```, and wait until the installation is over.\n\n![PodInstall screenshot](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/Pod_command line.png)\n\n*From now one you will have to open your project using the ```xcworkspace``` file, and no more the ```xcodeproject``` file*\n\n6. Open the project, add:\n\n```swift\nimport xmpp_messenger_ios\n``` \nto your ``` AppDelegate.swift```  file. Build \u0026 run to confirm that everything went well.\n\n*if you encounter an error, try to deep clean ```Xcode``` (```command+alt+shift+k```)*\n\n\n\n## Let's create your chat client !\n\n**At this point your project should compile without errors**\n\nWe are going to create 4 classes, one to display the conversations, name it ```OpenChatsTableViewController.swift```, one for chatting, name it ```ChatViewController.swift```, one to display the list of contacts, name it ```ContactListTableViewController.swift``` and one to act as a settings page, name it ```SettingsViewController.swift```.\n\n*You can remove or reuse the apple-provided ```ViewController.swift``` file*\n\n\n### Let's start with the Storyboard\n1. Open your ```Main.storyboard``` file, and remove the current ViewController.\n2. Drop in a ```UITableViewController```, and asign it to ```OpenChatsTableViewController.swift```\n![Class asignment](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/Class selection.png)\n\nNow you will select this Controller as the ```initial view controller```\n\n![initialVC screen](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/initial_VC.png)\n\nWe will now embed an ```UINavigationController```: within ```Xcode```, go to ```editor```-\u003e```Embed in```-\u003e```Navigation controller```\n\n![Embed controller](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/embed.png)\n\nNow that we have a ```UINavigationBar``` on ```OpenChatsViewController```, we will add two ```UIBarButtonItem```:\n\n- On the left, select system item ```add```\n- On the right, simply name it \"Settings\"\n\nOnce finished, it should look like this:\n\n![finished screen](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/openchatVC .png)\n\n3. Drop an ```UIViewController```, asign it to ```SettingsViewController.swift```, then embed it inside an ```UINavigationController```.\n\nReturn on ```OpenChatsTableViewController```. Select the ```settings``` ```UIBarButtonItem``` with the ```ctrl``` key and drag the cursor on the ```SettingsViewController```'s ```UINavigationController``` to create a ```segue``` between the ```ViewControllers```.\n\nIt should look like this:\n\n![segue example](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/settings segue.png)\n\nNow select the ```Segue```, and in the properties, name it ```One.HomeToSettings```\n\n![segue naming](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/segue.png)\n\nReturn on ```SettingsViewController```, and on the ```UINavigationBar```, add an ```UIBarButtonItem```, and name it \"Done\".\n\nAlso drop two ```UITextField```, and one ```UIButton```.\n\nName the ```UIbutton```'s title \"validate\" and set the ```UITextField```'s placeholder to \"Username\" and \"Password\".\n\nIt should look like this:\n\n![textfield placeholder](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/texfield username.png)\n\n**Note: you should select -secure entry- on the password's ```UITextField``` to replace every entered letter into dots**\n\n\nYou will now split ```Xcode```'s screen to display side by side both ```interface``` and ```code```, simply by selecting this button:\n\n![button tosplit](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/xcode split.png)\n\nOnce done, ```ctrl``` select the textfield's and validate's button to the class to create ```IBOutlet```'s. Name them respetctivly userNameTextField, passwordTextField and validateButton.\n\n![creating outlets](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/creating outlets.png)\n\nFor the last one, the validate button, you will have to ```ctrl``` drag again, but this time, select ```action``` instead of ```outlet```, and name the ```IBAction``` \"close\".\n\n![creating methods](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/action validate.png)\n\nDo the same operation, but on the \"Done\" ```UIBarButtonItem```, and name the method \"close\"\n\nWe need to do one last operation: setting the ```UITextField```'s ```delegates```. For this, you will have to select a ```UITextField``` at the time, go to the connection tab and click-drag your cursor on the ```SettingsViewController```.\n\n![Setting delegates](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/settin delegate.png)\n\n###This is all about 'connection'\n\n1. Open ```AppDelegate.swift```. We will add to calls in here. One to start the services, and the other one to stop them.\n\nIn:\n\n```swift\nfunc application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -\u003e Bool\n```\nAdd the following code:\n\n```swift\nOneChat.start(archiving: true, delegate: nil) { (stream, error) -\u003e Void in\n\tif let _ = error {\n\t\t//handle start errors here\n\t} else {\n\t\t//Activate online UI\n\t}\n}\n```\n\nAnd in:\n\n```swift\nfunc applicationWillTerminate(application: UIApplication)\n```\nAdd: \n\n```swift\nOneChat.stop()\n```\t\t\n\n2. Go back to ```SettingsViewController.swift```, and add the following ```import``` at the top of the file:\n\n```swift\nimport XMPPFramework\nimport xmpp_messenger_ios\n```\nWe will now take care of the ```UITextField```'s ```delegates```: we will add a method to hide the keyboard if the user if out of focus, and a method to trigger the validation if the user click on the keyboard's return key.\n\nTo hide the keyboard, Add:\n\n```swift\nlet tap = UITapGestureRecognizer(target: self, action: \"DismissKeyboard\")\n```\n\nIn your ```viewDidLoad``` method, then implement the ```DismissKeyboard``` method:\n\n```swift\nfunc DismissKeyboard() {\n\tif usernameTextField.isFirstResponder() {\n\t\tusernameTextField.resignFirstResponder()\n\t} else if passwordTextField.isFirstResponder() {\n\t\tpasswordTextField.resignFirstResponder()\n\t}\n}\n```\n\nAnd to trigger the validation, we'll use the ```UITextField``` delegates:\n\n```swift\nfunc textFieldShouldReturn(textField: UITextField) -\u003e Bool {\n\tif passwordTextField.isFirstResponder() {\n\t\ttextField.resignFirstResponder()\n\t\tvalidate(self)\n\t} else {\n\t\ttextField.resignFirstResponder()\n\t}\n\treturn true\n}\n```\nNow we are going to set default values in the ```UITextfield```'s, and add a way to change the \"validate\" ```UIButton``` if the user is already connected.\n\nAdd the following in your ```viewWillappear```:\n\n```swift\nif OneChat.sharedInstance.isConnected() {\n\tusernameTextField.hidden = true\n\tpasswordTextField.hidden = true\n\tvalidateButton.setTitle(\"Disconnect\", forState: UIControlState.Normal)\n} else {\n\tif NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) != \"kXMPPmyJID\" {\n\tusernameTextField.text = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID)\n\tpasswordTextField.text = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myPassword)\n}\n```\n\nHere we just change the name of the \"validate\" ```UIButton``` and hide the ```UITextField```'s in case the user is already connected, if not, and if the user already gave credentials, we place them in the ```UITextField```.\n\nPlace this inside the ```validate()``` method:\n\n```swift\nif OneChat.sharedInstance.isConnected() {\n\tOneChat.sharedInstance.disconnect()\n\tusernameTextField.hidden = false\n\tpasswordTextField.hidden = false\n\tvalidateButton.setTitle(\"Validate\", forState: UIControlState.Normal)\n} else {\n\tOneChat.sharedInstance.connect(username: self.usernameTextField.text!, password: \tself.passwordTextField.text!) { (stream, error) -\u003e Void in\n\tif let _ = error {\n\t\tlet alertController = UIAlertController(title: \"Sorry\", message: \"An error occured: \\(error)\", preferredStyle: UIAlertControllerStyle.Alert)\n\t\talertController.addAction(UIAlertAction(title: \"Ok\", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -\u003e Void in\n\t\t\t\t//do something\n\t\t}))\n\t\tself.presentViewController(alertController, animated: true, completion: nil)\n\t} else {\n\t\tself.dismissViewControllerAnimated(true, completion: nil)\n\t}\n}\n```\n\nThe ```validate``` method will connect the user and dismiss the settings screen if the ```connect()``` succeed, and if the user is already connected, it will disconnect him and reset the ```UI```\n\n3. Return to the ```Storyboard```, select the ```OpenChatsTableViewController```, then select the ```UITableViewCell``` inside the ```UITableView``` and in the property tabs, fill the ```reuse identifier``` to ```OneCellReuse```:\n\n![reuse screen](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/cell reuse.png)\n\nOpen up the source code, ```OpenChatsTableViewController.swift```, and add the following imports at the top of the file:\n\n```swift\nimport XMPPFramework\nimport xmpp_messenger_ios\n```\n\nThis ```Controller``` will be displaying all the open / stored chat conversation between two or more participants, so we need to set both ```datasource``` and ```delegate``` for the ```UITableView```, as well as a way to know if a new conversation was added to the list.\n\nCreate a ```variable``` witch will contains the conversation:\n\n```swift\nvar chatList = NSArray()\n```\n\nThen, implement the following ```datasource``` methods:\n\n```swift\noverride func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n\treturn OneChats.getChatsList().count\n}\n\noverride func numberOfSectionsInTableView(tableView: UITableView) -\u003e Int {\n\treturn 1\n}\n```\n\nAnd the most important ```delegate```:\n\n```swift\noverride func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -\u003e UITableViewCell {\n\tlet cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(\"OneCellReuse\", forIndexPath: indexPath)\n\tlet user = OneChats.getChatsList().objectAtIndex(indexPath.row) as! XMPPUserCoreDataStorageObject\n\n\tcell!.textLabel!.text = user.displayName\n\n\tOneChat.sharedInstance.configurePhotoForCell(cell!, user: user)\n\n\tcell?.imageView?.layer.cornerRadius = 24\n\tcell?.imageView?.clipsToBounds = true\n\n\treturn cell!\n}\n```\n\nIf, like me, you don't like the extra lines on the ```UITableView```, you can add the following ```delegate```:\n\n```swift\noverride func tableView(tableView: UITableView, heightForFooterInSection section: Int) -\u003e CGFloat {\n\treturn 0.01\n}\n\noverride func tableView(tableView: UITableView, viewForFooterInSection section: Int) -\u003e UIView? {\n\treturn UIView()\n}\n```\n\nLast ```UITableView```'s touch, add ```tableView.rowHeight = 50``` at the end of your ```viewDidLoad```.\n\nYou can now conform the class to the ```OneRosterDelegate```:\n\n```swift\nclass OpenChatsTableViewController: UITableViewController, OneRosterDelegate\n```\n\nAn error should now pop:\n\n![xcode error](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/error.png)\n\nThis is because we **have** to conform to the ```OneRosterProtocol```. Do it by implementing the following method:\n\n```swift\nfunc oneRosterContentChanged(controller: NSFetchedResultsController) {\n\t//Will reload the tableView to reflect roster's changes\n\ttableView.reloadData()\n}\n```\nImplementing a ```delegate``` method is great, but we need to set yourself as the delegate if we want to be notified when the roster content change.\nYou will do this in your ```viewWillAppear``` method:\n\n```swift\nOneRoster.sharedInstance.delegate = self\n```\n\nFollowing the same logic, remove yourself as ```delegate``` in your ```viewWillDisapear``` method:\t\n\n```swift\nOneRoster.sharedInstance.delegate = nil\n```\n\nReturn to the top, in your ```viewWillAppear```, and add the ```connect()``` function, to present the ```SettingViewController``` if the user isn't logged in:\n\n```swift\nOneChat.sharedInstance.connect(username: kXMPP.myJID, password: kXMPP.myPassword) { (stream, error) -\u003e Void in\n\tif let _ = error {\n\t\tself.performSegueWithIdentifier(\"One.HomeToSetting\", sender: self)\n\t} else {\n\t\t//set up online UI\n\t}\n}\n```\n\n### Build \u0026 Run, you should be redirected to the settings page, where you can login sucessfully.\n\n4. Displaying a list of chat conversation is great, but creating one is even better ! Switch back to your ```Storyboard```, and drop a ```UITableViewController```. Asing it to ```ContactListTableViewController.swift```. Embed a ```UINavigationController``` and name it's ```Storyboard ID``` \"contactListNav\":\n\n![storyboard name](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/storyboard id.png)\n\nNow you will create a ```Modal presentation``` segue between the ```+ UIBarButtonItem``` in the ```OpenChatsTableViewController``` and the ```ContactListViewController```. \n\nName this segue \"chat.to.add\" \n\n![chat segue](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/segue naming.png)\n\nReturn to ```ContactListTableViewController``` and drop a ```UIBarButtonItem```, select the ```system style``` \"done\" and create an action method named \"close\":\n\n```swift\n@IBAction func close(sender: AnyObject) {\n\tself.dismissViewControllerAnimated(true, completion: nil)\n}\n```\n\nNow select the ```UITableViewCell``` and set the ```reuse identifier``` like you did for the ```OpenChatsTableViewController```.\n\nLet's take care of this ```ContactListTableViewController```, at the top of the file, add:\n\n```swift\nimport XMPPFramework\nimport xmpp_messenger_ios\n```\n\nLike you did for the ```OpenChatsTableViewController```, add ```OneRosterDelegate``` to your class declaration, set up the delegate in ```viewWillAppear``` and ```viewWillDisapear```, as well as implementing the required protocol method ```oneRosterContentChanged```.\n\nLet's implement the ```UITableViewDelegate``` now:\n\n```swift\noverride func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n\tlet sections: NSArray? =  OneRoster.buddyList.sections\n\n\tif section \u003c sections!.count {\n\t\tlet sectionInfo: AnyObject = sections![section]\n\n\t\treturn sectionInfo.numberOfObjects\n\t}\n\n\treturn 0;\n}\n\noverride func numberOfSectionsInTableView(tableView: UITableView) -\u003e Int {\n\treturn OneRoster.buddyList.sections!.count\n}\n\noverride func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -\u003e String? {\n\tlet sections: NSArray? = OneRoster.sharedInstance.fetchedResultsController()!.sections\n\n\tif section \u003c sections!.count {\n\t\tlet sectionInfo: AnyObject = sections![section]\n\t\tlet tmpSection: Int = Int(sectionInfo.name)!\n\n\t\tswitch (tmpSection) {\n\t\t\tcase 0 :\n\t\t\t\treturn \"Available\"\n\n\t\t\tcase 1 :\n\t\t\t\treturn \"Away\"\n\n\t\t\tdefault :\n\t\t\t\treturn \"Offline\"\n\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\noverride func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -\u003e UITableViewCell {\n\tlet cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(\"OneCellReuse\", forIndexPath: indexPath)\n\tlet user = OneRoster.userFromRosterAtIndexPath(indexPath: indexPath)\n\n\tcell!.textLabel!.text = user.displayName;\n\n\tif user.unreadMessages.intValue \u003e 0 {\n\t\tcell!.backgroundColor = .orangeColor()\n\t} else {\n\t\tcell!.backgroundColor = .whiteColor()\n\t}\n\tOneChat.sharedInstance.configurePhotoForCell(cell!, user: user)\n\n\treturn cell!;\n}\n```\n\nThis will populate the ```UITableView``` with the content of your ```roster```. It will display the name and picture of your contacts, ordered by status *available, away, offline*.\n\n### Build \u0026 Run, tap on the + button to see if everything is working properly.\n\n5. In this section we will add the ability to select a contact and start chatting !\n\nOpen your ```Storyboard``` and drop a ```UIViewController```. Asign it to ```ChatViewController.swift```.\n\nRemove the ```chat.to.add``` segue *from the + button to the ```ContactListTableViewController```* and re-create it from the ```ChatViewController``` itself. Name it \"chat.to.contact\". It should look like this:\n\n![storyboard screnfromconatct](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/first segue.png)\n\nNow you will create two more ```push/show segue```, from ```OpenChatsTableViewController``` to ```ChatViewController```. One from the ```+``` button, you will name it ```chat.to.add```, and the other from the ```UITableViewCell``` of ```OpenChatsTableViewController``` to ```ChatViewController```, name it ```chat.to.chat```. The completed ```Storyboard``` should now look like this:\n\n![completed storyboard](https://raw.githubusercontent.com/processone/xmpp-messenger-ios/master/Tutorial%20assets/completed storyboard.png)\n\nYou can return to ```ChatViewController.swift```. There will be a lot going on in this ```ViewController``` so make sure you're fully loaded with cafeine !\n\nAs usual, add the following ```import``` at the top of the file:\n\n```swift\nimport xmpp_messenger_ios\nimport JSQMessagesViewController\nimport XMPPFramework\n```\n\nThere will be some interesting stuff in that class, we will need a variable to store the recipient, and a library to give us the ```User Interface```.\n\nCreate the recipient variable:\n\n```swift\nvar recipient: XMPPUserCoreDataStorageObject?\n```\n\nIn your ```viewWillAppear``` method, we will check if a recipient has been set, in that case we will display the chat history, if not we’ll present the ```ContactListTableViewCcontroller``` to allow the user to select a contact. It’ll look like this :\n\n```swift\nif let recipient = recipient {\n\tnavigationItem.rightBarButtonItems = []\n\tnavigationItem.title = recipient.displayName\n} else {\n\tnavigationItem.title = \"New message\"\n\n\tnavigationItem.setRightBarButtonItem(UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: \"addRecipient\"), animated: true)\n\taddRecipient()\n}\n```\n\nImplement the ```addRecipient()``` method:\n\n```swift\nfunc addRecipient() {\n\tlet navController = storyboard?.instantiateViewControllerWithIdentifier(\"contactListNav\") as? UINavigationController\n\n\tpresentViewController(navController!, animated: true, completion: nil)\n}\n```\n\nIf you Build and run at this point, you will notice that every time you tap on ```done```, the ```ContactListViewController``` is displayed over and over, and that the cell selection does nothing … yet ! We will use a ```bool``` to solve the first problem, and a ```delegate``` for the second !\n\nAdd the ```var firstTime = true```, then, in your ```viewWillAppear```, encapsulate the ```addRecipient()``` like this :\n\n```swift\nif firstTime {\n\tfirstTime = false\n\taddRecipient()\n}\n```\n\nNow, it’s time to create your ```delegate``` ! Open ```ContactListTableViewController```, and add the following ```protocol``` behind the import’s statement\n\n```swift\nprotocol ContactPickerDelegate {\n\tfunc didSelectContact(recipient: XMPPUserCoreDataStorageObject)\n}\n```\n\nIt will be called whenever an user select a contact in the list.\nAdd the following property to call on :\n\n```swift\nvar delegate:ContactPickerDelegate?\n```\n\nAnd implement it in the ```UITableView didselectRowAtIndex``` method:\n\n```swift\noverride func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {\n\tdelegate?.didSelectContact(OneRoster.userFromRosterAtIndexPath(indexPath: indexPath))\n\tclose(self)\n}\n```\n\nNow that the protocol is setup, go back to ```ChatViewController.swift```, and add ```ContactPickerDelegate``` to the class declaration. Implement the mandatory method ```didSelectContact()```:\n\n```swift\nfunc didSelectContact(recipient: XMPPUserCoreDataStorageObject) {\n\tself.recipient = recipient\n\tnavigationItem.title = recipient.displayName\n}\n```\n\nHowever, it will not work until you set yourself as ```ContactPickerDelegate```. In the ```addRecipient()```, just before ```presentViewController```, add this line:\n\n```swift\nlet contactController: ContactListTableViewController? = navController?.viewControllers[0] as? ContactListTableViewController\ncontactController?.delegate = self\n```\n\nWe will now extend the contact selection, open up ```OpenChatsViewController``` and implement the ```segue delegates```:\n\n```swift\noverride func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -\u003e Bool {\n\tif identifier == \"chat.to.add\" {\n\t\tif !OneChat.sharedInstance.isConnected() {\n\t\t\tlet alert = UIAlertController(title: \"Attention\", message: \"You have to be connected to start a chat\", preferredStyle: UIAlertControllerStyle.Alert)\n\t\t\talert.addAction(UIAlertAction(title: \"Ok\", style: UIAlertActionStyle.Default, handler: nil))\n\n\t\t\tself.presentViewController(alert, animated: true, completion: nil)\n\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\noverride func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {\n\tif segue?.identifier == \"chats.to.chat\" {\n\t\tif let controller = segue?.destinationViewController as? ChatViewController {\n\t\t\tif let cell: UITableViewCell? = sender as? UITableViewCell {\n\t\t\t\tlet user = OneChats.getChatsList().objectAtIndex(tableView.indexPathForCell(cell!)!.row) as! XMPPUserCoreDataStorageObject\n\t\t\t\tcontroller.recipient = user\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\nThe first one will prevent contact selection if the user is offline, the second will fill the ```recipient``` corresponding at the selected cell.\n\n### BUILD \u0026 RUN, you should be able to select a contact !\n\n6. Now go back to ChatViewController, for the final part, the Chat !\n\nAdd ```JSQMessagesViewController``` to the class declaration, and create a variable message witch will hold the fetched message if there is some: \n\n```swift\nvar messages = NSMutableArray()\n```\n\nNow, you will need to implement the ```JSQMessageViewController``` delegates. Thoses methods are pretty simple, they will display and create an interface for the stored messages:\n\n```swift\noverride func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -\u003e JSQMessageData! {\n\tlet message: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\n\treturn message\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -\u003e JSQMessageBubbleImageDataSource! {\n\tlet message: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\n\tlet bubbleFactory = JSQMessagesBubbleImageFactory()\n\n\tlet outgoingBubbleImageData = bubbleFactory.outgoingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleLightGrayColor())\n\tlet incomingBubbleImageData = bubbleFactory.incomingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleGreenColor())\n\n\tif message.senderId == self.senderId {\n\t\treturn outgoingBubbleImageData\n\t}\n\n\treturn incomingBubbleImageData\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -\u003e JSQMessageAvatarImageDataSource! {\n\tlet message: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\n\tif message.senderId == self.senderId {\n\t\tif let photoData = OneChat.sharedInstance.xmppvCardAvatarModule?.photoDataForJID(OneChat.sharedInstance.xmppStream?.myJID) {\n\t\t\tlet senderAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(data: photoData), diameter: 30)\n\t\t\t\treturn senderAvatar\n\t\t\t} else {\n\t\t\t\tlet senderAvatar = JSQMessagesAvatarImageFactory.avatarImageWithUserInitials(\"SR\", backgroundColor: UIColor(white: 0.85, alpha: 1.0), textColor: UIColor(white: 0.60, alpha: 1.0), font: UIFont(name: \"Helvetica Neue\", size: 14.0), diameter: 30)\n\t\t\t\treturn senderAvatar\n\t\t\t}\n\t\t} else {\n\t\t\tif let photoData = OneChat.sharedInstance.xmppvCardAvatarModule?.photoDataForJID(recipient!.jid!) {\n\t\t\t\tlet recipientAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(data: photoData), diameter: 30)\n\t\t\t\treturn recipientAvatar\n\t\t\t} else {\n\t\t\t\tlet recipientAvatar = JSQMessagesAvatarImageFactory.avatarImageWithUserInitials(\"SR\", backgroundColor: UIColor(white: 0.85, alpha: 1.0), textColor: UIColor(white: 0.60, alpha: 1.0), font: UIFont(name: \"Helvetica Neue\", size: 14.0)!, diameter: 30)\n\t\t\t\treturn recipientAvatar\n\t\t\t}\n\t\t}\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellTopLabelAtIndexPath indexPath: NSIndexPath!) -\u003e NSAttributedString! {\n\tif indexPath.item % 3 == 0 {\n\t\tlet message: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\t\treturn JSQMessagesTimestampFormatter.sharedFormatter().attributedTimestampForDate(message.date)\n\t}\n\n\treturn nil;\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -\u003e NSAttributedString! {\n\tlet message: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\n\tif message.senderId == self.senderId {\n\t\treturn nil\n\t}\n\n\tif indexPath.item - 1 \u003e 0 {\n\t\tlet previousMessage: JSQMessage = self.messages[indexPath.item - 1] as! JSQMessage\n\t\tif previousMessage.senderId == message.senderId {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn nil\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -\u003e NSAttributedString! {\n\treturn nil\n}\n\n// Mark: UICollectionView DataSource\n\noverride func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -\u003e Int {\n\treturn self.messages.count\n}\n\noverride func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -\u003e UICollectionViewCell {\n\tlet cell: JSQMessagesCollectionViewCell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath) as! JSQMessagesCollectionViewCell\n\tlet msg: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\n\tif !msg.isMediaMessage {\n\t\tif msg.senderId == self.senderId {\n\t\t\tcell.textView!.textColor = UIColor.blackColor()\n\t\t\tcell.textView!.linkTextAttributes = [NSForegroundColorAttributeName:UIColor.blackColor(), NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue]\n\t\t} else {\n\t\t\tcell.textView!.textColor = UIColor.whiteColor()\n\t\t\tcell.textView!.linkTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor(), NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue]\n\t\t}\n\t}\n\n\treturn cell\n}\n\n// Mark: JSQMessages collection view flow layout delegate\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellTopLabelAtIndexPath indexPath: NSIndexPath!) -\u003e CGFloat {\n\tif indexPath.item % 3 == 0 {\n\t\treturn kJSQMessagesCollectionViewCellLabelHeightDefault\n\t}\n\n\treturn 0.0\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -\u003e CGFloat {\n\tlet currentMessage: JSQMessage = self.messages[indexPath.item] as! JSQMessage\n\t\tif currentMessage.senderId == self.senderId {\n\t\t\treturn 0.0\n\t\t}\n\n\t\tif indexPath.item - 1 \u003e 0 {\n\t\t\tlet previousMessage: JSQMessage = self.messages[indexPath.item - 1] as! JSQMessage\n\t\t\tif previousMessage.senderId == currentMessage.senderId {\n\t\t\t\treturn 0.0\n\t\t\t}\n\t\t}\n\n\treturn kJSQMessagesCollectionViewCellLabelHeightDefault\n}\n\noverride func collectionView(collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellBottomLabelAtIndexPath indexPath: NSIndexPath!) -\u003e CGFloat {\n\treturn 0.0\n}\n```\n\n7. Now we can handle the ```UI```, but we still need to be able to send and receive messages ! Add ```OneMessageDelegate``` to the class declaration, and implement the mandatory ```protocol```:\n\n```swift\nfunc oneStream(sender: XMPPStream, didReceiveMessage message: XMPPMessage, from user: XMPPUserCoreDataStorageObject) {\n\tif message.isChatMessageWithBody() {\n\t\tJSQSystemSoundPlayer.jsq_playMessageReceivedSound()\n\n\t\tif let msg: String = message.elementForName(\"body\")?.stringValue() {\n\t\t\tif let from: String = message.attributeForName(\"from\")?.stringValue() {\n\t\t\t\tlet message = JSQMessage(senderId: from, senderDisplayName: from, date: NSDate(), text: msg)\n\t\t\t\tmessages.addObject(message)\n\n\t\t\t\tself.finishReceivingMessageAnimated(true)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc oneStream(sender: XMPPStream, userIsComposing user: XMPPUserCoreDataStorageObject) {\nself.showTypingIndicator = !self.showTypingIndicator\nself.scrollToBottomAnimated(true)\n}\n```\n\nThe first method will be called whenever a message is received, while the second will be called when the remote user is composing a new message.\n\n### We can now receive messages ! It would be great if we could send some too no ?\n\n8. Add this to your ```viewDidLoad``` method :\n\n```swift\nOneMessage.sharedInstance.delegate = self\n\nif OneChat.sharedInstance.isConnected() {\n\tself.senderId = OneChat.sharedInstance.xmppStream?.myJID.bare()\n\tself.senderDisplayName = OneChat.sharedInstance.xmppStream?.myJID.bare()\n}\nself.inputToolbar!.contentView!.leftBarButtonItem!.hidden = true\nself.collectionView!.collectionViewLayout.springinessEnabled = true\n```\n\nWe will now add a method in your ```viewWillApear``` to fetch stored messages, if there is some:\n\n```swift\nself.messages = OneMessage.sharedInstance.loadArchivedMessagesFrom(jid: recipient.jidStr)\nself.collectionView?.reloadData()\n```\n\nThe completed ```viewWillAppear``` function should now look like this:\n\n```swift\noverride func viewWillAppear(animated: Bool) {\n\tif let recipient = recipient {\n\t\tnavigationItem.rightBarButtonItems = []\n\t\tnavigationItem.title = recipient.displayName\n\n\t\tself.messages = OneMessage.sharedInstance.loadArchivedMessagesFrom(jid: recipient.jidStr)\n\t\tself.collectionView?.reloadData()\n\t} else {\n\t\tnavigationItem.title = \"New message\"\n\n\t\tnavigationItem.setRightBarButtonItem(UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: \"addRecipient\"), animated: true)\n\n\t\tif firstTime {\n\t\t\tfirstTime = false\n\t\t\taddRecipient()\n\t\t}\n\t}\n}\n```\n\nNow, edit ```didSelectContact```, and add the following:\n\n```swift\nif !OneChats.knownUserForJid(jidStr: recipient.jidStr) {\n\tOneChats.addUserToChatList(jidStr: recipient.jidStr)\n} else {\n\tmessages = OneMessage.sharedInstance.loadArchivedMessagesFrom(jid: recipient.jidStr)\n\tfinishReceivingMessageAnimated(true)\n}\n```\n\nIt will fetch the stored message of un user we just select form the roster.\n\n##### The final touch, sending a message ! \n\n8. Implement the ```JSQMessageViewController``` delegate:\n\n```swift\noverride func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!) {\n\tlet fullMessage = JSQMessage(senderId: OneChat.sharedInstance.xmppStream?.myJID.bare(), senderDisplayName: OneChat.sharedInstance.xmppStream?.myJID.bare(), date: NSDate(), text: text)\n\tmessages.addObject(fullMessage)\n\n\tif let recipient = recipient {\n\t\tOneMessage.sendMessage(text, to: recipient.jidStr, completionHandler: { (stream, message) -\u003e Void in\n\t\t\tJSQSystemSoundPlayer.jsq_playMessageSentSound()\n\t\t\tself.finishSendingMessageAnimated(true)\n\t\t})\n\t}\n}\n```\n\n### Build \u0026 run, CONGRATULATION, you have a fully functionnal chat client !\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocessone%2Fxmpp-messenger-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprocessone%2Fxmpp-messenger-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocessone%2Fxmpp-messenger-ios/lists"}