Swift开发服务端(三):Perfect再次初体验


多少年后当眼前的一切成为结局,时间改变了我,改变了村里的一切。整个老掉的一代人,坐在黄昏里感叹岁月流逝、沧桑巨变。没人知道有些东西是被我改变的。在时间经过这个小村庄的时候,我帮了时间的忙,让该变的一切都有了变迁。我老的时候,我会说:我是在时光中老的。

——刘亮程《一个人的村庄》

简介

  • Perfect是一组完整、强大的工具箱、软件框架体系和Web应用服务器,可以在LinuxmacOS (OS X)上使用。该软件体系为Swift工程师量身定制了一整套用于开发轻量、易维护、规模可扩展的Web应用及其它REST服务的解决方案,这样Swift工程师就可以实现同时在服务器和客户端上采用同一种语言开发软件项目。
  • 由于建立在一个高性能异步网络引擎基础上,Perfect还能够在FastCGI上运行,支持安全套接字加密(SSL)。该软件体系还包含很多其它互联网服务器所需要的特点,包括WebSocketsiOS消息推送,而且很快会有更多强大的功能支持。

  • Swift开发服务端(一):MySQL安装及配置

  • Swift开发服务端(二):使用Parallels Desktop虚拟机安装Ubuntu系统及相关配置

正文

  • 有很多小伙伴肯定在想,我为什么会把第一次给了Perfect🌝,那是因为Perfect有官方中文文档,虽然不是非常完善的中文文档,但有胜于无,减少了入门的难度。
  • 什么,为什么叫再次初体验。那是因为上次的初体验不算。为什么不算、因为我说的🌚🌚。
  • 本篇完全基于Mac系统,为什么不基于上一篇安装的Ubuntu系统呢,因为相较于Mac,在Ubuntu下只是多了Swift安装环境配置和其他需要的服务安装,都特别简单,更重要的是我们需要使用到Xcode进行编码、编译。
  • Perfect官方文档在这里

Ubuntu安装Swift

  • 关于在Ubuntu系统下载安装Swift,参考Swift官方文档,已经介绍的非常详细了。
  • 如果你不想看英文官方文档,好吧,那我就再总结(复制)一下:

安装Git

  • 首先检查系统有没有安装Git,如果没有安装,直接使用包管理器安装:
1
sudo apt-get install git

安装Swift依赖组件

  • 确保您的系统上的包管理器是最新的:
1
sudo apt-get update
  • 安装Swift必要的依赖组件,如clangPython等。
    • openssh-server 我们在安装Ubuntu一章中已经安装过,所以不用重复安装了。
1
sudo apt-get install clang libicu-dev libpython2.7

安装Swift

  • 下载最新的Swift二进制文件,可以手动下载,也可以使用wget下载。
    • 根据自己的Ubuntu系统版本和需要的swift版本选择,在这里下载
    • .tar.gz后缀的是工具链二进制文件 .sig 后缀的是数字签名文件。
1
wget https://swift.org/builds/swift-4.0-release/ubuntu1604/swift-4.0-RELEASE/swift-4.0-RELEASE-ubuntu16.04.tar.gz
  • 如果你是第一次下载安装Swift包,需要将Swift的PGP密钥导入您的密钥环,密钥将用于验证下载的文件有无被被损坏或篡改。
1
2
3
4
5
6
gpg --keyserver hkp://pool.sks-keyservers.net \
--recv-keys \
'7463 A81A 4B2E EA1B 551F FBCF D441 C977 412B 37AD' \
'1BE1 E29A 084C B305 F397 D62A 9F59 7F4D 21A5 6D5F' \
'A3BA FD35 56A5 9079 C068 94BD 63BC 1CFE 91D3 06C6' \
'5E4D F843 FB06 5D7F 7E24 FBA2 EF54 30F0 71E1 B235'
  • 导入密钥之后,下载对应Swift版本的签名文件:
1
wget https://swift.org/builds/swift-4.0-release/ubuntu1604/swift-4.0-RELEASE/swift-4.0-RELEASE-ubuntu16.04.tar.gz.sig
  • 验证签名文件:
    • 如果出现警告信息,可忽略;如果出现错误信息,就需要重新下载Swift二进制文件。
1
gpg --verify swift-4.0-RELEASE-ubuntu16.04.tar.gz.sig
  • 安装完成密钥和签名文件,现在就可以安装Swift。 执行以下命令来提取之前下载的二进制文件:
1
tar xzf swift-4.0-RELEASE-ubuntu16.04.tar.gz
  • 将Swift工具链添加到环境变量中,以便运行swift命令:
1
export PATH=swift-4.0-RELEASE-ubuntu16.04/usr/bin:"${PATH}"
  • 这个命令只会将swift命令添加到当前shell会话路径中,如果要确保它在以后的会话中自动添加,要将其添加到.bashrc文件中。
    • 打开 .bashrc 文件
    • 文件尾部添加环境变量
    • 保存退出
1
2
vi ~/.bashrc // 也可以使用nano
export PATH=swift-4.0-RELEASE-ubuntu16.04/usr/bin:"${PATH}"
  • 在终端输入swift,如果输出Welcome to Apple Swift version xxx则表示安装swift成功。

Perfect服务端

使用工具新建Perfect项目

  • 想体验一个新技术,最简便的方法莫过于GUI工具,简单的点几下就可以运行起来。
  • Perfect Assistant专门用于服务器端Swift应用的启动、管理、编译、测试和部署,极大简化了上述工作的操作过程。无论是初次接触服务器的Swift程序员还是企业级的服务器Swift高级工程师,相信都可以从中受益。
  • 使用Perfect搭建服务端之所以比较强大的原因之一:提供了可视化的工具帮助快速入门体验一个服务端软件的搭建。
  • 而且她不仅支持自家的Perfect库,还支持另一个Swift服务端开发库Vapor
  • 更更更加支持新款MBP的Touch Bar,,恩,我的还是不支持Touch Bar的15款的MBP🌚。
perfect_touchBar

perfect_touchBar2

Perfect Assistant 功能

  • 新建Swift服务器工程,或者从现有项目模板中新建工程
  • 管理组件依存关系
  • 同步交叉编译——即在您的mac计算机上同步编译同一个服务器应用在macOS和Ubuntu操作系统上的二进制程序
  • 配置亚马逊弹性计算或者谷歌云应用
  • 将项目部署为亚马逊EC2弹性云计算服务器例程,或者谷歌云应用引擎服务器例程

下载Perfect Assistant

  • 官方下载地址在这里
  • 点击下载、安装即可。(注意需要科学上网,否则可能很慢)
perfectAssistant_download
  • 安装完成后打开界面如下:
perfectAssistant_interface
  • 关于docker
    • Perfect Assistant是支持使用docker进行编译和部署到Linux上的,安装docker后就可以在本地对代码进行linux环境的编译后直接部署到linux服务器上。
    • 更重要的是:使用docker可以打包应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。以后需要其他linux上部署,只需要将制作好的docker镜像拉取下来就可以运行,不需要再配置环境和依赖。
    • 安装docker大概需要15G左右的磁盘空间,我因为系统盘磁盘空间有限,就没有安装。之前试了下安装大概需要几分钟时间(前提网速够快)。 需要安装的小伙伴,直接点击如图所示的 立即安装docker、等待安装完成即可。
    • 关于想了解更多docker知识的,官网在这里

使用Perfect Assistant创建项目

  • 安装好Perfect Assistant后,快速创建一个服务端应用,只需要点击创建项目窗口的Perfect模板App
perfectAssistant_newProject
  • 选择项目需要放置的文件目录及项目名称(我这里还是以最早体验服务端项目的名字命名)。
  • 红框选项可选可不选,勾选后在Xcode编译项目的时候会,会运行及反馈一些linux环境的编译信息,后面需要的时候可以再重新勾选,现在的电脑配置其实勾选了也并不会增加太长的编译时间。

配置处理项目依赖

  • 项目依赖都是通过Swift的包管理器处理的,Swift包依赖管理请参考官方文档.
  • 创建项目完成会默认先拉取模板应用的相关依赖,在默认应用中默认已经配置过HTTPServer依赖了,处理窗口如下:
perfectAssistant_clone
  • 后期需要添加自己的依赖库时候,如数据库、打印等等,只需要在下面的可用依赖窗口中选择需要依赖直接拖拽到上面项目依赖窗口即可。

    perfectAssistant_dependencies
  • 如果在可用依赖窗口没有找到我们需要的,也可以点项目依赖窗口右边的+号按钮,添加自定义的项目依赖:其实就是添加依赖的Package Manager地址及功能类别。

  • 更多Perfect Assistant工具使用及功能介绍,请参考官方文档

编译运行项目

  • 项目依赖处理完成,就可以找到我们创建的项目目录下的iNoteServer.xcodeproj文件,具体名称请参考你创建项目的名称,通过Xcode打开。
  • 注意,在项目Scheme中选择可执行文件,默认选中的的是项目名称-Package
perfectAssistant_Project_build
  • 然后直接编译运行,就可以看到控制台输出:
1
[INFO] Starting HTTP server localhost on 0.0.0.0:8181
  • 此时就已经开了一个本地端口8181的服务,直接在浏览器输入上述地址及端口,就可以看到令人兴奋的Hello, world!了。

使用终端新建Perfect项目

  • 使用终端命令创建项目也不是很复杂,重要的是可以自己把控项目创建的细节,创建出来的项目更加的干净,我比较喜欢使用终端创建项目。

使用终端新建项目

  • 创建一个存放项目的文件夹iNote,后续服务端对应的客户端项目iNoteClient也可以放在这个文件夹内。
  • iNote文件夹下创建一个存放服务端项目的文件夹iNoteServer
  • iNoteServer目录初始化为一个Git仓库
  • 创建Package.swift文件,该文件是用Swift包管理器(SPM)管理依赖需要的,用来管理整个项目的依赖。我们可以使用Xcode打开该文件,编辑添加需要的项目依赖。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import PackageDescription

let versions = Version(0,0,0)..<Version(10,0,0)
let urls = [
"https://github.com/PerfectlySoft/Perfect-HTTPServer.git", // 最基本的服务器核心依赖
"https://github.com/PerfectlySoft/Perfect-MySQL.git", // MySQL服务器
"https://github.com/PerfectlySoft/Perfect-Logger.git", // 日志处理库
"https://github.com/PerfectlySoft/Perfect-RequestLogger.git" // 请求日志处理库
]

let package = Package(
name: "iNoteServer",
targets: [],
dependencies: urls.map { .Package(url: $0, versions: versions) }
)
  • 回到终端,创建Sources目录,该目录用于存放我们自己写的项目源代码。
  • 创建一个程序入口文件,并在空文件中插入一个打印函数:
1
echo 'print("hello world!")' >> Sources/main.swift

配置处理项目依赖

  • 项目依赖已经配置完成,此时需要处理项目依赖并生成可执行文件:
  • 处理过程会在终端输出对应的信息,时间长短取决于在Package.swift文件中配置的依赖库多少及每个库大小。
  • 生成的可执行文件在项目的根目录的debug目录下。
1
swift build
  • 默认是生成的Debug版本的可执行文件,如果是正式部署服务器的可执行文件,可使用如下指令生成发布版本可执行文件:
  • 生成的可执行文件在项目的根目录的release目录下。
1
swift build -c release
perfect_server_init
  • 一般很快就可以处理完成,处理完成就会在终端输出信息可以看到生成了一个由项目名称命名的可执行文件,根据提示信息执行对应的可执行文件,就可以输出我们的打印信息了:
1
2
./build/debug/iNoteServer // debug模式
./build/release/iNoteServer // release模式
  • 可以使用Swift包管理器创建一个xxx.xcodeproj文件用于使用Xcode来管理和编辑调试项目,执行下面的指令,生成Xcode项目管理文件:
1
swift package generate-xcodeproj
perfect_server_generate
  • 配置项目文件的搜索目录为整个项目目录:
    • $(SRCROOT)代表的时项目根目录下
    • $(PROJECT_DIR)代表的是整个项目
1
$(PROJECT_DIR)
  • 如果添加新的项目依赖库后只需要重新swift build 或者 swift build -c release等待完成即可。
  • 如果添加新的依赖后出现未知错误,可尝试删除项目根目录下的xxx.xcodeproj文件,然后使用swift package generate-xcodeproj重新生成项目文件即可。

搭建HTTP服务器

  • 创建iNoteAIP文件,用于管理所有的路由。大概分为用户及笔记两类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum iNoteAIP: String {

case base = "/iNote"

/// 注册页面
case register = "/register"

/// 登录页面
case login = "/login"

/// 获取笔记列表
case notes = "/notes"

/// 添加笔记
case creatNote = "/creatNote"

/// 删除笔记
case removeNote = "/removeNote"

/// 修改笔记
case editNote = "/editNote"
}
  • 创建一个NetworkServerManager文件用于服务器的相关操作,如服务器开启及停止,端口监控、路由监控及处理等等。
  • 关于服务器API设计及命名,请参考服务端指南 | 良好的 API 设计指南这一篇文章,写的很不错。
  • 首先实例化一个HTTPServer对象,绑定指定的端口,并添加项目需要用到的路由。
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
// 导入需要的头文件
import PerfectHTTP
import PerfectHTTPServer
import PerfectRequestLogger
import PerfectLogger

class NetworkServerManager {

let server = HTTPServer()
var routes = Routes(baseUri: iNoteAIP.base.rawValue)
var api1Routes = Routes(baseUri: "/v1") // 将所有的路由根据版本号进行区分管理,方便后期接口逻辑调整,新版本不会影响到旧版本

static let shared = NetworkServerManager()
private init() {
routesConfigure() // 配置路由
loggerConfigure() // 配置日志工具
}

func serverStart(_ port: UInt16 = 8181)
{
// 监听端口 绑定路由
server.serverPort = port
server.addRoutes(routes)
do {
try server.start()
} catch PerfectError.networkError(let code, let message) {
print("network error:\(code) \(message)")
} catch {
print("unknow network error: \(error)")
}
}
}
  • 具体的项目添加的路由(以注册为例子)以及配置系统的日志工具(日志信息记录到本地文件)。
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
extension NetworkServerManager {

private func addRoute(with method: HTTPMethod, uri: iNoteAIP, handler: @escaping RequestHandler)
{
api1Routes.add(method: method, uri: uri.rawValue, handler: handler)
}

/// 配置各个模块的路由
private func routesConfigure()
{
addRoute(with: .post, uri: .register, handler: registerHanlder())
// addRoute(with: .post, uri: .login, handler: loginHanlder())
//
// addRoute(with: .post, uri: .creatNote, handler: creatNoteHanlder())
// addRoute(with: .post, uri: .editNote, handler: editNoteHanlder())
// addRoute(with: .delete, uri: .removeNote, handler: removeNoteHanlder())
// addRoute(with: .get, uri: .notes, handler: notesHanlder())

// 所有路由
routes.add(api1Routes)
}

/// 添加日志文件记录
private func loggerConfigure()
{
// 日志路径
let logPath = "./files/logs"
if !Dir(logPath).exists {
try? Dir(logPath).create()
}
LogFile.location = "\(logPath)/iNoteServer.log" // 设置日志文件路径
// 增加日志过滤器,将日志写入相应的文件
server.setRequestFilters([(RequestLogger(), .high)]) // 首先增加高优先级的过滤器
server.setResponseFilters([(RequestLogger(), .low)]) // 最后增加低优先级的过滤器
// 项目中根据打印级别打印信息
//LogFile.debug("调试")
//LogFile.info("消息")
//LogFile.warning("警告")
//LogFile.error("出错")
//LogFile.critical("严重错误")
//LogFile.terminal("服务器终止")
}
}
  • 处理路由绑定的回调,即当用户触发的对应接口会在这个地方处理,以注册路由绑定的回调为例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//MARK: 处理路由回调
extension NetworkServerManager {

func registerHanlder() -> RequestHandler
{
return { request, response in
let user = User()
user.userName = request.param(name: "userName")
user.password = request.param(name: "password")
let resultString = UserMediator.creat(a: user)
response.setBody(string: resultString)
response.completed()
}
}
}
  • 创建一个UserMediator文件作为用户逻辑的中间层,用于管理用户的相关的逻辑,如用户是否重名、参数是否合法,响应数据格式处理等等:
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
enum ResponseStatus: String {
case success = "Success"
case failure = "Failure"
}

enum ResponseErrorMessage: String {
case parameterIncompleteness = "参数不完整"
case userExist = "用户已经注册"
case userCreationFailed = "用户注册失败"
case wrongUsernameOrPassword = "用户名密码错误"
}

let TableName = "T_User" // 数据库中的用户表

class UserMediator {
/// 创建成功返回对应的用户信息,不成功返回失败原因
static func creat(a user: User) -> String {

guard let userName = user.userName,
let password = user.password,
userName.count > 0,
password.count > 0
else {
return self.handle(with: .failure, isSuccess: false, message: ResponseErrorMessage.parameterIncompleteness.rawValue, value: [])
}

// 用户是否存在
let isExist = DataBaseManager.shared.isExist(of: userName, in: TableName)
if isExist {
return self.handle(with: .failure, isSuccess: false, message: ResponseErrorMessage.userExist.rawValue, value: [])
}

// 创建用户
let isSuccess = DataBaseManager.shared.store(user: user, to: TableName)
if !isSuccess {
return self.handle(with: .failure, isSuccess: false, message: ResponseErrorMessage.userCreationFailed.rawValue, value: [])
}

// 是否创建用户成功
let users = DataBaseManager.shared.users(of: userName, in: TableName)
return self.handle(with: .success, isSuccess: true, message: nil, value: users)
}
}

extension UserMediator {

/// 服务器响应数据处理
///
/// - Parameters:
/// - state: 服务器响应结果(实际处理应该为响应结果状态枚举,本处简单的处理为成功/失败)
/// - isSuccess: 是否成功的标记
/// - message: 描述信息
/// - value: 响应数据
/// - Returns: 响应数据序列化为json字符串
static func handle(with state: ResponseStatus, isSuccess: Bool, message: String?, value: [[String: Any]]?) -> String
{
let resultDict: [String : Any] = [
"status" : state.rawValue,
"flag" : isSuccess,
"message" : message ?? "",
"value" : value ?? []
]
do {
return try resultDict.jsonEncodedString()
} catch {
print(error)
return ""
}
}
}

MySQL数据库

  • MySQL数据库的安装,请参考之前的文章,本文开头有跳转连接。

创建数据库和表

  • 创建并配置MySQL数据库,本文以Navicat Premium为例子。
  • 打开Navicat Premium软件,点击左上角的连接,选择MySQL,在弹出的窗口中输入对应的信息:如我的连接名称为iNote,其他选项如HostPortUser Name保持默认,密码即为你的安装的MySQL密码。
MySQL_connect
  • 配置完服务器信息,就可以创建一个数据库,例如我创建一个名字为iNoteDB的数据库。
  • iNoteDB数据库中简单的新建了两张表,一张是用户表T_Users, 一张是笔记表T_Note。表结构如下:
  • 我的字符集选utf-8,排序规则utf8_general_ci
mysql_t-user

mysql_t-note
  • 我们也可以手动在表中插入数据,如我们在用户表中插入一条数据,用户名西门抽血,密码1234567

数据库管理工具

  • 创建DataBaseManager文件,用户管理数据库的连接、关闭及数据CRUD操作。
  • 数据库的连接配置文件如下:
1
2
3
4
5
6
7
//MARK: 数据库信息
struct MySQLConnector {
static let host = "127.0.0.1"
static let account = "root"
static let password = "1234567" // 之前安装的MySQL服务器的密码
static let database = "iNoteDB" // 刚刚创建的数据库名称
}
  • 创建数据库管理者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DataBaseManager {

/// 单例 全局数据库工具访问点
static let shared = DataBaseManager()
private var mysql: MySQL
private init()
{
mysql = MySQL.init() // 创建MySQL对象
let connected = mysql.connect(host: MySQLConnector.host, user: MySQLConnector.account, password: MySQLConnector.password, db: MySQLConnector.database)
guard connected else {
print("MySQL连接失败" + mysql.errorMessage())
return
}
}
}
  • 操作数据库进行常规的CURD

    • isExist方法 用于查询特定的用户是否存在
    • store方法 用于缓存一条用户记录到本地
    • users方法 用于查询特定的一条用户数据记录
  • PerfectMySQL的支持还不是很完善,写出来的代码比较丑陋,可以尝试下使用其他的数据或者配套的对象管理函数库(ORM),总的来说支持的服务器种类还是比较完善的,总能找到你中意的一款。

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
//MARK: User
extension DataBaseManager {

func isExist(of userName: String, in tableName: String) -> Bool
{
let sql = "SELECT * FROM \(tableName) WHERE userName = '\(userName)'"
if !mysql.query(statement: sql) {
LogFile.error("Error query : \(sql)")
return false
}

// 在当前会话过程中保存查询结果
let resultSets = mysql.storeResults()
var resultArray = [[String: String]]()

resultSets?.forEachRow(callback: { (row) in

let userID = row[0]
let userName = row[1]
let password = row[2]
let created = row[3]
let updated = row[4]
var dict = [String: String]()
dict["userID"] = userID
dict["userName"] = userName
dict["password"] = password
dict["created"] = created
dict["updated"] = updated
resultArray.append(dict)
})

guard let numRows = mysql.storeResults()?.numRows(), numRows <= 0 else {
return false
}
return true
}

func store(user: User, to tableName: String) -> Bool
{
guard let userName = user.userName, let pwd = user.password else { return false }
let values = "('\(userName)', '\(pwd)')"
let sql = "INSERT INTO \(tableName) (userName, password) VALUES \(values))"
if !mysql.query(statement: sql) {
LogFile.error("Error query : \(sql)")
return false
}
return true
}

func users(of userName: String, in tableName: String) -> [[String: Any]]?
{
let sql = "SELECT * FROM \(tableName) WHERE userName = '\(userName)'"
if !mysql.query(statement: sql) {
LogFile.error("Error query : \(sql)")
return nil
}
// 在当前会话过程中保存查询结果
let resultSets = mysql.storeResults()
var resultArray = [[String: String]]()
resultSets?.forEachRow(callback: { (row) in
let userID = row[0]
let userName = row[1]
let password = row[2]
let created = row[3]
let updated = row[4]
var dict = [String: String]()
dict["userID"] = userID
dict["userName"] = userName
dict["password"] = password
dict["created"] = created
dict["updated"] = updated
resultArray.append(dict)
})
return resultArray
}
}

启动及测试服务器

  • 在程序入口Main.swift文件配置启动服务器,因为上面我们已经在服务器管理者这个文件中封装好了启动方法,此时就可以一行搞定:
1
2
// 启动服务器
NetworkServerManager.shared.serverStart()
  • 运行项目,等待控制台输出如下信息代表启动服务器成功,然后测试注册服务器接口了,以post请求的注册接口为例子:
    • 我们这里使用Paw工具测试网络接口,当然你们也可以使用Post Man测试,甚至你想的话可以自己写网络接口调用测试。
    • 如果是get请求,你可以直接在浏览器就可以测试,如果你想改下本例子的请求方式,只需要在路由配置方法中把.get改为.post即可。
1
[INFO] Starting HTTP server  on 0.0.0.0:8181
  • 例如我们测试,用户名西门抽血,密码1234567进行注册,因为之前我们在数据库中手动添加了一条相同记录,所以注册结果如下图:接口调用成功,服务器返回注册失败信息。
    server_register_test

结尾

  • 本文只是对使用Perfect搭建服务器的一个概要性总结,希望大家通过本文可以对Swift开发服务器整个流程有一个认识,具体的细节和注意点都待完善。
  • 至于项目Demo,肯定是有的,但是目前是不可能给到大家了,因为我还没有写完啊,目前只是写了一个粗略的注册接口,并且逻辑还没写完😂。
  • 其实项目的完善部分就集中在接口响应处理的对服务器CRUD部分了,因为使用Mysql服务器用的很不顺手,可能会换用其他比较完善的数据库再完善这一部分。
  • 等服务端写完,在开坑对应的客户端,客户端就不用说了,大家估计都快写吐了😂。至于什么时候能写完,我自己都不知道,毕竟我这么懒🌚🌝!

参考

官方文档
Perfect官方文档在这里
Swift官方文档
Swift包管理器.
服务端指南 | 良好的 API 设计指南
服务端写Swift体验 (Perfect框架)
Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)

要不要鼓励一下😘