swift通知栏推送_如何使用Swift使用推送通知构建食品交付应用
swift通知欄推送
by Neo Ighodaro
由新Ighodaro
如何使用Swift使用推送通知構(gòu)建食品交付應(yīng)用 (How to build a food delivery app with push notifications using Swift)
A basic understanding of Swift and Node.js is needed to follow this tutorial.
要學(xué)習(xí)本教程,需要對Swift和Node.js有基本的了解。
Last mile delivery marketplaces make it easy to order food from a mobile device and have it delivered to a user’s door while it’s still hot.
最后一英里的運送市場使從移動設(shè)備訂購食物變得很容易,并且可以在仍然很熱的時候?qū)⑹澄镞\送到用戶家中。
Marketplaces like Deliveroo, Postmates, or Uber Eats use your device’s location to serve you a list of restaurants that are close enough to you (and open) so you can get your delivery as soon as possible.
像Deliveroo,Postmates或Uber Eats這樣的市場都使用您設(shè)備的位置為您提供了一個距離您足夠近(開著)的餐館列表,因此您可以盡快獲得送貨。
This realtime experience between the customer, restaurant, and driver relies on transactional push notifications to move the order from the kitchen to the table seamlessly. Customers want push notifications to alert them when their order is on its way and when they need to meet the driver at the door.
客戶,餐廳和駕駛員之間的這種實時體驗依賴于交易推送通知,以將訂單從廚房無縫地轉(zhuǎn)移到桌子上。 客戶希望推送通知在他們的訂單即將到來時以及何時需要在門口與駕駛員見面時向他們發(fā)出警報。
Setting up push notifications can be confusing and time consuming. However, with Pusher’s Push Notifications API, the process is a lot easier and faster.
設(shè)置推送通知可能會造成混亂和耗時。 但是,使用Pusher的Push Notifications API ,此過程變得更加輕松快捷。
In this article, we will be considering how you can build apps on iOS that have transactional push notifications. For this, we will be building a make-believe food delivery app.
在本文中,我們將考慮如何在具有事務(wù)性推送通知的iOS上構(gòu)建應(yīng)用程序。 為此,我們將構(gòu)建一個虛構(gòu)的外賣應(yīng)用程序。
先決條件 (Prerequisites)
A Mac with Xcode installed. Download Xcode here.
裝有Xcode的Mac。 在此處下載Xcode 。
- Knowledge of using Xcode. 了解使用Xcode的知識。
Knowledge of Swift.
了解Swift 。
A Pusher account. Create one here.
Pusher帳戶。 在此處創(chuàng)建一個 。
Basic knowledge of JavaScript/Node.js (Check out this tutorial).
JavaScript / Node.js的基礎(chǔ)知識( 請參閱本教程 )。
Cocoapods installed on your machine.
您的機器上已安裝了 Cocoapods。
Once you have the requirements, let’s start.
有了需求后,就開始吧。
構(gòu)建我們的應(yīng)用程序-規(guī)劃 (Building our application — planning)
Before we start building our application, we need to do some planning on how we want the application to work.
在開始構(gòu)建應(yīng)用程序之前,我們需要對我們希望應(yīng)用程序的工作方式進(jìn)行一些規(guī)劃。
We will be making three applications:
我們將提出三個申請:
- The backend application (Web using Node.js). 后端應(yīng)用程序(使用Node.js的Web)。
- The client application (iOS using Swift). 客戶端應(yīng)用程序(使用Swift的iOS)。
- The admin application (iOS using Swift). 管理應(yīng)用程序(使用Swift的iOS)。
后端應(yīng)用 (The backend application)
This will be the API. For simplicity, we will not add any sort of authentication to the API. We will be calling the API from our iOS applications. The API should be able to provide the food inventory, the orders, and also manage the orders. We will also be sending push notifications from the backend application.
這將是API。 為簡單起見,我們不會向API添加任何形式的身份驗證。 我們將從iOS應(yīng)用程序中調(diào)用API。 該API應(yīng)該能夠提供食品庫存,訂單以及管理訂單。 我們還將從后端應(yīng)用程序發(fā)送推送通知。
客戶端應(yīng)用 (The client application)
This will be the application that will be with the customer. It’s where the user will be able to order food. For simplicity, we will not have any sort of authentication, and everything will be straight to the point. A customer should be able to see the inventory and order one or more things from that inventory. They should also be able to see the list of their orders and the status of each order.
這將是與客戶一起使用的應(yīng)用程序。 用戶可以在這里訂購食物。 為簡單起見,我們將沒有任何形式的身份驗證,并且所有內(nèi)容都直截了當(dāng)。 客戶應(yīng)該能夠看到庫存并從該庫存中訂購一件或多件東西。 他們還應(yīng)該能夠看到他們的訂單列表以及每個訂單的狀態(tài)。
管理員應(yīng)用程序 (The admin application)
This will be the application that the company providing the service will use to fulfill orders. The application will display the available orders, and the admin will be able to set the status for each order.
這將是提供服務(wù)的公司用于履行訂單的應(yīng)用程序。 該應(yīng)用程序?qū)@示可用的訂單,管理員將能夠設(shè)置每個訂單的狀態(tài)。
構(gòu)建后端應(yīng)用程序(API) (Building the backend application (API))
The first thing we want to build is the API. We will be adding everything required to support our iOS applications, and will then add push notifications later on.
我們要構(gòu)建的第一件事是API。 我們將添加支持我們的iOS應(yīng)用程序所需的所有內(nèi)容,然后在以后添加推送通知。
To get started, create a project directory for the API. In the directory, create a new file called package.json. In the file, paste the following:
首先,為API創(chuàng)建一個項目目錄。 在目錄中,創(chuàng)建一個名為package.json的新文件。 在文件中,粘貼以下內(nèi)容:
{ "main": "index.js", "scripts": {}, "dependencies": { "body-parser": "^1.18.2", "express": "^4.16.2" } }Next run the command below in your terminal:
接下來在您的終端中運行以下命令:
$ npm installThis will install all the listed dependencies. Next, create an index.js file in the same directory as the package.json file and paste in the following code:
這將安裝所有列出的依賴項。 接下來,在與package.json文件相同的目錄中創(chuàng)建一個index.js文件,并粘貼以下代碼:
// -------------------------------------------------------- // Pull in the libraries // -------------------------------------------------------- const app = require('express')() const bodyParser = require('body-parser') // -------------------------------------------------------- // Helpers // -------------------------------------------------------- function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // -------------------------------------------------------- // In-memory database // -------------------------------------------------------- var user_id = null var orders = [] let inventory = [ { id: uuidv4(), name: "Pizza Margherita", description: "Features tomatoes, sliced mozzarella, basil, and extra virgin olive oil.", amount: 39.99, image: 'pizza1' }, { id: uuidv4(), name: "Bacon cheese fry", description: "Features tomatoes, bacon, cheese, basil and oil", amount: 29.99, image: 'pizza2' } ] // -------------------------------------------------------- // Express Middlewares // -------------------------------------------------------- app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: false})) // -------------------------------------------------------- // Routes // -------------------------------------------------------- app.get('/orders', (req, res) => res.json(orders)) app.post('/orders', (req, res) => { let id = uuidv4() user_id = req.body.user_id let pizza = inventory.find(item => item["id"] === req.body.pizza_id) if (!pizza) { return res.json({status: false}) } orders.unshift({id, user_id, pizza, status: "Pending"}) res.json({status: true}) }) app.put('/orders/:id', (req, res) => { let order = orders.find(order => order["id"] === req.params.id) if ( ! order) { return res.json({status: false}) } orders[orders.indexOf(order)]["status"] = req.body.status return res.json({status: true}) }) app.get('/inventory', (req, res) => res.json(inventory)) app.get('/', (req, res) => res.json({status: "success"})) // -------------------------------------------------------- // Serve application // -------------------------------------------------------- app.listen(4000, _ => console.log('App listening on port 4000!'))The above code is a simple Express application. Everything is self-explanatory and has comments to guide you.
上面的代碼是一個簡單的Express應(yīng)用程序。 一切都是不言自明的,并有注釋可以指導(dǎo)您。
In the first route, /orders, we display the list of orders available from the in-memory data store. In the second route, POST /orders, we just add a new order to the list of orders. In the third route, PUT /orders/:id, we just modify the status of a single order from the list of orders. In the fourth route, GET /inventory, we list the inventory available from the list of inventory in the database.
在第一個路線/orders ,我們顯示內(nèi)存數(shù)據(jù)存儲中可用的訂單列表。 在第二條路線POST /orders ,我們只是將新訂單添加到orders列表中。 在第三條路線PUT /orders/:id ,我們只是從orders列表中修改單個訂單的狀態(tài)。 在第四條路線GET /inventory ,我們從數(shù)據(jù)庫中的清單列表中列出可用inventory 。
We are done with the API for now, and we will revisit it when we need to add the push notification code. If you want to test that the API is working, then run the following command on your terminal:
目前,我們已經(jīng)完成了API的使用,當(dāng)我們需要添加推送通知代碼時,我們將對其進(jìn)行重新訪問。 如果要測試API是否正常運行,請在終端上運行以下命令:
$ node index.jsThis will start a new Node server listening on port 4000.
這將啟動在端口4000上偵聽的新Node服務(wù)器。
構(gòu)建客戶端應(yīng)用程序 (Building the client application)
The next thing we need to do is build the client application in Xcode. To start, launch Xcode and create a new ‘Single Application’ project. We will name our project PizzaareaClient.
我們需要做的下一步是在Xcode中構(gòu)建客戶端應(yīng)用程序。 首先,啟動Xcode并創(chuàng)建一個新的“單一應(yīng)用程序”項目。 我們將項目命名為PizzaareaClient。
Once the project has been created, exit Xcode and create a new file called Podfile in the root of the Xcode project you just created. In the file, paste in the following code:
創(chuàng)建項目后,退出Xcode并在剛創(chuàng)建的Xcode項目的根目錄中創(chuàng)建一個名為Podfile的新文件。 在文件中,粘貼以下代碼:
platform :ios, '11.0'target 'PizzareaClient' do use_frameworks! pod 'PusherSwift', '~> 5.1.1' pod 'Alamofire', '~> 4.6.0' endIn the file above, we specified the dependencies the project needs to run. Remember to change the target above to the name of your project. Now in your terminal, run the following command to install the dependencies:
在上面的文件中,我們指定了項目需要運行的依賴項。 請記住,將 上面 的 target 更改 為您的項目名稱。 現(xiàn)在在您的終端中,運行以下命令以安裝依賴項:
$ pod installAfter the installation is complete, open the Xcode workspace file that was generated by Cocoapods. This should relaunch Xcode.
安裝完成后,打開由Cocoapods生成的Xcode工作區(qū)文件。 這應(yīng)該重新啟動Xcode。
When Xcode has been relaunched, open the Main.storyboard file. In it we will create the storyboard for our client application. Below is a screenshot of how we have designed our storyboard:
Xcode重新啟動后,打開Main.storyboard文件。 在其中,我們將為客戶應(yīng)用程序創(chuàng)建情節(jié)提要。 以下是我們?nèi)绾卧O(shè)計故事板的屏幕截圖:
The first scene is the navigation view controller, which has a table view controller as the root controller. The navigation controller is the initial controller that is loaded when the application is launched.
第一個場景是導(dǎo)航視圖控制器,它具有一個表視圖控制器作為根控制器。 導(dǎo)航控制器是啟動應(yīng)用程序時加載的初始控制器。
創(chuàng)建披薩列表場景 (Creating the pizza list scene)
The second scene is the view controller that lists the inventory that we have available.
第二個場景是視圖控制器,它列出了我們可用的清單。
Create a new file in Xcode called PizzaTableListViewController.swift, make it the custom class for the second scene, and paste in the following code:
在Xcode中創(chuàng)建一個名為PizzaTableListViewController.swift的新文件,使其成為第二個場景的自定義類,然后粘貼以下代碼:
import UIKit import Alamofireclass PizzaListTableViewController: UITableViewController {var pizzas: [Pizza] = []override func viewDidLoad() { super.viewDidLoad()navigationItem.title = "Select Pizza"fetchInventory { pizzas in guard pizzas != nil else { return } self.pizzas = pizzas! self.tableView.reloadData() } }private func fetchInventory(completion: @escaping ([Pizza]?) -> Void) { Alamofire.request("http://127.0.0.1:4000/inventory", method: .get) .validate() .responseJSON { response in guard response.result.isSuccess else { return completion(nil) } guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }let inventory = rawInventory.flatMap { pizzaDict -> Pizza? in var data = pizzaDict! data["image"] = UIImage(named: pizzaDict!["image"] as! String)return Pizza(data: data) }completion(inventory) } }@IBAction func ordersButtonPressed(_ sender: Any) { performSegue(withIdentifier: "orders", sender: nil) }override func numberOfSections(in tableView: UITableView) -> Int { return 1 }override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return pizzas.count }override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCellcell.name.text = pizzas[indexPath.row].name cell.imageView?.image = pizzas[indexPath.row].image cell.amount.text = "$\(pizzas[indexPath.row].amount)" cell.miscellaneousText.text = pizzas[indexPath.row].descriptionreturn cell }override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 100.0 }override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { performSegue(withIdentifier: "pizza", sender: self.pizzas[indexPath.row] as Pizza) }override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "pizza" { guard let vc = segue.destination as? PizzaViewController else { return } vc.pizza = sender as? Pizza } } }In the viewDidLoad method, we call the fetchInventory method that uses Alamofire to fetch the inventory from our backend API. Then we save the response to the orders property of the controller.
在viewDidLoad方法中,我們調(diào)用fetchInventory方法,該方法使用Alamofire從后端API中獲取清單。 然后,將響應(yīng)保存到控制器的orders屬性。
The ordersButtonPressed is linked to the Orders button on the scene. This just presents the scene with the list of orders using a named segue orders.
ordersButtonPressed鏈接到場景中的“ Orders按鈕。 這只是呈現(xiàn)使用名為賽格瑞訂單列表現(xiàn)場orders 。
The tableView* methods implement methods available to the UITableViewDelegate protocol and should be familiar to you.
tableView*方法實現(xiàn)了UITableViewDelegate協(xié)議可用的方法,您應(yīng)該熟悉它們。
The final method prepare simply sends the pizza to the view controller on navigation. But this pizza is only sent over if the view controller being loaded is the PizzaViewController .
最終的prepare方法只是將pizza發(fā)送到導(dǎo)航中的視圖控制器。 但這種pizza只送過來,如果要加載的視圖控制器是PizzaViewController 。
Before we create the third scene, create a PizzaTableViewCell.swift class and paste in the following:
在創(chuàng)建第三個場景之前,請創(chuàng)建PizzaTableViewCell.swift類并粘貼以下內(nèi)容:
import UIKit class PizzaTableViewCell: UITableViewCell { @IBOutlet weak var pizzaImageView: UIImageView! @IBOutlet weak var name: UILabel! @IBOutlet weak var miscellaneousText: UILabel! @IBOutlet weak var amount: UILabel! override func awakeFromNib() { super.awakeFromNib() } }?? Make sure the custom class of the cells in the second scene is PizzaTableViewCell, and that the reusable identifier is Pizza.
??確保第二個場景中單元格的自定義類是PizzaTableViewCell ,并且可重復(fù)使用的標(biāo)識符是Pizza 。
創(chuàng)建披薩視圖場景 (Creating the pizza view scene)
The third scene in our storyboard is the Pizza view scene. This is where the selected inventory can be viewed.
我們的情節(jié)提要中的第三個場景是Pizza視圖場景。 在這里可以查看所選庫存。
Create a PizzaViewController.swift file, make it the custom class for the scene above, and paste in the following code:
創(chuàng)建一個PizzaViewController.swift文件,使其成為上面場景的自定義類,然后粘貼以下代碼:
import UIKit import Alamofireclass PizzaViewController: UIViewController {var pizza: Pizza?@IBOutlet weak var amount: UILabel! @IBOutlet weak var pizzaDescription: UILabel! @IBOutlet weak var pizzaImageView: UIImageView!override func viewDidLoad() { super.viewDidLoad()navigationItem.title = pizza!.name pizzaImageView.image = pizza!.image pizzaDescription.text = pizza!.description amount.text = "$\(String(describing: pizza!.amount))" }@IBAction func buyButtonPressed(_ sender: Any) { let parameters = [ "pizza_id": pizza!.id, "user_id": AppMisc.USER_ID ]Alamofire.request("http://127.0.0.1:4000/orders", method: .post, parameters: parameters) .validate() .responseJSON { response in guard response.result.isSuccess else { return self.alertError() }guard let status = response.result.value as? [String: Bool], let successful = status["status"] else { return self.alertError() }successful ? self.alertSuccess() : self.alertError() } }private func alertError() { return self.alert( title: "Purchase unsuccessful!", message: "Unable to complete purchase please try again later." ) }private func alertSuccess() { return self.alert( title: "Purchase Successful", message: "You have ordered successfully, your order will be confirmed soon." ) }private func alert(title: String, message: String) { let alertCtrl = UIAlertController(title: title, message: message, preferredStyle: .alert)alertCtrl.addAction(UIAlertAction(title: "Okay", style: .cancel) { action in self.navigationController?.popViewController(animated: true) })present(alertCtrl, animated: true, completion: nil) } }In the code above, we have multiple @IBOutlet’s and a single @IBAction. You need to link the outlets and actions to the controller from the storyboard.
在上面的代碼中,我們有多個@IBOutlet和一個@IBAction 。 您需要從情節(jié)提要中將插座和動作鏈接到控制器。
In the viewDidLoad we set the outlets so they display the correct values using the pizza sent from the previous view controller. The buyButtonPressed method uses Alamofire to place an order by sending a request to the API. The remaining methods handle displaying the error or success response from the API.
在viewDidLoad我們設(shè)置出口,以便它們使用從前一個視圖控制器發(fā)送的pizza顯示正確的值。 buyButtonPressed方法使用Alamofire通過向API發(fā)送請求來下訂單。 其余方法處理顯示來自API的錯誤或成功響應(yīng)。
創(chuàng)建訂單清單場景 (Creating the orders list scene)
The next scene is the Orders list scene. In this scene, all the orders are listed so the user can see them and their status:
下一個場景是“訂單”列表場景。 在此場景中,列出了所有訂單,因此用戶可以查看它們及其狀態(tài):
Create a OrderTableViewController.swift file, make it the custom class for the scene above, and paste in the following code:
創(chuàng)建一個OrderTableViewController.swift文件,使其成為上面場景的自定義類,然后粘貼以下代碼:
import UIKit import Alamofireclass OrdersTableViewController: UITableViewController {var orders: [Order] = []override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Orders"fetchOrders { orders in self.orders = orders! self.tableView.reloadData() } }private func fetchOrders(completion: @escaping([Order]?) -> Void) { Alamofire.request("http://127.0.0.1:4000/orders").validate().responseJSON { response in guard response.result.isSuccess else { return completion(nil) }guard let rawOrders = response.result.value as? [[String: Any]?] else { return completion(nil) }let orders = rawOrders.flatMap { ordersDict -> Order? in guard let orderId = ordersDict!["id"] as? String, let orderStatus = ordersDict!["status"] as? String, var pizza = ordersDict!["pizza"] as? [String: Any] else { return nil }pizza["image"] = UIImage(named: pizza["image"] as! String)return Order( id: orderId, pizza: Pizza(data: pizza), status: OrderStatus(rawValue: orderStatus)! ) }completion(orders) } }@IBAction func closeButtonPressed(_ sender: Any) { dismiss(animated: true, completion: nil) }override func numberOfSections(in tableView: UITableView) -> Int { return 1 }override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return orders.count }override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "order", for: indexPath) let order = orders[indexPath.row]cell.textLabel?.text = order.pizza.name cell.imageView?.image = order.pizza.image cell.detailTextLabel?.text = "$\(order.pizza.amount) - \(order.status.rawValue)"return cell }override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 100.0 }override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { performSegue(withIdentifier: "order", sender: orders[indexPath.row] as Order) }override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "order" { guard let vc = segue.destination as? OrderViewController else { return } vc.order = sender as? Order } } }The code above is similar to the code in the PizzaTableViewController above. However, instead of fetching the inventory, it fetches the orders. Instead of passing the pizza in the last method, it passes the order to the next controller. The controller also comes with a closeButtonPressed method that just dismisses the controller and returns to the inventory list scene.
上面的代碼PizzaTableViewController上面的PizzaTableViewController的代碼。 但是,它不是獲取庫存,而是獲取orders 。 而不是通過最后一個方法傳遞pizza ,而是將order傳遞給下一個控制器。 控制器還帶有closeButtonPressed方法,該方法只是關(guān)閉控制器并返回到清單清單場景。
創(chuàng)建訂單狀態(tài)場景 (Creating the Order Status Scene)
The next scene is the Order scene. In this scene, we can see the status of the order:
下一個場景是訂購場景。 在此場景中,我們可以看到訂單的狀態(tài):
?? The scene above has an invisible view right above the status label. You need to use this view to create an @IBOutlet to the controller.
above?上面的場景在狀態(tài)標(biāo)簽的正上方具有不可見的視圖。 您需要使用此視圖為控制器創(chuàng)建@IBOutlet 。
Create a OrderViewController.swift file, make it the custom class for the scene above, and paste in the following code:
創(chuàng)建一個OrderViewController.swift文件,使其成為上面場景的自定義類,然后粘貼以下代碼:
import UIKitclass OrderViewController: UIViewController {var order: Order?@IBOutlet weak var status: UILabel! @IBOutlet weak var activityView: ActivityIndicator!override func viewDidLoad() { super.viewDidLoad()navigationItem.title = order?.pizza.nameactivityView.startLoading()switch order!.status { case .pending: status.text = "Processing Order" case .accepted: status.text = "Preparing Order" case .dispatched: status.text = "Order is on its way!" case .delivered: status.text = "Order delivered" activityView.strokeColor = UIColor.green activityView.completeLoading(success: true) } } }In the code above, we are doing all the work in our viewDidLoad method. In there we have the ActivityIndicator class, which we will create next, referenced as an @IBOutlet.
在上面的代碼中,我們正在執(zhí)行viewDidLoad方法中的所有工作。 在那里,我們有ActivityIndicator類,接下來將創(chuàng)建該類,將其稱為@IBOutlet 。
創(chuàng)建應(yīng)用程序的其他部分 (Creating other parts of the application)
We are using a third-party library called the [ActivityIndicator](https://github.com/abdulKarim002/activityIndicator), but since the package is not available via Cocoapods, we have opted to create it ourselves and import it.
我們正在使用一個名為[ActivityIndicator](https://github.com/abdulKarim002/activityIndicator)的第三方庫,但是由于該軟件包無法通過Cocoapods獲得,因此我們選擇自己創(chuàng)建并導(dǎo)入。
Create a new file in Xcode called ActivityIndicator and paste the code from the repo here into it.
在Xcode中創(chuàng)建一個名為ActivityIndicator的新文件,并將代碼從倉庫中粘貼到其中。
Next, create a new Order.swift file and paste in the following code:
接下來,創(chuàng)建一個新的Order.swift文件并粘貼以下代碼:
import Foundationstruct Order { let id: String let pizza: Pizza var status: OrderStatus }enum OrderStatus: String { case pending = "Pending" case accepted = "Accepted" case dispatched = "Dispatched" case delivered = "Delivered" }Finally, create a Pizza.swift and paste in the following code:
最后,創(chuàng)建Pizza.swift并粘貼以下代碼:
import UIKitstruct Pizza { let id: String let name: String let description: String let amount: Float let image: UIImageinit(data: [String: Any]) { self.id = data["id"] as! String self.name = data["name"] as! String self.amount = data["amount"] as! Float self.description = data["description"] as! String self.image = data["image"] as! UIImage } }That is all for the client application. One last thing we need to do, though, is modify the info.plist file. We need to add an entry to the plist file to allow connection to our local server:
這就是客戶端應(yīng)用程序的全部內(nèi)容。 但是,我們需要做的最后一件事是修改info.plist文件。 我們需要在plist文件中添加一個條目,以允許連接到我們的本地服務(wù)器:
Let’s move on to the admin application.
讓我們繼續(xù)進(jìn)行管理應(yīng)用程序。
生成管理應(yīng)用程序 (Building the admin application)
Launch a new instance of Xcode and create a new ‘Single Application’ project. We will name our project PizzaareaAdmin.
啟動Xcode的新實例并創(chuàng)建一個新的“單一應(yīng)用程序”項目。 我們將我們的項目命名為PizzaareaAdmin。
Once the project has been created, exit Xcode and create a new file called Podfile in the root of the Xcode project you just created. In the file, paste in the following code:
創(chuàng)建項目后,退出Xcode并在剛創(chuàng)建的Xcode項目的根目錄中創(chuàng)建一個名為Podfile的新文件。 在文件中,粘貼以下代碼:
platform :ios, '11.0'target 'PizzareaAdmin' do use_frameworks! pod 'PusherSwift', '~> 5.1.1' pod 'Alamofire', '~> 4.6.0' endIn the file above, we specified the dependencies the project needs to run. Remember to change the **target** above to the name of your project.
在上面的文件中,我們指定了項目需要運行的依賴項。 請記住,將 上面的“ **target** 更改 為您的項目名稱。
Now, in your terminal, run the following command to install the dependencies:
現(xiàn)在,在您的終端中,運行以下命令以安裝依賴項:
$ pod installAfter the installation is complete, open the Xcode workspace file that was generated by Cocoapods. This should relaunch Xcode.
安裝完成后,打開由Cocoapods生成的Xcode工作區(qū)文件。 這應(yīng)該重新啟動Xcode。
When Xcode has been relaunched, open the Main.storyboard file. In there we will create the storyboard for our client application. Below is a screenshot of how we have designed our storyboard:
Xcode重新啟動后,打開Main.storyboard文件。 在這里,我們將為客戶應(yīng)用程序創(chuàng)建情節(jié)提要。 以下是我們?nèi)绾卧O(shè)計故事板的屏幕截圖:
Above we have a navigation view controller that is the initial view controller.
上面我們有一個導(dǎo)航視圖控制器,它是初始視圖控制器。
創(chuàng)建訂單清單場景 (Creating the orders list scene)
The orders list scene is supposed to show the list of clients’ orders. From there we can change the status of each order when we want.
訂單列表場景應(yīng)該顯示客戶訂單的列表。 從那里我們可以在需要時更改每個訂單的狀態(tài)。
Create a new file in Xcode called OrdersListViewController.swift, make it the custom class for the second scene, and paste in the following code:
在Xcode中創(chuàng)建一個名為OrdersListViewController.swift的新文件,使其成為第二個場景的自定義類,并粘貼以下代碼:
import UIKit import Alamofireclass OrdersTableViewController: UITableViewController {var orders: [Order] = []override func viewDidLoad() { super.viewDidLoad()navigationItem.title = "Client Orders"fetchOrders { orders in self.orders = orders! self.tableView.reloadData() } }private func fetchOrders(completion: @escaping([Order]?) -> Void) { Alamofire.request("http://127.0.0.1:4000/orders").validate().responseJSON { response in guard response.result.isSuccess else { return completion(nil) }guard let rawOrders = response.result.value as? [[String: Any]?] else { return completion(nil) }let orders = rawOrders.flatMap { ordersDict -> Order? in guard let orderId = ordersDict!["id"] as? String, let orderStatus = ordersDict!["status"] as? String, var pizza = ordersDict!["pizza"] as? [String: Any] else { return nil }pizza["image"] = UIImage(named: pizza["image"] as! String)return Order( id: orderId, pizza: Pizza(data: pizza), status: OrderStatus(rawValue: orderStatus)! ) }completion(orders) } }override func numberOfSections(in tableView: UITableView) -> Int { return 1 }override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return orders.count }override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "order", for: indexPath) let order = orders[indexPath.row]cell.textLabel?.text = order.pizza.name cell.imageView?.image = order.pizza.image cell.detailTextLabel?.text = "$\(order.pizza.amount) - \(order.status.rawValue)"return cell }override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 100.0 }override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let order: Order = orders[indexPath.row]let alertCtrl = UIAlertController( title: "Change Status", message: "Change the status of the order based on the progress made.", preferredStyle: .actionSheet )alertCtrl.addAction(createActionForStatus(.pending, order: order)) alertCtrl.addAction(createActionForStatus(.accepted, order: order)) alertCtrl.addAction(createActionForStatus(.dispatched, order: order)) alertCtrl.addAction(createActionForStatus(.delivered, order: order)) alertCtrl.addAction(createActionForStatus(nil, order: nil))present(alertCtrl, animated: true, completion: nil) }private func createActionForStatus(_ status: OrderStatus?, order: Order?) -> UIAlertAction { let alertTitle = status == nil ? "Cancel" : status?.rawValue let alertStyle: UIAlertActionStyle = status == nil ? .cancel : .defaultlet action = UIAlertAction(title: alertTitle, style: alertStyle) { action in if status != nil { self.setStatus(status!, order: order!) } }if status != nil { action.isEnabled = status?.rawValue != order?.status.rawValue }return action }private func setStatus(_ status: OrderStatus, order: Order) { updateOrderStatus(status, order: order) { successful in guard successful else { return } guard let index = self.orders.index(where: {$0.id == order.id}) else { return }self.orders[index].status = status self.tableView.reloadData() } }private func updateOrderStatus(_ status: OrderStatus, order: Order, completion: @escaping(Bool) -> Void) { let url = "http://127.0.0.1:4000/orders/" + order.id let params = ["status": status.rawValue]Alamofire.request(url, method: .put, parameters: params).validate().responseJSON { response in guard response.result.isSuccess else { return completion(false) } guard let data = response.result.value as? [String: Bool] else { return completion(false) }completion(data["status"]!) } } }The code above is similar to the code in the PizzaListTableViewController in the client application, so check back there if you need further explanation.
上面的代碼與客戶端應(yīng)用程序中的PizzaListTableViewController中的代碼相似,因此如果需要進(jìn)一步的說明,請在此處檢查。
There is a createActionForStatus, which is a helper for creating and configuring UIAlertAction object. There is a setStatus method that just attempts to set the status for an order. And then there is the updateOrderStatus method that sends the update request using Alamofire to the API.
有一個createActionForStatus ,它是用于創(chuàng)建和配置UIAlertAction對象的幫助器。 有一個setStatus方法只是嘗試設(shè)置訂單的狀態(tài)。 然后是updateOrderStatus方法,該方法使用Alamofire將更新請求發(fā)送到API。
Next, create the Order.swift and Pizza.swift classes like we did before in the client application:
接下來,像以前在客戶端應(yīng)用程序中一樣創(chuàng)建Order.swift和Pizza.swift類:
// Order.swift import Foundationstruct Order { let id: String let pizza: Pizza var status: OrderStatus }enum OrderStatus: String { case pending = "Pending" case accepted = "Accepted" case dispatched = "Dispatched" case delivered = "Delivered" }// Pizza.swift import UIKitstruct Pizza { let id: String let name: String let description: String let amount: Float let image: UIImageinit(data: [String: Any]) { self.id = data["id"] as! String self.name = data["name"] as! String self.amount = data["amount"] as! Float self.description = data["description"] as! String self.image = data["image"] as! UIImage } }That’s all for the admin application. One last thing we need to do, though, is modify the info.plist file as we did in the client application.
這就是管理應(yīng)用程序的全部內(nèi)容。 但是,我們需要做的最后一件事是像在客戶端應(yīng)用程序中一樣修改info.plist文件。
向我們的送餐iOS應(yīng)用添加推送通知 (Adding Push Notifications to our food delivery iOS app)
At this point, the application works as expected out of the box. We now need to add push notifications to the application to make it more engaging even when the user is not currently using the application.
此時,該應(yīng)用程序可以按預(yù)期工作。 現(xiàn)在,我們需要向應(yīng)用程序添加推送通知,以使其更具吸引力,即使用戶當(dāng)前未使用該應(yīng)用程序也是如此。
?? You need to be enrolled to the Apple Developer program to be able to use the Push Notifications feature. Also, Push Notifications do not work on Simulators, so you will need an actual iOS device to test.
??您需要注冊到Apple Developer程序才能使用Push Notifications功能。 此外,推送通知在模擬器上也不起作用,因此您將需要實際的iOS設(shè)備進(jìn)行測試。
Pusher’s Push Notifications API has first-class support for native iOS applications. Your iOS app instances subscribe to I**nterests**, then your servers send push notifications to those interests. Every app instance subscribed to that interest will receive the notification, even if the app is not open on the device at the time.
Pusher的Push Notifications API對本地iOS應(yīng)用程序具有一流的支持。 您的iOS應(yīng)用實例訂閱了I ** nterests **,然后您的服務(wù)器向這些興趣發(fā)送推送通知。 訂閱該興趣的每個應(yīng)用程序?qū)嵗紩盏酵ㄖ?#xff0c;即使該應(yīng)用程序當(dāng)時不在設(shè)備上打開。
This section describes how you can set up an iOS app to receive transactional push notifications about your food delivery orders through Pusher.
本節(jié)介紹如何設(shè)置iOS應(yīng)用程序以通過Pusher接收有關(guān)您的食品交付訂單的事務(wù)性推送通知。
配置APN (Configure APNs)
Pusher relies on the Apple Push Notification service (APNs) to deliver push notifications to iOS application users on your behalf. When we deliver push notifications, we use your APNs Key. This page guides you through the process of getting an APNs Key and how to provide it to Pusher.
Pusher依靠Apple Push Notification Service(APN)來代表您向iOS應(yīng)用程序用戶傳遞推送通知。 當(dāng)我們傳遞推送通知時,我們將使用您的APNs密鑰。 本頁指導(dǎo)您完成獲取APNs密鑰的過程,以及如何將其提供給Pusher。
Head over to the Apple Developer dashboard by clicking here and then create a new Key as seen below:
通過單擊此處轉(zhuǎn)到Apple Developer儀表板,然后創(chuàng)建一個新密鑰,如下所示:
When you have created the key, download it. Keep it safe as we will need it in the next section.
創(chuàng)建密鑰后,請下載它。 確保安全,因為我們將在下一節(jié)中使用它。
?? You have to keep the generated key safe as you cannot get it back if you lose it.
??您必須保護(hù)生成的密鑰安全,因為如果丟失它將無法找回。
創(chuàng)建您的Pusher應(yīng)用程序 (Creating your Pusher application)
The next thing you need to do is create a new Pusher Push Notification application from the Pusher dashboard.
接下來需要做的是從Pusher儀表板創(chuàng)建一個新的Pusher Push Notification應(yīng)用程序。
When you have created the application, you should be presented with a Quickstart wizard that will help you set up the application.
創(chuàng)建應(yīng)用程序后,應(yīng)顯示一個快速入門向?qū)?#xff0c;該向?qū)椭O(shè)置應(yīng)用程序。
In order to configure Push Notifications, you will need to get an APNs key from Apple. This is the same key as the one we downloaded in the previous section. Once you’ve got the key, upload it to the Quickstart wizard.
為了配置推送通知,您將需要從Apple獲得APNs密鑰。 這與上一節(jié)中下載的密鑰相同。 獲取密鑰后,將其上傳到快速入門向?qū)А?
Enter your Apple Team ID. You can get the Team ID from here. Click on continue to proceed to the next step.
輸入您的Apple Team ID。 您可以從此處獲取團(tuán)隊ID。 單擊繼續(xù)以繼續(xù)下一步。
更新客戶端應(yīng)用程序以支持推送通知 (Updating your client application to support Push Notifications)
In your client application, open the Podfile and add the following pod to the list of dependencies:
在您的客戶端應(yīng)用程序中,打開Podfile并將以下pod添加到依賴項列表中:
pod 'PushNotifications'Now run the pod install command as you did earlier to pull in the notifications package. When installation is complete, create a new class AppMisc.swift and in there paste the following:
現(xiàn)在,像以前一樣運行pod install命令,以獲取通知包。 安裝完成后,創(chuàng)建一個新的AppMisc.swift類,并在其中粘貼以下內(nèi)容:
class AppMisc { static let USER_ID = NSUUID().uuidString.replacingOccurrences(of: "-", with: "_") }In the little class above, we generate a user ID for the session. In a real application, you would typically have an actual user ID after authentication.
在上面的小類中,我們?yōu)闀捝梢粋€用戶ID。 在實際的應(yīng)用程序中,身份驗證后通常會具有實際的用戶ID。
Next open the AppDelegate class and import the PushNotifications package:
接下來打開AppDelegate類并導(dǎo)入PushNotifications包:
import PushNotificationsNow, as part of the AppDelegate class, add the following:
現(xiàn)在,作為AppDelegate類的一部分,添加以下內(nèi)容:
let pushNotifications = PushNotifications.sharedfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.pushNotifications.start(instanceId: "PUSHER_NOTIF_INSTANCE_ID") self.pushNotifications.registerForRemoteNotifications() return true }func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { self.pushNotifications.registerDeviceToken(deviceToken) { try? self.pushNotifications.subscribe(interest: "orders_" + AppMisc.USER_ID) } }? Replace PUSHER_PUSH_NOTIF_INSTANCE_ID with the key given to you by the Pusher application.
? 將P USHER_PUSH_NOTIF_INSTANCE_ID替換為Pusher應(yīng)用程序提供給您的密鑰。
In the code above, we set up push notifications in the application(didFinishLaunchingWithOptions:) method and then we subscribe in the application(didRegisterForRemoteNotificationsWithDeviceToken:) method.
在上面的代碼中,我們在application(didFinishLaunchingWithOptions:)方法中設(shè)置了推送通知,然后在application(didRegisterForRemoteNotificationsWithDeviceToken:)方法中進(jìn)行了訂閱。
Next, we need to enable push notifications for the application. In the project navigator, select your project, and click on the Capabilities tab. Enable Push Notifications by turning the switch ON.
接下來,我們需要為應(yīng)用程序啟用推送通知。 在項目導(dǎo)航器中,選擇您的項目,然后單擊“ 功能”選項卡。 通過打開開關(guān)啟用推送通知 。
更新您的管理應(yīng)用程序以支持推送通知 (Updating your admin application to support Push Notifications)
Your admin application also needs to be able to receive Push Notifications. The process is similar to the set up above. The only difference will be the interest we will be subscribing to in AppDelegate which will be orders.
您的管理應(yīng)用程序還需要能夠接收推送通知。 該過程類似于上面的設(shè)置。 唯一的區(qū)別是我們將在AppDelegate中訂閱的興趣是訂單 。
更新您的API以發(fā)送推送通知 (Updating your API to send Push Notifications)
Push Notifications will be published using our backend server API, which is written in Node.js. For this we will use the Node.js SDK. cd to the backend project directory and run the following command:
推送通知將使用我們的后端服務(wù)器API發(fā)布,該API用Node.js編寫。 為此,我們將使用Node.js SDK 。 cd到后端項目目錄,然后運行以下命令:
$ npm install pusher-push-notifications-node --saveNext, open the index.js file and import the pusher-push-notifications-node package:
接下來,打開index.js文件并導(dǎo)入pusher-push-notifications-node包:
const PushNotifications = require('pusher-push-notifications-node');let pushNotifications = new PushNotifications({ instanceId: 'PUSHER_PUSH_NOTIF_INSTANCE_ID', secretKey: 'PUSHER_PUSH_NOTIF_SECRET_KEY' });Next, we want to add a helper function that returns a notification message based on the order status. In the index.js add the following:
接下來,我們要添加一個輔助函數(shù),該函數(shù)根據(jù)訂單狀態(tài)返回通知消息。 在index.js添加以下內(nèi)容:
function getStatusNotificationForOrder(order) { let pizza = order['pizza'] switch (order['status']) { case "Pending": return false; case "Accepted": return `? Your "${pizza['name']}" is being processed.` case "Dispatched": return `?? Your "${order['pizza']['name']}" is on it’s way` case "Delivered": return `? Your "${pizza['name']}" has been delivered. Bon Appetit.` default: return false; } }Next, in the PUT /orders/:id route, add the following code before the return statement:
Next, in the PUT /orders/:id route, add the following code before the return statement:
let alertMessage = getStatusNotificationForOrder(order)if (alertMessage !== false) { pushNotifications.publish([`orders_${user_id}`], { apns: { aps: { alert: { title: "Order Information", body: alertMessage, }, sound: 'default' } } }) .then(response => console.log('Just published:', response.publishId)) .catch(error => console.log('Error:', error)); }In the code above, we send a push notification to the **orders_${user_id}** interest (user_id is the ID generated and passed to the backend server from the client) whenever the order status is changed. This will be a notification that will be picked up by our client application, since we subscribed for that interest earlier.
In the code above, we send a push notification to the **orders_${user_id}** interest ( user_id is the ID generated and passed to the backend server from the client) whenever the order status is changed. This will be a notification that will be picked up by our client application, since we subscribed for that interest earlier.
Next, in the POST /orders route, add the following code before the return statement:
Next, in the POST /orders route, add the following code before the return statement:
pushNotifications.publish(['orders'], { apns: { aps: { alert: { title: "? New Order Arrived", body: `An order for ${pizza['name']} has been made.`, }, sound: 'default' } } }) .then(response => console.log('Just published:', response.publishId)) .catch(error => console.log('Error:', error));In this case, we are sending a push notification to the orders interest. This will be sent to the admin application that is subscribed to the orders interest.
In this case, we are sending a push notification to the orders interest. This will be sent to the admin application that is subscribed to the orders interest.
That’s all there is to adding push notifications using Pusher. Here are screen recordings of our applications in action:
That's all there is to adding push notifications using Pusher. Here are screen recordings of our applications in action:
Conclusion (Conclusion)
In this article, we created a basic food delivery system and used that to demonstrate how to use Pusher to send Push Notifications in multiple applications using the same Pusher application. Hopefully you learned how you can use Pusher to simplify the process of sending Push Notifications to your users.
In this article, we created a basic food delivery system and used that to demonstrate how to use Pusher to send Push Notifications in multiple applications using the same Pusher application. Hopefully you learned how you can use Pusher to simplify the process of sending Push Notifications to your users.
This post was first published to Pusher.
This post was first published to Pusher .
翻譯自: https://www.freecodecamp.org/news/how-to-build-a-food-delivery-app-with-push-notifications-using-swift-2aa259ffea58/
swift通知欄推送
總結(jié)
以上是生活随笔為你收集整理的swift通知栏推送_如何使用Swift使用推送通知构建食品交付应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到刺猬意味着什么
- 下一篇: react本地储存_如何使用React和