swift通知栏推送_如何使用Swift和Laravel使用推送通知创建iOS加密跟踪应用

news/2024/6/29 0:29:13

swift通知栏推送

by Neo Ighodaro

由新Ighodaro

如何使用Swift和Laravel使用推送通知创建iOS加密跟踪应用 (How to create an iOS crypto tracking app with push notifications using Swift and Laravel)

第2部分 (Part 2)

You will need the following installed on your machine: Xcode, the Laravel CLI, SQLite and Cocoapods. Familiarity with the Xcode IDE will be helpful. You should have completed part one of the series.
您将需要在计算机上安装以下软件:Xcode,Laravel CLI,SQLite和Cocoapods。 熟悉Xcode IDE将有所帮助。 您应该已经完成​​了系列的第一部分。

In the first part of this article, we started developing our cryptocurrency alert application. We developed the backend of the application that will power the iOS application. As it stands, our backend application can return settings for a device based on its UUID, save the settings for a device based on its UUID and also figure out what devices to send push notifications to when the currencies update.

在本文的第一部分 ,我们开始开发我们的加密货币警报应用程序。 我们开发了将支持iOS应用程序的应用程序后端。 就目前而言,我们的后端应用程序可以根据其UUID返回设备的设置,根据其UUID保存设备的设置,还可以确定在货币更新时向哪些设备发送推送通知。

In this part, we will focus on creating the iOS application using Swift and Xcode.

在这一部分中,我们将重点介绍使用Swift和Xcode创建iOS应用程序。

先决条件 (Prerequisites)

To follow along you need the following requirements:

要遵循,您需要满足以下要求:

  • Completed part one of this article.

    完成了本文的第一部分 。

  • Xcode installed on your machine.

    Xcode安装在您的计算机上。

  • Knowledge of the Xcode IDE.

    了解Xcode IDE。
  • Basic knowledge using the Laravel framework.

    使用Laravel框架的基本知识。

  • Basic knowledge of the Swift programming language.

    Swift编程语言的基本知识。

  • Laravel CLI installed on your machine.

    您的计算机上已安装Laravel CLI 。

  • SQLite installed on your machine. Installation guide.

    在您的计算机上安装了SQLite。 安装指南 。

  • Cocoapods installed on your machine.

    您的机器上安装了可可足类 。

  • Pusher Beams and Channels application.

    推杆和通道应用程序。

我们将要建设的 (What we will be building)

We already started out by building the backend of the application using Laravel. So next, we will build the iOS application using Swift. If you want to test the push notifications then you will need to run the application on a live device.

我们已经开始使用Laravel构建应用程序的后端。 因此,接下来,我们将使用Swift构建iOS应用程序。 如果要测试推送通知,则需要在实时设备上运行该应用程序。

客户端应用程序将如何工作 (How the client application will work)

For the client app, the iOS application, we will create a simple list that will display the available currencies and the current prices to the dollar. Whenever the price of the cryptocurrency changes, we will trigger an event using Pusher Channels so the prices are updated.

对于客户端应用程序(iOS应用程序),我们将创建一个简单列表,该列表将显示可用货币和美元的当前价格。 每当加密货币的价格发生变化时,我们都会使用Pusher Channels触发事件,以便更新价格。

From the application, you will be able to set a minimum and maximum price change when you want to be alerted. For instance, you can configure the application to send a push notification to the application when the price of one Etherium (ETH) goes below $500. You can also configure the application to receive a notification when the price of Bitcoin goes above $5000.

通过该应用程序,您可以在想要收到警报时设置最小和最大价格变化。 例如,您可以将应用程序配置为在一个以太网(ETH)的价格低于$ 500时向应用程序发送推送通知。 您还可以将应用程序配置为在比特币价格超过5000美元时接收通知。

应用程序的外观 (How the application will look)

When we are done with the application, here’s how the application will look:

处理完应用程序后,以下是该应用程序的外观:

Let’s get started.

让我们开始吧。

设置您的客户端应用程序 (Setting up your client application)

Launch Xcode and click Create a new Xcode project. Select Single View App and click Next. Enter your Product Name, we will call our project cryptoalat, and select Swift from the Language options. You can also change any other detail you wish to on the screen then click Next.

启动Xcode,然后单击“ 创建新的Xcode项目” 。 选择Single View App ,然后单击Next 。 输入您的产品名称 ,我们将我们的项目称为cryptoalat ,然后从“ 语言”选项中选择“ 快速 ”。 您还可以在屏幕上更改任何其他想要的详细信息,然后单击“ 下一步”

安装依赖 (Installing dependencies)

Now you have your Xcode project. Close Xcode and open a terminal window. cd to the iOS project directory in terminal and run the command below to create a Podfile:

现在,您有了Xcode项目。 关闭Xcode并打开一个终端窗口。 cd到终端中的iOS项目目录,然后运行以下命令来创建Podfile:

$ pod init

The Podfile is a specification that describes the dependencies of the targets of one or more Xcode projects. The file should simply be named Podfile. All the examples in the guides are based on CocoaPods version 1.0 and onwards. — Cocoapods Guides

Podfile是一种规范,描述了一个或多个Xcode项目的目标依赖关系。 该文件应简单地命名为Podfile。 指南中的所有示例均基于CocoaPods 1.0版及更高版本。 — 椰壳纲指南

This will generate a new file called Podfile in the root of your project. Open this file in any editor and update the file as seen below:

这将在项目的根目录中生成一个名为Podfile的新文件。 在任何编辑器中打开此文件并更新文件,如下所示:

// File: Podfile    platform :ios, '11.0'
target 'cryptoalat' do      use_frameworks!
pod 'Alamofire', '~> 4.7.2'      pod 'PushNotifications', '~> 0.10.8'      pod 'PusherSwift', '~> 6.1.0'      pod 'NotificationBannerSwift', '~> 1.6.3'    end

If you used a project name other than cryptoalat, then change it in the Podfile to match your project’s target name.

如果您使用的项目名称不是cryptoalat,则在Podfile中对其进行更改以匹配您的项目的目标名称。

Go to terminal and run the command below to install your dependencies:

转到终端并运行以下命令以安装依赖项:

$ pod install

When the installation is complete, you will have a *.xcworkspace file in the root of your project. Open this file in Xcode and let’s start developing our cryptocurrency alert application.

安装完成后,项目的根目录中将有一个*.xcworkspace文件。 在Xcode中打开此文件,让我们开始开发我们的加密货币警报应用程序。

生成iOS应用程序 (Building the iOS application)

创建故事板 (Creating our storyboard)

The first thing we need to do is design our storyboard for the application. This is what we want the storyboard to look like when we are done.

我们需要做的第一件事是为应用程序设计故事板。 这就是我们希望故事板完成后的样子。

Open the Main.storyboard file and design as seen above.

打开Main.storyboard文件并进行设计,如上所示。

Above we have three scenes. The first scene, which is the entry point, is the launch scene. We then draw a manual segue with an identifier called Main. Then we set the segue Kind to Present Modally. This will present the next scene which is a navigation view controller. Navigation controllers already have an attached root view controller by default.

上面我们有三个场景。 第一个场景(即入口点)是发射场景。 然后,我们绘制一个名为Main的手动序列。 然后我们将segue Kind设置为Modal Present 。 这将显示下一个场景,它是导航视图控制器。 默认情况下,导航控制器已经具有附加的根视图控制器。

We will use this attached view controller, which is a TableViewController, as the main view for our application. It’ll list the available currencies and show us a text field that allows us to change the setting for that currency when it is tapped.

我们将使用这个附加的视图控制器TableViewController作为应用程序的主视图。 它将列出可用的货币,并显示一个文本字段,允许我们在点击该货币时更改其设置。

On the third scene, we set the reuse identifier of the cells to coin and we drag two labels to the prototype cell. The first label will be for the coin name and the second label will be for the price.

在第三个场景中,我们将单元的重用标识符设置为硬币,然后将两个标签拖到原型单元上。 第一个标签用于硬币名称,第二个标签用于价格。

Now that we have the scenes, let’s create some controllers and view classes and connect them to our storyboard scenes.

现在我们有了场景,让我们创建一些控制器并查看类并将它们连接到我们的情节提要场景。

创建您的控制器 (Creating your controllers)

In Xcode, create a new class LaunchViewController and paste the contents of the file below into it:

在Xcode中,创建一个新的类LaunchViewController并将以下文件的内容粘贴到其中:

import UIKit
class LaunchViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {            super.viewDidAppear(animated)
SettingsService.shared.loadSettings {                self.routeToMainController()            }        }
fileprivate func routeToMainController() {            performSegue(withIdentifier: "Main", sender: self)        }    }

Set the controller as the custom class for the first scene in the Main.storyboard file.

将控制器设置为Main.storyboard文件中第一个场景的自定义类。

In the code, we load the settings using a SettingsService class we will create later. When the settings are loaded for the device, we then call the routeToMainController method, which routes the application to the main controller using the Main segue we created earlier.

在代码中,我们使用稍后将创建的SettingsService类加载设置。 为设备加载设置后,我们将调用routeToMainController方法,该方法使用我们先前创建的Main segue将应用程序路由到主控制器。

The next controller we will be creating will be the CoinsTableViewController. This will be the controller that will be tied to the third scene which is the main scene.

我们将要创建的下一个控制器将是CoinsTableViewController 。 这将是与第三个场景(主要场景)相关联的控制器。

Create the CoinsTableViewController and replace the contents with the following code:

创建CoinsTableViewController并将内容替换为以下代码:

import UIKit    import PusherSwift    import NotificationBannerSwift
struct Coin {        let name: String        let rate: Float    }
class CoinsTableViewController: UITableViewController {
var coins: [Coin] = []
override func viewDidLoad() {            super.viewDidLoad()        }
override func numberOfSections(in tableView: UITableView) -> Int {            return 1        }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {            return coins.count        }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {            let coin = coins[indexPath.row]            let cell = tableView.dequeueReusableCell(withIdentifier: "coin", for: indexPath) as! CoinTableViewCell
cell.name.text = "1 \(coin.name) ="            cell.amount.text = "$\(String(coin.rate))"
return cell        }    }

Set the controller as the custom class for the first scene in the Main.storyboard file.

将控制器设置为Main.storyboard文件中第一个场景的自定义类。

Above we have defined the Coin struct and it has a name and rate property. We have the controller which we define the coins property as an array of Coins. We then have some boilerplate code that comes with creating a table view controller.

上面我们定义了Coin结构,它具有namerate属性。 我们有一个控制器,我们将coins属性定义为Coin的数组。 然后,我们有了创建表视图控制器时附带的一些样板代码。

The numberOfSections method specifies how many sections the table will have. In the first tableView method, we return the number of coins available and in the second tableView method, we define how we want each row to be handled.

numberOfSections方法指定表将包含多少节。 在第一个tableView方法中,我们返回可用coins数量,在第二个tableView方法中,我们定义如何处理每一行。

创建其他支持类 (Creating other supporting classes)

If you noticed in the code above, we referenced a CoinTableViewCell as the class for each row in the last tableView method. Let’s create that.

如果您在上面的代码中注意到,我们将CoinTableViewCell引用为最后一个tableView方法中每一行的类。 让我们来创建它。

Create a CoinTableViewCell class and paste the following code into it:

创建一个CoinTableViewCell类,并将以下代码粘贴到其中:

class CoinTableViewCell: UITableViewCell {        @IBOutlet weak var name: UILabel!            @IBOutlet weak var amount: UILabel!    }

Open the Main.storyboard file and set the class as the custom class for the prototype cell in the third scene of the Main.storyboard file. When you have set the class, connect the @IBOutlets as specified in the cell class above.

打开Main.storyboard文件,并在Main.storyboard文件的第三个场景Main.storyboard该类设置为原型单元的自定义类。 设置完类后,按照上面的单元格类中的指定连接@IBOutlet

The next class we need to create is the SettingsService. This class will be responsible for updating and fetching the settings for the device.

我们需要创建的下一个类是SettingsService 。 此类将负责更新和获取设备的设置。

Create a new SettingsService class and replace the contents with the following code:

创建一个新的SettingsService类,并将内容替换为以下代码:

import Foundation    import Alamofire    import NotificationBannerSwift
class SettingsService {        static let key = "CryptoAlat"        static let shared = SettingsService()
var settings: Settings? {            get {                return self.getCachedSettings()            }            set(settings) {                if let settings = settings {                    self.updateCachedSettings(settings)                }            }        }
private init() {}
func loadSettings(completion: @escaping() -> Void) {            fetchRemoteSettings { settings in                guard let settings = settings else {                    return self.saveSettings(self.defaultSettings()) { _ in                        completion()                    }                }
self.updateCachedSettings(settings)                completion()            }        }
fileprivate func defaultSettings() -> Settings {            return Settings(                btc_min_notify: 0,                 btc_max_notify: 0,                 eth_min_notify: 0,                 eth_max_notify: 0            )        }
func saveSettings(_ settings: Settings, completion: @escaping(Bool) -> Void) {            updateRemoteSettings(settings, completion: { saved in                if saved {                    self.updateCachedSettings(settings)                }
completion(saved)            })        }
fileprivate func fetchRemoteSettings(completion: @escaping (Settings?) -> Void) {            guard let deviceID = AppConstants.deviceIDFormatted else {                return completion(nil)            }
let url = "\(AppConstants.API_URL)?u=\(deviceID)"            Alamofire.request(url).validate().responseJSON { resp in                if let data = resp.data, resp.result.isSuccess {                    let decoder = JSONDecoder()                    if let settings = try? decoder.decode(Settings.self, from: data) {                        return completion(settings)                    }                }
completion(nil)            }        }
fileprivate func updateRemoteSettings(_ settings: Settings, completion: @escaping(Bool) -> Void) {            guard let deviceID = AppConstants.deviceIDFormatted else {                return completion(false)            }
let params = settings.toParams()            let url = "\(AppConstants.API_URL)?u=\(deviceID)"            Alamofire.request(url, method: .post, parameters: params).validate().responseJSON { resp in                guard resp.result.isSuccess, let res = resp.result.value as? [String: String] else {                    return StatusBarNotificationBanner(title: "Failed to update settings.", style: .danger).show()                }
completion((res["status"] == "success"))            }        }
fileprivate func updateCachedSettings(_ settings: Settings) {            if let encodedSettings = try? JSONEncoder().encode(settings) {                UserDefaults.standard.set(encodedSettings, forKey: SettingsService.key)            }        }
fileprivate func getCachedSettings() -> Settings? {            let defaults = UserDefaults.standard            if let data = defaults.object(forKey: SettingsService.key) as? Data {                let decoder = JSONDecoder()                if let decodedSettings = try? decoder.decode(Settings.self, from: data) {                    return decodedSettings                }            }
return nil        }    }

Above we have the SettingsService. The first method loadSettings loads the settings from the API and then saves it locally. If there is no setting remotely, it calls the defaultSettings method and saves the response to the API.

上面有SettingsService 。 第一种方法loadSettings从API加载设置,然后将其保存在本地。 如果远程没有设置,它将调用defaultSettings方法并将响应保存到API。

The saveSettings method saves the Settings remotely using updateRemoteSettings and then locally using updateCachedSettings. The fetchRemoteSettings gets the settings from the API and decodes the response using the Swift decodable API.

saveSettings方法使用updateRemoteSettings远程保存Settings ,然后使用updateCachedSettings本地保存SettingsfetchRemoteSettings从API获取设置,并使用Swift可解码API解码响应。

Next, let’s define the Settings struct and have it extend Codable. In the same file for the SettingsService, add this above the SettingsService class definition:

接下来,让我们定义Settings结构并将其扩展Codable 。 在SettingsService的同一文件中,将其添加到SettingsService类定义上方:

struct Settings: Codable {        var btc_min_notify: Int?        var btc_max_notify: Int?        var eth_min_notify: Int?        var eth_max_notify: Int?
func toParams() -> Parameters {            var params: Parameters = [:]
if let btcMin = btc_min_notify { params["btc_min_notify"] = btcMin }            if let btcMax = btc_max_notify { params["btc_max_notify"] = btcMax }            if let ethMin = eth_min_notify { params["eth_min_notify"] = ethMin }            if let ethMax = eth_max_notify { params["eth_max_notify"] = ethMax }
return params        }    }

Above we have a simple Settings struct that conforms to Codable. We also have a toParams method that converts the properties to a Parameters type so we can use it with Alamofire when making requests.

上面我们有一个简单的Settings结构,它符合Codable 。 我们还有一个toParams方法,可将属性转换为Parameters类型,以便在发出请求时可以将其与Alamofire一起使用。

One last class we need to create is AppConstants. We will use this class to keep all the data that we expect to remain constant and unchanged throughout the lifetime of the application.

我们需要创建的最后一个类是AppConstants 。 我们将使用此类来保留我们期望在应用程序整个生命周期中保持不变的所有数据。

Create a AppConstants file and paste the following code:

创建一个AppConstants文件并粘贴以下代码:

import UIKit
struct AppConstants {        static let API_URL = "http://127.0.0.1:8000/api/settings"        static let deviceID = UIDevice.current.identifierForVendor?.uuidString        static let deviceIDFormatted = AppConstants.deviceID?.replacingOccurrences(of: "-", with: "_").lowercased()        static let PUSHER_INSTANCE_ID = "PUSHER_BEAMS_INSTANCE_ID"        static let PUSHER_APP_KEY = "PUSHER_APP_KEY"        static let PUSHER_APP_CLUSTER = "PUSHER_APP_CLUSTER"    }

Replace the PUSHER_* keys with the values gotten from the Pusher Channels and Beams dashboard.

PUSHER_*键替换为从Pusher Channels and Beams仪表板获得的值。

更新设备设置 (Updating the settings for the device)

Now that we have defined the settings service, let’s update our controller so the user can set the minimum and maximum prices for each currency.

现在,我们已经定义了设置服务,让我们更新控制器,以便用户可以设置每种货币的最低和最高价格。

Open the CoinsTableViewController class and add the following method:

打开CoinsTableViewController类并添加以下方法:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {        let coin = coins[indexPath.row]
var minTextField: UITextField?        var maxTextField: UITextField?
let title = "Manage \(coin.name) alerts"        let message = "Notification will be sent to you when price exceeds or goes below minimum and maximum price. Set to zero to turn off notification."
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addTextField { textfield in            minTextField = textfield            textfield.placeholder = "Alert when price is below"        }
alert.addTextField { textfield in            maxTextField = textfield            textfield.placeholder = "Alert when price is above"        }
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { action in            guard let minPrice = minTextField?.text, let maxPrice = maxTextField?.text else {                return StatusBarNotificationBanner(title: "Invalid min or max price", style: .danger).show()            }
var btcMin: Int?, btcMax: Int?, ethMin: Int?, ethMax: Int?
switch coin.name {            case "BTC":                btcMin = Int(minPrice)                btcMax = Int(maxPrice)            case "ETH":                ethMin = Int(minPrice)                ethMax = Int(maxPrice)            default:                return            }
let settings = Settings(                btc_min_notify: btcMin,                btc_max_notify: btcMax,                eth_min_notify: ethMin,                eth_max_notify: ethMax            )
SettingsService.shared.saveSettings(settings, completion: { saved in                if saved {                    StatusBarNotificationBanner(title: "Saved successfully").show()                }            })        }))
present(alert, animated: true, completion: nil)    }

The method above is automatically called when a row is selected. In this method, we display a UIAlertController with two text fields for the minimum price and the maximum price. When the prices are submitted, the SettingsService we created earlier takes care of updating the values both locally and remotely.

当选择一行时,上述方法会自动调用。 在此方法中,我们显示一个带有两个文本字段的UIAlertController ,分别是最低价格和最高价格。 提交价格后,我们之前创建的SettingsService将负责在本地和远程更新值。

添加实时加密货币更新支持 (Adding realtime cryptocurrency update support)

Open the CoinsTableViewController and add the pusher property to the class as seen below:

打开CoinsTableViewController并将pusher属性添加到该类中,如下所示:

var pusher: Pusher!

Then replace the viewDidLoad method with the following code:

然后,使用以下代码替换viewDidLoad方法:

override func viewDidLoad() {        super.viewDidLoad()
pusher = Pusher(            key: AppConstants.PUSHER_APP_KEY,             options: PusherClientOptions(host: .cluster(AppConstants.PUSHER_APP_CLUSTER))        )
let channel = pusher.subscribe("currency-update")
let _ = channel.bind(eventName: "currency.updated") { data in            if let data = data as? [String: [String: [String: Float]]] {                guard let payload = data["payload"] else { return }
self.coins = []
for (coin, deets) in payload {                    guard let currentPrice = deets["current"] else { return }                    self.coins.append(Coin(name: coin, rate: currentPrice))                }
Dispatch.main.async {                    self.tableView.reloadData()                }            }        }
pusher.connect()    }

In the code above, we are using the Pusher Swift SDK to subscribe to our currency-update Pusher Channel. We then subscribe to the currency.updated event on that channel. Whenever that event is triggered, we refresh the price of the cryptocurrency in realtime.

在上面的代码中,我们使用Pusher Swift SDK订阅了currency-update Pusher Channel。 然后,我们在该频道上订阅currency.updated事件。 每当触发该事件时,我们都会实时刷新加密货币的价格。

将推送通知添加到我们的iOS新应用程序 (Adding push notifications to our iOS new application)

To add push notification support, open the AppDelegate class and replace the contents with the following:

要添加推送通知支持,请打开AppDelegate类,并将内容替换为以下内容:

import UIKit    import PushNotifications
@UIApplicationMain    class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {            PushNotifications.shared.start(instanceId: AppConstants.PUSHER_INSTANCE_ID)            PushNotifications.shared.registerForRemoteNotifications()            return true        }
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {            PushNotifications.shared.registerDeviceToken(deviceToken) {                if let deviceID = AppConstants.deviceIDFormatted {                    try? PushNotifications.shared.subscribe(interest: "\(deviceID)_eth_changed")                    try? PushNotifications.shared.subscribe(interest: "\(deviceID)_btc_changed")                }            }        }    }

In the class above, we use the Pusher Beams Swift SDK to register the device for push notifications. We then subscribe to the *_eth_changed and *_btc_changed interests, where * is the device’s unique UUID.

在上面的类中,我们使用Pusher Beams Swift SDK注册用于推送通知的设备。 然后,我们订阅*_eth_changed*_btc_changed兴趣,其中*是设备的唯一UUID。

Now that we have completed the logic for the application, let’s enable push notifications on the application in Xcode.

现在,我们已经完成了应用程序的逻辑,让我们在Xcode中启用应用程序的推送通知。

In the project navigator, select your project, and click on the Capabilities tab. Enable Push Notifications by turning the switch ON.

在项目导航器中,选择您的项目,然后单击“ 功能”选项卡。 通过打开开关启用推送通知 。

This will create an entitlements file in the root of your project. With that, you have provisioned your application to fully receive push notifications.

这将在项目的根目录中创建一个权利文件。 这样,您就可以调配应用程序以完全接收推送通知。

允许我们的应用程序本地连接 (Allowing our application to connect locally)

If you are going to be testing the app’s backend using a local server, then there is one last thing we need to do. Open the info.plist file and add an entry to the plist file to allow connection to our local server:

如果您要使用本地服务器测试应用程序的后端,那么我们需要做的最后一件事。 打开info.plist文件,然后在plist文件中添加一个条目,以允许连接到我们的本地服务器:

That’s all. We can run our application. However, remember that to demo the push notifications, you will need an actual iOS device as simulators cannot receive push notifications. If you are using a physical device, you’ll need to expose your local API using Ngrok and then change the API_URL in AppConstants.

就这样。 我们可以运行我们的应用程序。 但是, 请记住,要演示推送通知,您将需要一台实际的iOS设备,因为模拟器无法接收推送通知。 如果您使用的是物理设备,则需要使用Ngrok公开本地API,然后 AppConstants更改API_URL

Anytime you want to update the currency prices, run the command below manually in your Laravel application:

每当您想更新货币价格时,请在L​​aravel应用程序中手动运行以下命令:

$ php artisan schedule:run

Here is a screen recording of the application in action:

这是正在运行的应用程序的屏幕录像:

结论 (Conclusion)

In this article, we have been able to see how easy it is to create a cryptocurrency alert website using Laravel, Swift, Pusher Channels and Pusher Beams. The source code to the application built in this article is available on GitHub.

在本文中,我们已经看到使用Laravel,Swift,Pusher Channels和Pusher Beams创建一个加密货币警报网站是多么容易。 GitHub上提供了本文构建的应用程序的源代码。

This article was first published on Pusher.

本文最初在Pusher上发表。

翻译自: https://www.freecodecamp.org/news/create-a-cryptocurrency-tracking-app-with-push-notifications-using-swift-and-laravel-part-2-the-6275674a12f/

swift通知栏推送


http://lihuaxi.xjx100.cn/news/238837.html

相关文章

Python 数据库操作 psycopg2

文章目录安装基本使用安装 psycopg 是 Python 语言中 PostpreSQL数据库接口 安装环境: Python:v2.7, v3.4~3.8PostGreSQL:7.4~12 pip install psycopg2基本使用 import psycopg2def connect_db(host: str,port: int,database: str,user:…

php.ini 中开启短标签

控制参数&#xff1a; short_open_tag On如果设置为Off&#xff0c;则不能正常解析类似于这样形式的php文件&#xff1a;<?phpinfo()?>而只能解析<?phpphpinfo()?>这样形式的php文件所以要想php支持短标签&#xff0c;需要我们把short_open_tag 设置为On. 本…

中国联通备战5G MWC发布《Edge-Cloud平台架构及产业生态白皮书》

2月26日&#xff0d;3月1日&#xff0c;中国联通受邀参加2018MWC世界移动通信大会&#xff0c;作为本次大会GSMA智慧城市展区参展的唯一中国运营商&#xff0c;中国联通提出以服务为驱动的面向5G网络切片的演进思路&#xff0c;为客户提供4G到5G演进阶段的一致性的网络服务&…

区块链到底是什么

想指代更多区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 1.什么是区块链&#xff1f; 区块链是通过去中心&#xff0c;化去信任的方式集体维护一个可靠数据库的技术方案。参与系统中的任意多个节点把系统一段时间内的全部的信息&#xff0c;数据通过密码学算法…

在后台代码中引入XAML的方法

本文将介绍三种方法用于在后台代码中动态加载XAML&#xff0c;其中有两种方法是加载已存在的XAML文件&#xff0c;一种方法是将包含XAML代码的字符串转换为WPF的对象。 这些是我在编写RegeX时获得的经验&#xff0c;它们将会给WPF程序带来更多的灵活性。 一、在资源字典中载入项…

【学习笔记】git 使用文档

安装 git # mac 环境 brew install git检查是否安装成功 ➜ ~ git --version git version 2.20.1 (Apple Git-117)卸载 git ➜ ~ which -a git /usr/bin/git ➜ ~ cd /usr/bin ➜ bin sudo rm -rf git*git init 命令 对一个空文件&#xff0c;git 初始化。文件名称增加…

stackoverflow_StackOverflow帐户如何确保您在公认的开发人员表格中占有一席之地

stackoverflowby Melchor Tatlonghari由Melchor Tatlonghari StackOverflow帐户如何确保您在公认的开发人员表格中占有一席之地 (How a StackOverflow account can secure you a seat at the recognised developer table) I have never met a developer who hasn’t heard of …

AR介绍

AR介绍 AR全名扩增实境&#xff0c;是一种实时融合现实与虚拟的图像技术。 AR技术的三板斧&#xff1a;感知&#xff08;寻找目标定位位置-与环境交互&#xff09;&#xff0c;渲染&#xff08;实现产品交互-与客户交互&#xff09;&#xff0c;追踪&#xff08;捕捉目标运动轨…