Build live comments using Swift
A basic understanding of Swift and PHP are needed to follow this tutorial.
Many applications come with a section where users can comment on the item they are previewing. When a comment is posted, while you are looking at this item, the best UX is to see the comment immediately as it is made by the user. In this tutorial we will demonstrate how we can achieve a live commenting feature using Pusher on an iOS application.
We will be building a gallery viewing application with comments. You can add your own comment to the item and it will be available instantly to other users looking at the item. For the sake of brevity, we will be building a very basic application and focusing more on the implementation than the design. When we are done, we should be able to achieve something like what’s shown below:
Setting up our XCode project
Create a new project on XCode and call it whatever you want. We will name ours “showcaze”. You can simply follow the wizard XCode provides. Select the single page application as the base template. Once you are done with this, you will need to prepare the dependencies to be used by the application.
The easiest way install dependencies is by using CocoaPods. If you don’t have CocoaPods installed you can install them via RubyGems.
gem install cocoapods
Then configure CocoaPods in our application. First initialise the project by running this command in the top-level directory of your XCode project:
pod init
This will create a file called Podfile. Open it in your text editor, and make sure to add the following lines specifying your application’s dependencies:
# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'
target 'showcaze' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for showcaze
pod 'PusherSwift', '~> 4.0'
pod 'Alamofire', '~> 4.4'
end
And then run pod install
to download and install the dependencies. When cocoapods asks you to, close XCode. Now open the project_name.xcworkspace
file in the root of your project, in my case, the showcaze.xcworkspace
file. This should launch XCode.
Note: Because we are using the latest version of XCode which contains Swift 3, we are using specific dependency versions that are compatible with it. You might need to use a lower dependency version tag depending on your XCode and Swift version.
Creating our status updates list view
We want to display the available comments and then allow people to add comments to this list. When we are done we want to have a simple view just like this.
Open the Main storyboard and delete the current View Controller
scene. Drag a new UITableViewController
into the scene. Create a CommentsTableViewController
and associate the newly created View Controller
scene with it. Finally, while the view controller is selected in the storyboard, from XCode toolbar click Editor > Embed In > Navigation Controller.
After this, you should add the labels as needed to the prototype cell, add a reuse identifier and create a CommentTableViewCell
class and make it the custom class for the prototype table cell.
This is the source of the CommentTableViewCell
:
import UIKit
class CommentTableViewCell: UITableViewCell {
@IBOutlet weak var username: UILabel!
@IBOutlet weak var comment: UITextView!
}
Note there are two
@IBOutlet
s in the class. You need to create the cell label and text view thenControl + Drag
them to theCommentTableViewCell
class to create IBOutlets.
This is the source of the CommentsTableViewController
:
import UIKit
import Alamofire
import PusherSwift
class CommentsTableViewController: UITableViewController {
let MESSAGES_ENDPOINT = "https://live-commenting-ios.herokuapp.com/"
var pusher: Pusher!
var comments = [
["username": "John", "comment": "Amazing application nice!"],
["username": "Samuel", "comment": "How can I add a photo to my profile? This is longer than the previous comment."]
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 78;
navigationItem.title = "View Comments"
listenForNewComments()
addComposeButtonToNavigationBar()
}
private func listenForNewComments() -> Void {
pusher = Pusher(key: "PUSHER_API_KEY")
let channel = pusher.subscribe("comments")
let _ = channel.bind(eventName: "new_comment", callback: { (data: Any?) -> Void in
if let data = data as? [String: AnyObject] {
let comment = ["username":"Anonymous", "comment": (data["text"] as! String)]
self.comments.insert(comment, at: 0)
self.tableView.beginUpdates()
self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
self.tableView.endUpdates()
}
})
pusher.connect()
}
private func addComposeButtonToNavigationBar() -> Void {
let button = UIBarButtonItem(barButtonSystemItem: .compose,
target: self,
action: #selector(buttonTapped))
navigationItem.setRightBarButton(button, animated: false)
}
func buttonTapped() -> Void {
let alert = UIAlertController(title: "Post",
message: "Enter a comment and see it inserted in real time using Pusher",
preferredStyle: .alert)
alert.addTextField { (textField) in
textField.text = nil
textField.placeholder = "Enter comment"
}
alert.addAction(UIAlertAction(title: "Add Comment", style: .default, handler: { [weak alert] (_) in
let textField = alert?.textFields![0]
if (textField?.hasText)! {
self.postComment(comment: (textField?.text)!)
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
private func postComment(comment: String) -> Void {
Alamofire.request(MESSAGES_ENDPOINT, method: .post, parameters: ["comment": comment])
.validate()
.responseJSON { response in
switch response.result {
case .success:
print("Posted successfully")
case .failure(let error):
print(error)
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell
cell.username?.text = "? " + (comments[indexPath.row]["username"] ?? "Anonymous")
cell.comment?.text = comments[indexPath.row]["comment"]
return cell
}
}
To highlight some important parts of the code, lets break a few down.
private func listenForNewComments() -> Void {
pusher = Pusher(key: "PUSHER_API_KEY")
let channel = pusher.subscribe("comments")
let _ = channel.bind(eventName: "new_comment", callback: { (data: Any?) -> Void in
if let data = data as? [String: AnyObject] {
let text = data["text"] as! String
self.comments.insert(text, at: 0)
self.tableView.beginUpdates()
self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
self.tableView.endUpdates()
}
})
pusher.connect()
}
This function basically initialises the Pusher Swift SDK, subscribes to a channel’s comments
, listens for an event new_comment
and then fires a callback when that event is triggered from anywhere. In the callback, the text is appended to the top of the table data, then the tableView
is updated with the new row.
private func addComposeButtonToNavigationBar() -> Void {
let button = UIBarButtonItem(barButtonSystemItem: .compose,
target: self,
action: #selector(buttonTapped))
navigationItem.setRightBarButton(button, animated: false)
}
func buttonTapped() -> Void {
let alert = UIAlertController(title: "Post",
message: "Enter a comment and see it inserted in real time using Pusher",
preferredStyle: .alert)
alert.addTextField { (textField) in
textField.text = nil
textField.placeholder = "Enter comment"
}
alert.addAction(UIAlertAction(title: "Add Comment", style: .default, handler: { [weak alert] (_) in
let textField = alert?.textFields![0]
if (textField?.hasText)! {
self.postComment(comment: (textField?.text)!)
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
This block simply adds a compose button at the right of the navigation bar. It fires a buttonTapped
callback when the button is well tapped. The buttonTap
callback launches an alert view controller with a text field where the comment text is supposed to be entered in.
Connecting to Pusher
Create a new Pusher account (it’s free) if you do not already have one. Create a new application and retrieve your keys. This is the key you will use above. When you have your keys, you can plug it above and also below in the back-end service.
Building the backend
Now that we have our application subscribed to the Pusher event and also posting comments, we will have to build a backend to support it. For the backend, I have made a simple PHP script. It will be uploaded as a part of the repository. You will need to host somewhere if you wish to use the demo, we have chosen Heroku for this.
Here is our simple PHP script:
<?php
//
require('vendor/autoload.php');
$comment = $_POST['comment'] ?? false;
if ($comment) {
$status = "success";
$options = ['encrypted' => true];
$pusher = new Pusher('PUSHER_API_KEY', 'PUSHER_API_SECRET', 'PUSHER_APP_ID', $options);
$pusher->trigger('comments', 'new_comment', ['text' => $comment]);
} else {
$status = "failure";
}
header('Content-Type: application/json');
echo json_encode(["result" => $status]);
Heres the composer.json
file to install dependencies:
{
"require": {
"pusher/pusher-php-server": "^2.6"
},
"require-dev": {
"heroku/heroku-buildpack-php": "*"
}
}
The dev dependency is only useful if you are deploying the test script to Heroku, and here’s the Procfile
if you are:
web: vendor/bin/heroku-php-apache2 ./
Now that we have the server running, whenever you make a comment on the application, it will be sent to Pusher and then the page will be updated with it.
Conclusion
Now we have built a live commenting feature for your iOS app. Hopefully, you have learnt a thing or two on how to use the Pusher iOS Swift SDK to make realtime updates to your iOS application. If you have done something great on your own, leave a comment below and share it with us.
21 April 2017
by Neo Ighodaro