swift通知栏推送_如何使用Swift使用推送通知构建食品交付应用

swift通知栏推送

by Neo Ighodaro

由新Ighodaro

如何使用Swift使用推送通知构建食品交付应用 (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.

要学习本教程,需要对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.

最后一英里的运送市场使从移动设备订购食物变得很容易,并且可以在仍然很热的时候将食物运送到用户家中。

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这样的市场都使用您设备的位置为您提供了一个距离您足够近(开着)的餐馆列表,因此您可以尽快获得送货。

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.

客户,餐厅和驾驶员之间的这种实时体验依赖于交易推送通知,以将订单从厨房无缝地转移到桌子上。 客户希望推送通知在他们的订单即将到来时以及何时需要在门口与驾驶员见面时向他们发出警报。

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.

设置推送通知可能会造成混乱和耗时。 但是,使用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.

在本文中,我们将考虑如何在具有事务性推送通知的iOS上构建应用程序。 为此,我们将构建一个虚构的外卖应用程序。

先决条件 (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帐户。 在此处创建一个 。

  • Basic knowledge of JavaScript/Node.js (Check out this tutorial).

    JavaScript / Node.js的基础知识( 请参阅本教程 )。

  • Cocoapods installed on your machine.

    您的机器上已安装了 Cocoapods。

Once you have the requirements, let’s start.

有了需求后,就开始吧。

构建我们的应用程序-规划 (Building our application — planning)

Before we start building our application, we need to do some planning on how we want the application to work.

在开始构建应用程序之前,我们需要对我们希望应用程序的工作方式进行一些规划。

We will be making three applications:

我们将提出三个申请:

  • The backend application (Web using Node.js).

    后端应用程序(使用Node.js的Web)。
  • The client application (iOS using Swift).

    客户端应用程序(使用Swift的iOS)。
  • The admin application (iOS using Swift).

    管理应用程序(使用Swift的iOS)。

后端应用 (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应用程序中调用API。 该API应该能够提供食品库存,订单以及管理订单。 我们还将从后端应用程序发送推送通知。

客户端应用 (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.

这将是与客户一起使用的应用程序。 用户可以在这里订购食物。 为简单起见,我们将没有任何形式的身份验证,并且所有内容都直截了当。 客户应该能够看到库存并从该库存中订购一件或多件东西。 他们还应该能够看到他们的订单列表以及每个订单的状态。

管理员应用程序 (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.

这将是提供服务的公司用于履行订单的应用程序。 该应用程序将显示可用的订单,管理员将能够设置每个订单的状态。

构建后端应用程序(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.

我们要构建的第一件事是API。 我们将添加支持我们的iOS应用程序所需的所有内容,然后在以后添加推送通知。

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创建一个项目目录。 在目录中,创建一个名为package.json的新文件。 在文件中,粘贴以下内容:

{      "main": "index.js",      "scripts": {},      "dependencies": {        "body-parser": "^1.18.2",        "express": "^4.16.2"      }    }

Next run the command below in your terminal:

接下来在您的终端中运行以下命令:

$ npm install

This 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文件相同的目录中创建一个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应用程序。 一切都是不言自明的,并有注释可以指导您。

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 ,我们显示内存数据存储中可用的订单列表。 在第二条路线POST /orders ,我们只是将新订单添加到orders列表中。 在第三条路线PUT /orders/:id ,我们只是从orders列表中修改单个订单的状态。 在第四条路线GET /inventory ,我们从数据库中的清单列表中列出可用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:

目前,我们已经完成了API的使用,当我们需要添加推送通知代码时,我们将对其进行重新访问。 如果要测试API是否正常运行,请在终端上运行以下命令:

$ node index.js

This will start a new Node server listening on port 4000.

这将启动在端口4000上侦听的新Node服务器。

构建客户端应用程序 (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中构建客户端应用程序。 首先,启动Xcode并创建一个新的“单一应用程序”项目。 我们将项目命名为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:

创建项目后,退出Xcode并在刚创建的Xcode项目的根目录中创建一个名为Podfile的新文件。 在文件中,粘贴以下代码:

platform :ios, '11.0'
target 'PizzareaClient' do      use_frameworks!      pod 'PusherSwift', '~> 5.1.1'      pod 'Alamofire', '~> 4.6.0'    end

In 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 更改 为您的项目名称。 现在在您的终端中,运行以下命令以安装依赖项:

$ pod install

After the installation is complete, open the Xcode workspace file that was generated by Cocoapods. This should relaunch Xcode.

安装完成后,打开由Cocoapods生成的Xcode工作区文件。 这应该重新启动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文件。 在其中,我们将为客户应用程序创建情节提要。 以下是我们如何设计故事板的屏幕截图:

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.

第一个场景是导航视图控制器,它具有一个表视图控制器作为根控制器。 导航控制器是启动应用程序时加载的初始控制器。

创建披萨列表场景 (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中创建一个名为PizzaTableListViewController.swift的新文件,使其成为第二个场景的自定义类,然后粘贴以下代码:

import UIKit    import Alamofire
class 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! PizzaTableViewCell
cell.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].description
return 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方法中,我们调用fetchInventory方法,该方法使用Alamofire从后端API中获取清单。 然后,将响应保存到控制器的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按钮。 这只是呈现使用名为赛格瑞订单列表现场orders

The tableView* methods implement methods available to the UITableViewDelegate protocol and should be familiar to you.

tableView*方法实现了UITableViewDelegate协议可用的方法,您应该熟悉它们。

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发送到导航中的视图控制器。 但这种pizza只送过来,如果要加载的视图控制器是PizzaViewController

Before we create the third scene, create a PizzaTableViewCell.swift class and paste in the following:

在创建第三个场景之前,请创建PizzaTableViewCell.swift类并粘贴以下内容:

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 ,并且可重复使用的标识符是Pizza

创建披萨视图场景 (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.

我们的情节提要中的第三个场景是Pizza视图场景。 在这里可以查看所选库存。

Create a PizzaViewController.swift file, make it the custom class for the scene above, and paste in the following code:

创建一个PizzaViewController.swift文件,使其成为上面场景的自定义类,然后粘贴以下代码:

import UIKit    import Alamofire
class 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 。 您需要从情节提要中将插座和动作链接到控制器。

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我们设置出口,以便它们使用从前一个视图控制器发送的pizza显示正确的值。 buyButtonPressed方法使用Alamofire通过向API发送请求来下订单。 其余方法处理显示来自API的错误或成功响应。

创建订单清单场景 (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:

下一个场景是“订单”列表场景。 在此场景中,列出了所有订单,因此用户可以查看它们及其状态:

Create a OrderTableViewController.swift file, make it the custom class for the scene above, and paste in the following code:

创建一个OrderTableViewController.swift文件,使其成为上面场景的自定义类,然后粘贴以下代码:

import UIKit    import Alamofire
class 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方法,该方法只是关闭控制器并返回到清单清单场景。

创建订单状态场景 (Creating the Order Status Scene)

The next scene is the Order scene. In this scene, we can see the status of the order:

下一个场景是订购场景。 在此场景中,我们可以看到订单的状态:

⚠️ 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️上面的场景在状态标签的正上方具有不可见的视图。 您需要使用此视图为控制器创建@IBOutlet

Create a OrderViewController.swift file, make it the custom class for the scene above, and paste in the following code:

创建一个OrderViewController.swift文件,使其成为上面场景的自定义类,然后粘贴以下代码:

import UIKit
class OrderViewController: UIViewController {
var order: Order?
@IBOutlet weak var status: UILabel!        @IBOutlet weak var activityView: ActivityIndicator!
override func viewDidLoad() {            super.viewDidLoad()
navigationItem.title = order?.pizza.name
activityView.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.

在上面的代码中,我们正在执行viewDidLoad方法中的所有工作。 在那里,我们有ActivityIndicator类,接下来将创建该类,将其称为@IBOutlet

创建应用程序的其他部分 (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获得,因此我们选择自己创建并导入。

Create a new file in Xcode called ActivityIndicator and paste the code from the repo here into it.

在Xcode中创建一个名为ActivityIndicator的新文件,并将代码从仓库中粘贴到其中。

Next, create a new Order.swift file and paste in the following code:

接下来,创建一个新的Order.swift文件并粘贴以下代码:

import Foundation
struct 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:

最后,创建Pizza.swift并粘贴以下代码:

import UIKit
struct Pizza {        let id: String        let name: String        let description: String        let amount: Float        let image: UIImage
init(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:

这就是客户端应用程序的全部内容。 但是,我们需要做的最后一件事是修改info.plist文件。 我们需要在plist文件中添加一个条目,以允许连接到我们的本地服务器:

Let’s move on to the admin application.

让我们继续进行管理应用程序。

生成管理应用程序 (Building the admin application)

Launch a new instance of Xcode and create a new ‘Single Application’ project. We will name our project PizzaareaAdmin.

启动Xcode的新实例并创建一个新的“单一应用程序”项目。 我们将我们的项目命名为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:

创建项目后,退出Xcode并在刚创建的Xcode项目的根目录中创建一个名为Podfile的新文件。 在文件中,粘贴以下代码:

platform :ios, '11.0'
target 'PizzareaAdmin' do      use_frameworks!      pod 'PusherSwift', '~> 5.1.1'      pod 'Alamofire', '~> 4.6.0'    end

In 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:

现在,在您的终端中,运行以下命令以安装依赖项:

$ pod install

After the installation is complete, open the Xcode workspace file that was generated by Cocoapods. This should relaunch Xcode.

安装完成后,打开由Cocoapods生成的Xcode工作区文件。 这应该重新启动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文件。 在这里,我们将为客户应用程序创建情节提要。 以下是我们如何设计故事板的屏幕截图:

Above we have a navigation view controller that is the initial view controller.

上面我们有一个导航视图控制器,它是初始视图控制器。

创建订单清单场景 (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.

订单列表场景应该显示客户订单的列表。 从那里我们可以在需要时更改每个订单的状态。

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中创建一个名为OrdersListViewController.swift的新文件,使其成为第二个场景的自定义类,并粘贴以下代码:

import UIKit    import Alamofire
class 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 : .default
let 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.

上面的代码与客户端应用程序中的PizzaListTableViewController中的代码相似,因此如果需要进一步的说明,请在此处检查。

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 ,它是用于创建和配置UIAlertAction对象的帮助器。 有一个setStatus方法只是尝试设置订单的状态。 然后是updateOrderStatus方法,该方法使用Alamofire将更新请求发送到API。

Next, create the Order.swift and Pizza.swift classes like we did before in the client application:

接下来,像以前在客户端应用程序中一样创建Order.swiftPizza.swift类:

// Order.swift    import Foundation
struct 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 UIKit
struct Pizza {        let id: String        let name: String        let description: String        let amount: Float        let image: UIImage
init(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.

这就是管理应用程序的全部内容。 但是,我们需要做的最后一件事是像在客户端应用程序中一样修改info.plist文件。

向我们的送餐iOS应用添加推送通知 (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.

此时,该应用程序可以按预期工作。 现在,我们需要向应用程序添加推送通知,以使其更具吸引力,即使用户当前未使用该应用程序也是如此。

⚠️ 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设备进行测试。

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应用程序具有一流的支持。 您的iOS应用实例订阅了I ** nterests **,然后您的服务器向这些兴趣发送推送通知。 订阅该兴趣的每个应用程序实例都会收到通知,即使该应用程序当时不在设备上打开。

This section describes how you can set up an iOS app to receive transactional push notifications about your food delivery orders through Pusher.

本节介绍如何设置iOS应用程序以通过Pusher接收有关您的食品交付订单的事务性推送通知。

配置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应用程序用户传递推送通知。 当我们传递推送通知时,我们将使用您的APNs密钥。 本页指导您完成获取APNs密钥的过程,以及如何将其提供给Pusher。

Head over to the Apple Developer dashboard by clicking here and then create a new Key as seen below:

通过单击此处转到Apple Developer仪表板,然后创建一个新密钥,如下所示:

When you have created the key, download it. Keep it safe as we will need it in the next section.

创建密钥后,请下载它。 确保安全,因为我们将在下一节中使用它。

⚠️ You have to keep the generated key safe as you cannot get it back if you lose it.

⚠️您必须保护生成的密钥安全,因为如果丢失它将无法找回。

创建您的Pusher应用程序 (Creating your Pusher application)

The next thing you need to do is create a new Pusher Push Notification application from the Pusher dashboard.

接下来需要做的是从Pusher仪表板创建一个新的Pusher Push Notification应用程序。

When you have created the application, you should be presented with a Quickstart wizard that will help you set up the application.

创建应用程序后,应显示一个快速入门向导,该向导将帮助您设置应用程序。

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密钥。 这与上一节中下载的密钥相同。 获取密钥后,将其上传到快速入门向导。

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。 您可以从此处获取团队ID。 单击继续以继续下一步。

更新客户端应用程序以支持推送通知 (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:

在您的客户端应用程序中,打开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:

现在,像以前一样运行pod install命令,以获取通知包。 安装完成后,创建一个新的AppMisc.swift类,并在其中粘贴以下内容:

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.

在上面的小类中,我们为会话生成一个用户ID。 在实际的应用程序中,身份验证后通常会具有实际的用户ID。

Next open the AppDelegate class and import the PushNotifications package:

接下来打开AppDelegate类并导入PushNotifications包:

import PushNotifications

Now, as part of the AppDelegate class, add the following:

现在,作为AppDelegate类的一部分,添加以下内容:

let pushNotifications = PushNotifications.shared
func 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应用程序提供给您的密钥。

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:)方法中设置了推送通知,然后在application(didRegisterForRemoteNotificationsWithDeviceToken:)方法中进行了订阅。

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.

接下来,我们需要为应用程序启用推送通知。 在项目导航器中,选择您的项目,然后单击“ 功能”选项卡。 通过打开开关启用推送通知 。

更新您的管理应用程序以支持推送通知 (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.

您的管理应用程序还需要能够接收推送通知。 该过程类似于上面的设置。 唯一的区别是我们将在AppDelegate中订阅的兴趣是订单

更新您的API以发送推送通知 (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:

推送通知将使用我们的后端服务器API发布,该API用Node.js编写。 为此,我们将使用Node.js SDK 。 cd到后端项目目录,然后运行以下命令:

$ npm install pusher-push-notifications-node --save

Next, open the index.js file and import the pusher-push-notifications-node package:

接下来,打开index.js文件并导入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:

接下来,我们要添加一个辅助函数,该函数根据订单状态返回通知消息。 在index.js添加以下内容:

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通知栏推送

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/394175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins持续集成实践之java项目自动化部署

关于Linux安装Jenkins可以参考我的这篇博文Ubuntu16.04环境安装jenkins 1.安装部署插件 进入插件管理,并搜索该插件Deploy to container Plugin进行安装 ,下载地址为:https://wiki.jenkins-ci.org/display/JENKINS/DeployPlugin 2.安装完后&a…

云计算时代企业内部IT人员的新定位

本文讲的是云计算时代企业内部IT人员的新定位,【IT168 云计算频道】渐渐的云计算热起来,但是怎么去严格定义云计算,还是没有一个统一的说法,最常用的就是举例子的方式来说什么是云计算,最常用来打比方的是电力&#xf…

Java 多线程 笔记 转自http://www.cnblogs.com/lwbqqyumidi/p/3804883.html

多线程作为Java中很重要的一个知识点, 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基…

leetcode207. 课程表(dfs/bfs)

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1] 给定课程总量以及它们的先决条件…

r.java是什么_R.java文件介绍

http://blog.chinaunix.net/uid-21411227-id-4133828.html注意:R.java文件不能手动修改。1. HelloWorld工程中的R.java文件解析package com.android.hellworld;public final class R {public static final class attr {}public static final class drawable {public…

python qt 拖拽组件使用方法_Python QT组件库qtwidgets的使用

虽然Qt提供了不少现成的组件,但是在Python中使用PyQt5或PySide2进行图形界面程序开发的过程,还是免不了要根据自己的需求组合一些小部件以形成新的自定义组件。最近州的先生在写一个桌面图形界面的登录密码框的过程中,发现了这样一个小巧的自…

get与post区别

两种 HTTP 请求方法:GET 和 POST 在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST。 GET - 从指定的资源请求数据。POST - 向指定的资源提交要被处理的数据GET 方法 请注意,查询字符串(名称/…

java 实现 sql join_Sql 数据库 join 连接

sql里面有两个连接一个是union,另一个就是join他们两个的区别:union 连接的是行 是一行一行的连 而 join 连接的是列(字段) (他们俩的区别暂时就就知道这点)join连接的使用的前提:1.必须要有至少一个表(一个表可以用自连接)2.必须要有相关联的列(字段)&#xff…

开源与云计算

本文讲的是开源与云计算,【IT168 资讯】几年来我一直担心开源运动可能会遭受Kim Stanley Robinson在“Green Mars”中精辟论述的问题:“历史的浪潮比我们做得还要快。”创新者被抛在后面,他们曾经改变的世界拿着他们的主意向着意想不到的方向…

c/c++连接mysql数据库设置及乱码问题(vs2013连接mysql数据库,使用Mysql API操作数据库)...

我的安装环境: (1)vs2013(32位版) (vs2013只有32位的 没有64位的,但是它可以编译出64位的程序) ; (2)mysql-5.7.15(64位) vs2013中的设置(按步骤来,顺序不要乱) (1)首先在vs2013中新建一个控制台程序 Mysq…

leetcode542. 01 矩阵(bfs/dp)

给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 0 0 0 bfs代码 class Solution {int[][] res;public int[][] updateMatrix(int[][] matrix) {int[][] dirnew…

react本地储存_如何使用React和本地存储构建freeCodeCamp的配方框

react本地储存by Edward Njoroge爱德华尼约格(Edward Njoroge) 如何使用React和本地存储构建freeCodeCamp的配方框 (How to build freeCodeCamp’s recipe box using React and local storage) I completed my first edition of the Free Code Camp recipe box project on May…

调用接口返回500_公交卡余额查询接口开放使用啦!

API说明本API返回数据仅支持JSON格式且会对中文进 行unicode 编码,JSON格式返回数据基本格式如下:{"errCode": 0,"errMsg": "OK","data": {}}其中 errCode 表示请求状态,0表示请求成功, …

stark组件开发之组合搜索基本显示

数据的获取,上一篇,已经有了!然后就是,如何进行展示的问题。到了展示这里,又有了新的问题, 因为从数据库,取得的数据。 分为 queryset 和 tuple 两种数据结构。tuple 中,只是字符串。…

美国安全厂商在云安全上的最新进展

本文讲的是美国安全厂商在云安全上的最新进展,【IT168 资讯】优利系统公司日前推出了一系列云产品和服务,并且着重强调企业创建私有云,公有云或混合云工具的安全。  Unisys Secure Cloud是优利系统公司推出的一种管理云服务,承诺…

hessianphp java_hessian 在PHP中的使用

一、hessian是什么?看到这个单词我还不知道怎么读,音标是[hes]读黑森。Hessian是一个轻量级的远程的数据交换工具,使用简单的方法提供了RMI(远程方法调用)的功能. 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议&…

leetcode1025. 除数博弈(dp/数学)

爱丽丝和鲍勃一起玩游戏&#xff0c;他们轮流行动。爱丽丝先手开局。 最初&#xff0c;黑板上有一个数字 N 。在每个玩家的回合&#xff0c;玩家需要执行以下操作&#xff1a; 选出任一 x&#xff0c;满足 0 < x < N 且 N % x 0 。 用 N - x 替换黑板上的数字 N 。 如…

100万用户服务器_我的应用在一个月内如何增长超过100万用户

100万用户服务器by Assaf Elovic通过阿萨夫埃洛维奇 我的应用在一个月内如何增长超过100万用户 (How my app grew by over 1M users in one month) 只需要这种简单的每周方法和耐心。 (All it took was this simple weekly approach and patience.) Building and promoting a …

原生支付url参数错误_小程序支付

下载微信JSAPI支付的 SDK : https://pay.weixin.qq.com/wiki/doc/api/download/WxpayAPI_php.zip &#xff1b;解压后放在extend 文件夹下&#xff0c;命名为wepay下载你的商户证书&#xff0c;放在extend/wepay/cert/ 文件夹下面。自行将 extend/wepay/example/WxPay.Config.p…

Android清理设备内存具体完整演示样例(二)

版权声明&#xff1a; https://blog.csdn.net/lfdfhl/article/details/27672913 MainActivity例如以下: package cc.c;import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.List; import android.app.Activity; import a…