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.pswp.cn/news/394175.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394175.shtml
英文地址,請注明出處:http://en.pswp.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…