在构建聊天应用程序时,看到朋友的列表和朋友的状态并不少见。像WhatsApp这样的应用程序具有此功能,检查您的朋友的状态非常有用,并知道在那时向他们发送消息是否明智。
我们将在一个虚构的iOS聊天应用程序中构建一个类似的功能。我们将使用Pusher为应用程序实现实时功能,以便当有人发布新状态更新时,您可以实时看到它发生变化。
这里是我们完成应用程序后的画面记录
要遵循本文,您必须具备以下要求:
- 有关Swift 3的一些知识
- 有关如何使用Xcode的知识
- 基本JavaScript知识- 安装在您计算机上的NPM和CocoaPods。
- 有关终端的基本知识(命令行)
- Pusher应用程序(您将需要应用程序的ID,秘密,密钥和群集)。
如果您目前没有帐户,请创建一个Pusher帐户。
准备好我们的项目
要开始前,我们需要创建iOS项目,然后安装应用程序正常运行所需的一些依赖关系。让我们开始吧。
在Xcode中设置我们的项目在
您的机器上启动Xcode并创建一个新项目。创建一个应用程序项目并按照向导进行操作,直到到达主要的故事板。一旦你在那里,退出Xcode。
在您的终端中,cd进入Xcode项目目录,然后运行以下命令:
$ pod init
这将Podfile在您的应用程序的根部创建一个内部。Podfile是我们将定义Cocoapods依赖关系的地方。在文本编辑器中打开并替换为以下内容:
platform :ios, '8.4'
target 'project_name' do
use_frameworks!
pod 'PusherSwift', '~> 4.0'
pod 'Alamofire', '~> 4.4'
end
在上面,我们刚刚指定了我们希望CocoaPods安装到我们的应用程序中的依赖关系。不要忘记用您的实际项目名称替换project_name。
现在去终端并运行命令:
$ pod install
这应该安装我们在我们指定的所有依赖和库Podfile。大!最后,打开项目目录并双击目录中的.xcworkspace文件以在Xcode中启动项目工作区。
创建我们的实时iOS应用程序的用户界面
现在我们已经在Xcode中创建了项目并成功安装了所有的依赖关系,接下来我们要做的就是创建iOS应用程序的用户界面。main.storyboard在Xcode中打开文件,让我们开始设计UI。
这是我们在本节末尾想要的:
在画布中添加一个导航控制器,并将其设置为根视图控制器。完成此操作后,您需要将TableViewController附件更新到导航控制器。
首先,用Xcode创建一个新类ctrl+n; 类名应该是FriendsViewController,它应该扩展UITableViewController。然后,在main.storyboard文件中,确保你TableViewController使用它FriendsViewController作为它的自定义类。
配置原型单元格
现在我们已经创建了表格视图控制器,我们需要配置其单元格以匹配我们试图实现的目标。
点击主故事板文件上的"Prototype Cells",并使属性检查器看起来接近下面的图像。
对于图像**avatar.png** **,您可以将45x45 像素的图像添加到Xcode项目中,并将其用作单元格的图像。**
我们可以做的最后一件事(完全是可选的)是改变我们应用程序的导航栏颜色。让我们这样做。
打开AppDelegate该类并在该application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)方法中粘贴以下内容:
UINavigationBar.appearance().barTintColor = UIColor(red: 18.0/255.0, green: 140.0/255.0, blue: 126.0/255.0, alpha: 1.0)
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
有了这个,你已经完成了为应用程序创建UI,剩下的就是支持它的功能。现在让我们来做。
创建我们的实时iOS应用程序的功能
对于功能,我们将它分成两部分。第一部分将着重于添加用于更新状态的功能,第二部分将着重于实时更新。
创建初始功能:U **** pdate status
允许打开FriendsViewController并进行一些修改。第一个修改是将一个更新"状态"按钮添加到导航栏的右上角。
在viewDidLoad控制器的方法中,添加下面的代码:
navigationItem.title = "Friends List"
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Status",
style: .plain,
target: self,
action: #selector(showPopup(_:))
)
上面的代码只需在导航栏中设置控制器的标题,并在导航栏的右侧添加一个按钮。
如果你注意到,在action参数中它指向一个方法,showPopup所以让我们创建这个方法。将此方法添加到控制器中:
public func showPopup(_ sender: Any) {
let alertController = UIAlertController(
title: "Update your status",
message: "What would you like your status to say?",
preferredStyle: .alert
)
alertController.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
textField.placeholder = "Status"
})
alertController.addAction(UIAlertAction(title: "Update", style: .default, handler: {(_ action: UIAlertAction) -> Void in
let status = (alertController.textFields?[0].text)! as String
self.postStatusUpdate(message: status)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
因此,我们在这里所做的是,当调用操作并showPopup调用方法时,应用程序将显示一个弹出框,要求用户输入其状态。
现在,弹出窗口调用了一个postStatusUpdate在我们的应用程序中不存在的方法。现在让我们创建这个方法。
在视图控制器中,添加下面的方法:
public func postStatusUpdate(message: String) {
let params: Parameters = ["username": username, "status": message]
Alamofire.request(FriendsViewController.API_ENDPOINT + "/status", method: .post, parameters: params).validate().responseJSON { response in
switch response.result {
case .success:
_ = "Updated"
case .failure(let error):
print(error)
}
}
}
在这种方法中,我们正在使用该Alamofire库向端点FriendsViewController.API_ENDPOINT + "/status``"(尚不存在)发出请求。现在,因为我们没有导入Alamofire库,也没有定义FriendsViewController.API_ENDPOINT我们会得到错误。
在视图控制器的顶部,导入Alamofire库:
import 'Alamofire'
另外,在类内部,在类定义之后,添加以下内容来声明API_ENDPOINT哪个将指向远程HTTP服务器。
static let API_ENDPOINT = "http://localhost:4000";
我们现在使用的端点是本地服务器,稍后将在本文中创建。如果您使用远程服务器,则需要将此值替换为服务器的URL。
所以,现在,当您运行应用程序并单击"状态"按钮时,它会弹出一个对话框,您可以输入更新。但是,由于我们尚未创建后端来响应此调用,因此它将失败并且不会执行任何操作。我们将在后面的文章中谈谈。
更新默认表视图控制器方法默认情况下,表视图控制器
带有一些方法,我们将迅速更改它们以适合我们的应用程序。
打开视图控制器并更新方法numberOfSections。设置返回值1.这将确保显示第一个和唯一部分。
接下来,更新tableView(tableView: UITableView, numberOfRowsInSection: section)方法并创建返回值friends.count。这将确保为friends列表中的每个条目创建适量的行。
要使单元格显示每个朋友的详细信息,请tableView(tableView:UITableView, cellForRowAt indexPath:IndexPath)使用以下代码更新该方法的内容:
let cell = tableView.dequeueReusableCell(withIdentifier: "friends", for: indexPath)
var status = friends[indexPath.row]["status"]
if status == "" {
status = "User has not updated status!"
}
cell.detailTextLabel?.textColor = UIColor.gray
cell.imageView?.image = UIImage(named: "avatar.png")
cell.textLabel?.text = friends[indexPath.row]["username"]
cell.detailTextLabel?.text = status
return cell
上面的代码只是获取当前单元格,并用状态,用户名和图像更新所需的单元格标签(以防您想添加另一个图像)。
最后,向视图控制器添加一个新方法:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 75.0
}
这只会将表格的行高增加到等于75.0。这将更容易适应单元格的内容。
使用Pusher为我们的iOS应用程序添加实时更新状态
现在,在我们使用Pusher添加实时在线状态更新之前,我们希望添加一些排序的伪朋友列表。
我们将使用Pusher来做朋友列表。我们将通过创建一个不持久的类属性来实现这一点,并且在这个变量中,我们将存储任何联机的人的详细信息。
添加一个伪朋友列表
在视图控制器中,添加一些新的属性:
var friends : [[String:String]] = []
var username : String = ""
var pusher : Pusher!
该friends属性将存储所有联机用户,该username属性将为当前用户存储一个随机用户名,并且该pusher属性将存储Pusher库实例。
现在,在该viewDidLoad方法中,添加以下代码:
username = "Anonymous" + String(Int(arc4random_uniform(1000)))
listenForRealtimeEvents()
// --- Update online presence at intervals --- //
let date = Date().addingTimeInterval(0)
let timer = Timer(fireAt: date, interval: 1, target: self, selector: #selector(postOnlinePresence), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
在第1行,我们只需将该username属性随机字符串作为用户名。
在第3行,我们调用一个listenForRealtimeEvents还不存在的方法(我们稍后会创建它)。
而在第6 - 8行,我们只是基本上添加了一个循环调用postOnlinePresence(还不存在)。这个电话会每秒更新一次您的在线状态。
listenForRealtimeEvents现在让我们创建该方法。将下面的代码添加到视图控制器:
private func listenForRealtimeEvents() {
pusher = Pusher(key: "PUSHER_KEY", options: PusherClientOptions(host: .cluster("PUSHER_CLUSTER")))
let channel = pusher.subscribe("new_status")
let _ = channel.bind(eventName: "online", callback: { (data: Any?) -> Void in
if let data = data as? [String: AnyObject] {
let username = data["username"] as! String
let index = self.friends.index(where: { $0["username"] == username })
if username != self.username && index == nil {
self.friends.append(["username": username, "status": "No Status"])
self.tableView.reloadData()
}
}
})
pusher.connect()
}
在我们刚刚创建的方法中,我们使用Pusher键和应用程序集群实例化了Pusher库。然后我们订阅了一个名为new_status的Pusher频道,并且在该频道上,我们开始收听名为online的事件。
在回调中,当事件监听器被触发时,我们从事件中获取用户名。然后,我们检查在friends匹配列表中是否有用户名。如果没有,我们将它附加到朋友列表并重新载入表格数据。
因此,总之,每次有人上线时,都会将名称附加到朋友列表中,并重新载入表格视图。
接下来,我们将创建一种postOnlinePresence定期发布当前用户在线状态的方法,以便其他人可以将其选中。在视图控制器中添加下面的代码:
public func postOnlinePresence() {
let params: Parameters = ["username": username]
Alamofire.request(FriendsViewController.API_ENDPOINT + "/online", method: .post, parameters: params).validate().responseJSON { response in
switch response.result {
case .success:
_ = "Online"
case .failure(let error):
print(error)
}
}
}
上面的代码只是点击一个端点,从而将用户标记为在线。
使用Pusher向应用程序添加状态更新我们iOS应用程序的最后部分将添加更新的侦听器,以便每次有人更新其状态时都会添加更新。
为此,打开listenForRealtimeEvents方法并在实例化推送变量后添加以下内容:
let channel = pusher.subscribe("new_status")
let _ = channel.bind(eventName: "update", callback: { (data: Any?) -> Void in
if let data = data as? [String: AnyObject] {
let username = data["username"] as! String
let status = data["status"] as! String
let index = self.friends.index(where: { $0["username"] == username })
if index != nil {
self.friends[index!]["status"] = status
self.tableView.reloadData()
}
}
})
上面的代码为new_status通道的更新事件创建一个侦听器。当事件被触发时,回调会检查用户名是否是朋友列表的一部分。如果是,则更新该条目的状态并重新载入表格视图数据。
现在我们已经成功地将实时功能添加到我们的应用程序中。接下来我们要做的是创建一个后端来帮助我们实际触发可以被我们的iOS应用程序拾取的Pusher事件。
为我们的实时iOS状态更新应用程序创建NodeJS后端
为Web应用程序创建一个目录,然后创建一些新文件:
**index.js**
// ------------------------------------------------------
// Import all required packages and files
// ------------------------------------------------------
let Pusher = require('pusher');
let express = require('express');
let app = express();
let bodyParser = require('body-parser')
let pusher = new Pusher(require('./config.js'));
// ------------------------------------------------------
// Set up Express middlewares
// ------------------------------------------------------
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// ------------------------------------------------------
// Define routes and logic
// ------------------------------------------------------
app.post('/status', (req, res, next) => {
let payload = {username: req.body.username, status: req.body.status};
pusher.trigger('new_status', 'update', payload);
res.json({success: 200});
});
app.post('/online', (req, res, next) => {
let payload = {username: req.body.username};
pusher.trigger('new_status', 'online', payload);
res.json({success: 200});
});
app.get('/', (req, res) => {
res.json("It works!");
});
// ------------------------------------------------------
// Catch errors
// ------------------------------------------------------
app.use((req, res, next) => {
let err = new Error('Not Found: ');
err.status = 404;
next(err);
});
// ------------------------------------------------------
// Start application
// ------------------------------------------------------
app.listen(4000, () => console.log('App listening on port 4000!'));
在这个文件中,我们创建了一个基本的Express应用程序。该应用程序有两个重要的端点:POST /online和POST /status。它们都会触发Pusher事件,并且会在我们的iOS应用程序中被侦听器拾取。
接下来创建config.js文件:
module.exports = {
appId: 'PUSHER_ID',
key: 'PUSHER_KEY',
secret: 'PUSHER_SECRET',
cluster: 'PUSHER_CLUSTER',
};
这是我们的Pusher配置文件。在这里,用您的推子仪表板中提供的凭证替换空的字符串。
最后,创建一个package.json文件:
{
"main": "index.js",
"dependencies": {
"body-parser": "^1.16.0",
"express": "^4.14.1",
"pusher": "^1.5.1"
}
}
该文件包含Node应用程序正常运行所需的所有节点程序包。
最后,在Node应用程序的目录中,运行下面的命令:
$ npm install && node index.js
第一个命令将安装所有依赖项,第二个命令将在节点中启动一个Express服务器。当您看到消息" 应用程序在4000端口监听!" **时,您知道您的后端应用程序已准备就绪。
测试我们的实时状态更新应用程序
一旦运行了本地节点Web服务器,您需要进行一些更改,以便您的应用程序可以与本地Web服务器通信。在该info.plist文件中,进行以下更改:
通过这一改变,您可以构建并运行您的应用程序,并且可以直接与您的本地Web应用程序通话。
总结
在文章中,我们已经能够创建一个具有实时用户状态更新的iOS应用程序,类似于WhatsApp目前的应用程序。