如何使用Swift和Laravel构建一个数字货币追踪程序的后端

数字货币一直是并且仍然是今年最大的趋势之一。随着比特币等货币达到历史新高,新公司创造代币和产品,它显示了数字货币的潜在价值。然而,数字货币价格不稳定,可能会随时下降或上升,因此密切关注这些变化始终是个好主意。

在本文中,我们将构建一个应用程序,密切关注数字货币市场的变化。该应用程序将关注BTC和ETH,并允许应用程序的用户在希望收到有关硬币当前价格的通知时设置最小和最大金额。该应用程序将使用Swift,Laravel,Pusher Channels和Pusher Beams来构建。

必备条件

要跟进,您需要满足以下要求:

我们将要构建什么

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

客户端应用程序如何工作

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

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

后端应用程序如何工作

对于后端应用程序,我们将使用Laravel,我们将创建允许用户更新设置并加载设备设置的端点。API将负责检查加密货币的当前价格,并在价格变化时发送渠道更新和Beams通知。

但是,由于价格变化无法预测,我们将模拟货币变化,以便我们可以预览应用程序。我们还将使用Laravel中的任务调度来触发对当前货币价格的检查。

在生产环境中,我们将调度程序设置为cronjob,但由于我们正在开发中,因此我们将手动运行命令以触发价格更改。

应用程序的外观

完成应用程序后,以下是应用程序的外观:

让我们开始吧。

设置推送通道Channels

登录您的Pusher仪表板。如果您没有帐户,请创建一个帐户。您的信息中心应如下所示:

创建一个新的频道应用。您可以通过点击右下角的创建新频道应用Create new Channels app轻松完成此操作。创建新应用时,会为您提供密钥。保持安全,因为您很快就会需要它们。

设置Pusher Beams

接下来,登录到新的Pusher仪表板,在这里我们将创建一个Pusher Beams实例。如果您还没有帐户,则应该注册。单击侧栏上的Beams按钮,然后单击Create,这将启动弹出窗口以创建新的Beams实例Create a new Beams instance。将其命名为cryptoalat

创建实例后,您将看到快速入门指南。选择IOS快速入门并按照向导进行操作。

完成创建Beams应用程序后,将为您提供实例ID和密钥,稍后我们将需要这些。

设置后端应用程序

在终端中,运行以下命令以创建新的Laravel项目:

1
$ laravel new cryptoapi

此命令将创建一个新的Laravel项目并安装所有必需的Laravel依赖项。

接下来,让我们安装一些项目特定的依赖项。打开composer.json文件,在require属性中添加以下依赖项:

1
2
3
4
5
6
7
// File: composer.json
"require": {
[...]

"neo/pusher-beams": "^1.0",
"pusher/pusher-php-server": "~3.0"
},

现在运行以下命令来安装这些依赖项。

1
$ composer update

安装完成后,在您选择的文本编辑器中打开项目。Visual Studio Code非常好用。

设置我们的Pusher Beams库

我们要做的第一件事是设置我们刚刚使用composer引入的Pusher Beams库。要进行设置,请打开.env文件并添加以下键:

1
2
PUSHER_BEAMS_SECRET_KEY="PUSHER_BEAMS_SECRET_KEY"
PUSHER_BEAMS_INSTANCE_ID="PUSHER_BEAMS_INSTANCE_ID"

您应该使用设置Beams应用程序时获得的密钥替换PUSHER_BEAMS_*占位符。

接下来,打开config/broadcasting.php文件并滚动,直到看到connections键。在那里,您将拥有pusher设置,将以下内容添加到pusher配置中:

1
2
3
4
5
6
7
8
'pusher' => [
// [...]

'beams' => [
'secret_key' => env('PUSHER_BEAMS_SECRET_KEY'),
'instance_id' => env('PUSHER_BEAMS_INSTANCE_ID'),
],
],

设置我们的Pusher Channels库

下一步是设置Pusher Channels。Laravel本身支持Pusher Channels,因此我们不需要做太多设置。

打开.env文件并更新以下密钥:

1
2
3
4
5
6
7
8
BROADCAST_DRIVER=pusher

// [...]

PUSHER_APP_ID="PUSHER_APP_ID"
PUSHER_APP_KEY="PUSHER_APP_KEY"
PUSHER_APP_SECRET="PUSHER_APP_SECRET"
PUSHER_APP_CLUSTER="PUSHER_APP_CLUSTER"

在上方将BROADCAST_DRIVER设置为pusher,然后将其他PUSHER_APP_*键设置为使用从Pusher仪表板获取的键替换占位符。这就是我们为此应用程序设置Pusher Channels所需要做的一切。

构建后端应用程序

现在我们已经设置了所有依赖项,我们可以开始构建应用程序。 我们将从创建路线开始。 但是,我们不是创建控制器来挂钩路由,而是直接将逻辑添加到路由中。

设置数据库,迁移和模型

由于我们将使用数据库,因此我们需要设置我们将要使用的数据库。为了方便起见,我们将使用SQLite。在database目录中创建一个空的database.sqlite文件。

打开.env文件并替换:

1
2
3
4
5
6
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

1
2
DB_CONNECTION=sqlite
DB_DATABASE=/full/path/to/your/database.sqlite

接下来,让我们为devices表创建一个迁移。我们将使用此表来存储设备及其通知设置。这将有助于我们了解要向其发送推送通知的设备。

运行以下命令以创建迁移和模型:

1
$ php artisan make:model Device -m

-m标志将指示instruct artisan在模型旁边创建迁移。

此命令将生成两个文件,即database/migrations的迁移文件和app目录中的模型。我们先编辑迁移文件。

打开database/migrations目录中的*_create_devices_table.php迁移文件,并使用以下内容替换内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateDevicesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('devices', function (Blueprint $table) {
$table->increments('id');
$table->string('uuid')->unique();
$table->float('btc_min_notify')->default(0);
$table->float('btc_max_notify')->default(0);
$table->float('eth_min_notify')->default(0);
$table->float('eth_max_notify')->default(0);
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('devices');
}
}

up方法中,我们定义了devices表的结构。我们有uuid字段,它将是每个注册设备的唯一字符串。我们有两个btc_notify字段,用于保存BTC的最低和最高价格,此时应通知设备。同样适用于* eth_*_notify字段。

要运行迁移,请运行以下命令:

1
$ php artisan migrate

打开app/Device.php模型并使用以下代码替换内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;

class Device extends Model
{
use Notifiable;

public $timestamps = false;

protected $fillable = [
'uuid',
'btc_min_notify',
'btc_max_notify',
'eth_min_notify',
'eth_max_notify',
];

protected $cast = [
'btc_min_notify' => 'float',
'btc_max_notify' => 'float',
'eth_min_notify' => 'float',
'eth_max_notify' => 'float'
];

public function scopeAffected($query, string $currency, $currentPrice)
{
return $query->where(function ($q) use ($currency, $currentPrice) {
$q->where("${currency}_min_notify", '>', 0)
->where("${currency}_min_notify", '>', $currentPrice);
})->orWhere(function ($q) use ($currency, $currentPrice) {
$q->where("${currency}_max_notify", '>', 0)
->where("${currency}_max_notify", '<', $currentPrice);
});
}
}

在上面的模型中,我们将$timestamps属性设置为false,以确保Eloquent不会尝试更新created_atupdated_at字段,这是正常行为。

我们还有scopeAffected方法,它是Eloquent scope的一个例子。我们使用此功能在货币发生价格变动后获取受影响的设备。因此,例如,如果BTC的价格下降,此方法将检查设备和设置,以查看需要通知此更改的设备。

Laravel文档上说,本地范围允许您定义可在整个应用程序中轻松重用的常见约束集。例如,您可能需要经常检索所有被视为热门的用户。要定义范围,请在Eloquent模型方法前加上scope

当我们需要知道要向其发送推送通知的设备时,我们将在稍后的应用程序中使用此范围。

创建路由

打开routes/api.php文件,并使用以下代码替换该文件的内容:

1
2
3
4
// File: routes/api.php
<?php
use App\Device;
use Illuminate\Http\Request;

接下来,让我们添加第一条路由。将以下代码附加到routes文件:

1
2
3
4
// File: routes/api.php
Route::get('/settings', function (Request $request) {
return Device::whereUuid($request->query('u'))->firstOrFail()['settings'];
});

在上面的路由中,我们返回u查询参数中提供的设备的设置。这意味着如果已注册的设备命中/settings端点并通过u参数传递设备UUID,则将返回该设备的设置。

接下来,在相同的路由文件中,将以下内容粘贴到文件的底部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Route::post('/settings', function (Request $request) {
$settings = $request->validate([
'btc_min_notify' => 'int|min:0',
'btc_max_notify' => 'int|min:0',
'eth_min_notify' => 'int|min:0',
'eth_max_notify' => 'int|min:0',
]);

$settings = array_filter($settings, function ($value) { return $value > 0; });

$device = Device::firstOrNew(['uuid' => $request->query('u')]);
$device->fill($settings);
$saved = $device->save();

return response()->json([
'status' => $saved ? 'success' : 'failure'
], $saved ? 200 : 400);
});

上面,我们已经定义了路由POST/settings的路由。此路由将设置保存到数据库。如果设置尚不存在,它将创建一个新条目,否则将更新现有条目。

这就是路由的全部。

创建jobs,事件和通知程序

接下来,我们需要创建将定期运行的Laravel job,以检查货币价格是否有变化。

运行以下命令以创建新的Laravel job:

1
$ php artisan make:job CheckPrices

这将在app目录中创建一个新的CheckPrices类。打开该类并使用以下内容替换内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?php

namespace App\Jobs;

use App\Device;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Events\CurrencyUpdated;
use App\Notifications\CoinPriceChanged;

class CheckPrices implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

protected $supportedCurrencies = ['ETH', 'BTC'];

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$payload = $this->getPricesForSupportedCurrencies();

if (!empty($payload)) {
$this->triggerPusherUpdate($payload);
$this->triggerPossiblePushNotification($payload);
}
}

private function triggerPusherUpdate($payload)
{
event(new CurrencyUpdated($payload));
}

private function triggerPossiblePushNotification($payload)
{
foreach ($this->supportedCurrencies as $currency) {
$currentPrice = $payload[$currency]['current'];

$currency = strtolower($currency);

foreach (Device::affected($currency, $currentPrice)->get() as $device) {
$device->notify(new CoinPriceChanged($currency, $device, $payload));
}
}
}

public function getPricesForSupportedCurrencies(): array
{
$payload = [];

foreach ($this->supportedCurrencies as $currency) {
if (config('app.debug') === true) {
$response = [
$currency => [
'USD' => (float) rand(100, 15000)
]
];
} else {
$url = "https://min-api.cryptocompare.com/data/pricehistorical?fsym={$currency}&tsyms=USD&ts={$timestamp}";

$response = json_decode(file_get_contents($url), true);
}

if (json_last_error() === JSON_ERROR_NONE) {
$currentPrice = $response[$currency]['USD'];

$previousPrice = cache()->get("PRICE_${currency}", false);

if ($previousPrice == false or $previousPrice !== $currentPrice) {
$payload[$currency] = [
'current' => $currentPrice,
'previous' => $previousPrice,
];
}

cache()->put("PRICE_${currency}", $currentPrice, (24 * 60 * 60));
}
}

return $payload;
}
}

在上面的类中,我们实现了ShouldQueue接口。这样就可以使job(作业)排队等候。在生产服务器中,排队作业会使应用程序更快,因为它可能需要一段时间才能执行以便以后执行的作业。

我们在这个课程中有四种方法。第一个是handle方法。执行作业时会自动调用此程序。在此方法中,我们获取可用货币的价格,然后检查价格是否已更改。如果有,我们发布Pusher Channel事件,然后检查是否有任何设备需要根据用户的设置进行通知。如果有,我们会向该设备发送推送通知。

我们有triggerPusherUpdate方法,它触发CurrencyUpdated事件。我们将在下一节中创建此事件。我们还有一个triggerPossiblePushNotification方法,它获取应该通知货币变化的设备列表,然后使用CoinPriceChanged类通知用户,我们将在下一节中创建。

最后,我们有getPricesForSupportedCurrencies方法,getPricesForSupportedCurrencies获取货币的当前价格。在这种方法中,我们有一个模拟货币当前价格的调试模式。

要确保我们刚创建的这个类已正确安排,请打开app/Console/Kernel.php文件,并在schedule方法中,将以下代码添加到schedule方法中:

1
$schedule->job(new \App\Jobs\CheckPrices)->everyMinute();

现在我们每次运行命令php artisan schedule:runschedule方法中的所有作业都将运行。通常,在生产环境中,我们需要将schedule命令添加为cronjob,但是,我们将手动运行此命令。

接下来要做的就是创建通知和事件。 在终端中,运行以下命令:

1
2
$ php artisan make:event CurrencyUpdated    
$ php artisan make:notification CoinPriceChanged

这将在EventsNotifications目录中创建一个类。

在事件类中,CurrencyUpdated粘贴以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class CurrencyUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;

public $payload;

public function __construct($payload)
{
$this->payload = $payload;
}

public function broadcastOn()
{
return new Channel('currency-update');
}

public function broadcastAs()
{
return 'currency.updated';
}
}

在上面的事件类中,我们有broadcastOn方法,它指定我们想要广播事件的Pusher通道。我们还有broadcastAs方法,它指定我们想要广播到频道的事件的名称。

CoinPriceChanged通知类中,使用以下代码替换内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

namespace App\Notifications;

use App\Device;
use Illuminate\Bus\Queueable;
use Neo\PusherBeams\PusherBeams;
use Neo\PusherBeams\PusherMessage;
use Illuminate\Notifications\Notification;

class CoinPriceChanged extends Notification
{
use Queueable;

private $currency;
private $device;
private $payload;

public function __construct(string $currency, Device $device, array $payload)
{
$this->currency = $currency;
$this->device = $device;
$this->payload = $payload;
}

public function via($notifiable)
{
return [PusherBeams::class];
}

public function toPushNotification($notifiable)
{
$currentPrice = $this->payload[strtoupper($this->currency)]['current'];

$previousPrice = $this->payload[strtoupper($this->currency)]['current'];

$direction = $currentPrice > $previousPrice ? 'climbed' : 'dropped';

$currentPriceFormatted = number_format($currentPrice);

return PusherMessage::create()
->iOS()
->sound('success')
->title("Price of {$this->currency} has {$direction}")
->body("The price of {$this->currency} has {$direction} and is now \${$currentPriceFormatted}");
}

public function pushNotificationInterest()
{
$uuid = strtolower(str_replace('-', '_', $this->device->uuid));

return "{$uuid}_{$this->currency}_changed";
}
}

在上面的类中,我们有toPushNotification类,它使用Pusher Beams库准备推送通知。我们还有pushNotificationInterest方法,该方法根据货币和设备ID设置推送通知的兴趣名称。

这就是后端的全部内容,现在只需运行以下命令即可启动服务器:

1
$ php artisan serve

这将启动运行我们的应用程序的PHP服务器。 此外,如果您需要手动触发货币更改,请运行以下命令:

1
$ php artisan schedule:run

现在我们完成了后端,我们可以使用Swift和Xcode创建应用程序。

结论

在本文的这一部分中,我们为加密货币警报应用程序创建了后端。

GitHub上提供了此应用程序的源代码

======================================================================

如果你想学习区块链并在Blockchain Technologies建立职业生涯,那么请查看我们分享的一些以太坊、比特币、EOS、Fabric等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • Hyperledger Fabric 区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、nodejs链码与应用开发的操作实践,是Nodejs工程师学习Fabric区块链开发的最佳选择。
  • Hyperledger Fabric java 区块链开发详解,课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、java链码与应用开发的操作实践,是java工程师学习Fabric区块链开发的最佳选择。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是如何使用Swift和Laravel构建一个数字货币追踪程序的后端