Swift——简单服务端开发实战

相比不少iOS开发者在平时业余时间,都学习或者瞎搞过非iOS开发相关的技术,比如PHP,H5,Python,Kotlin,Go等,但是不管是为了打发时间,还是作为业余学习爱好,或者是广度拓展知识量,抑或者为了装逼,但是对我来说目前吃饭的家伙依然还是iOS,虽然iOS依然学的很水,就在5月份的时候去深圳参加过一次2017@Swift开发者大会,除了感触自己英语很差之外,还有一个记忆深刻的话题就是关于使用Swift开发服务端应用,当时确实有不少人在学习和研究相关技术,由于工作的原因也就没有去捣鼓,当然也可能是因为Swift开发服务端并不是那么稳定,完善,最近不知道为撒,突然想花点时间尝试一下,不管结果怎样或者是不是外面传的那么牛逼,我就当尝鲜吧……哈哈!

前言

这里就不介绍什么Swift相关的知识了,如果不了解或者不够熟悉Swift的请查看相关文档

目前而言关于Swift开发服务端应用的库比较好的有两个:Vapor和Perfect
当然也还有其他关于类似的,具体可以参考这里的对比:服务端写Swift体验 (Perfect框架)

因为我所在的一个2017@Swift开发者大会群中,其中有一位是Perfect作者之一,而且经常有听到关于Perfect的讨论,
我也前往官网和github看了一下,文档非常详细,star也有12k,所以我决定从Perfect开始尝试。当然Vapor据说也不错,
有机会也可以研究一下,然后对比,选择一个适合自己的来做点小东西。

Vapor参考:服务端 Swift - Vapor 篇

介绍

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

其实总结一句就是:

Perfect是一套可以用来开发服务端应用的Swift库。

第一步:装备阶段

开发环境

环境配置

swift –version
iCocos:blog icocos$ swift --version
Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)
Target: x86_64-apple-macosx10.9

注意:最新版本的Swift 3.0。如果低于3.0版本则Perfect是无法成功编译。

软件

Perfect依赖于若干软件接口库,比如OpenSSL、libssl-dev和uuid-dev

brew install openssl libssl-dev uuid-dev

编译项目

Clone
git clone https://github.com/PerfectlySoft/PerfectTemplate.git
Swift编译命令
cd PerfectTemplate
swift build
命令行启动Swift Server
.build/debug/PerfectTemplate

编译后可以启动一个本地的服务器,监听您计算机的8181端口:

iCocos:PerfectTemplate icocos$ .build/debug/PerfectTemplate
[INFO] Starting HTTP server localhost on 0.0.0.0:8181

服务器现在已经运行并等待连接。从浏览器打开http://localhost:8181/ 可以看到“Hello, world!”信息。

是不是感觉很熟悉,这不是和学习PHP的时候一样的吗?

Xcode

Swift软件包管理器(SPM)能够创建一个Xcode项目,并且能够运行PerfectTemplate模板服务器,还能为您的项目提供完全的源代码编辑和调试。在您的终端命令行内输入:

swift package generate-xcodeproj    

然后打开产生的文件“PerfectTemplate.xcodeproj”,确定选择了可执行的目标文件,并选择在“我的Mac”运行。现在您可以运行并调试服务器了。

iCocos:PerfectTemplate icocos$ swift package generate-xcodeproj
generated: ./PerfectTemplate.xcodeproj

运行项目

打开Clone下来的项目文件PerfectTemplate中的.xcodeproj文件,选择带命令行图片(不能选错)的目标运行
一行代码都不用写,就会发现Xcode命令行打印了一段:

[INFO] Starting HTTP server localhost on 0.0.0.0:8181

到这里第一步基本上已经完成,下面基本上就可以开始撸代码了。

第二步:初步调整

调整接口返回数据

找到main.swift文件,handler方法中有这么一段:

// An example request handler.
// This 'handler' function can be referenced directly in the configuration below.
func handler(data: [String:Any]) throws -> RequestHandler {
    return {
        request, response in
        // Respond with a simple message.
        response.setHeader(.contentType, value: "text/html")
        response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, world!</body></html>")
        // Ensure that response.completed() is called when your processing is done.
        response.completed()
    }
}

直接将Hello, world!替换为想要的内容,再在浏览器输入 0.0.0.0:8181
刚刚所修改的内容就会直接显示出来。

慢慢的,我发现和之前学习PHP所设计的步骤和操作越来越像

添加软件包

打开PerfectTemplate项目中的Package.swift,在package前面添加需要的软件包,
并替换package中dependencies获取方式

//软件包管理
import PackageDescription

let versions = Version(0,0,0)..<Version(10,0,0)
let urls = [
    "https://github.com/PerfectlySoft/Perfect-HTTPServer.git",      //HTTP服务
    "https://github.com/PerfectlySoft/Perfect-MySQL.git",           //MySQL服务
    "https://github.com/PerfectlySoft/Perfect-Mustache.git"         //Mustache
]

let package = Package(
    name: "PerfectDemoProject",
    targets: [],
    dependencies: urls.map { .Package(url: $0, versions: versions) }
)

第三步:搭建HTTP服务器

关于HTTP服务器配置官方(HTTP 服务器)其实已经给出了一个比较完成的介绍,但是由于篇幅过长这里就整理了一下:

1.编辑main.swift

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer

//HTTP服务
let networkServer = NetworkServerManager(root: "webroot", port: 8888)
networkServer.startServer()

2.创建并编辑NetworkServerManager.swift

import PerfectLib
import PerfectHTTP
import PerfectHTTPServer

open class NetworkServerManager {

    fileprivate var server: HTTPServer
    internal init(root: String, port: UInt16) {

        server = HTTPServer.init()                          //创建HTTPServer服务器
        var routes = Routes.init(baseUri: "/api")           //创建路由器
        configure(routes: &routes)                          //注册路由
        server.addRoutes(routes)                            //路由添加进服务
        server.serverPort = port                            //端口
        server.documentRoot = root                          //根目录
        server.setResponseFilters([(Filter404(), .high)])   //404过滤

    }

    //MARK: 开启服务
    open func startServer() {

        do {
            print("启动HTTP服务器")
            try server.start()
        } catch PerfectError.networkError(let err, let msg) {
            print("网络出现错误:\(err) \(msg)")
        } catch {
            print("网络未知错误")
        }

    }

    //MARK: 注册路由
    fileprivate func configure(routes: inout Routes) {

        // 添加接口,请求方式,路径
         routes.add(method: .get, uri: "/") { (request, response) in
         response.setHeader( .contentType, value: "text/html")          //响应头
         let jsonDic = ["hello": "world"]
         let jsonString = self.baseResponseBodyJSONData(status: 200, message: "成功", data: jsonDic)
         response.setBody(string: jsonString)                           //响应体
         response.completed()                                           //响应
         }

    }

    //MARK: 通用响应格式
     func baseResponseBodyJSONData(status: Int, message: String, data: Any!) -> String {

        var result = Dictionary<String, Any>()
        result.updateValue(status, forKey: "status")
        result.updateValue(message, forKey: "message")
        if (data != nil) {
            result.updateValue(data, forKey: "data")
        }else{
            result.updateValue("", forKey: "data")
        }
        guard let jsonString = try? result.jsonEncodedString() else {
            return ""
        }
        return jsonString

    }

    //MARK: 404过滤
    struct Filter404: HTTPResponseFilter {

        func filterBody(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
            callback(.continue)
        }

        func filterHeaders(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
            if case .notFound = response.status {
                response.setBody(string: "404 文件\(response.request.path)不存在。")
                response.setHeader(.contentLength, value: "\(response.bodyBytes.count)")
                callback(.done)

            } else {
                callback(.continue)
            }
        }

    }

}

3.运行项目

可以看到底部打印

启动HTTP服务    
[INFO] Starting HTTP server localhost on 0.0.0.0:8181

4.重新刷新浏览器链接会发现

{
    "status":200
    "date": {
        "hello": "world"
    }
    "message": "成功"
}

这里我们平时开发中返回的标准格式,具体含义这里就不介绍了

遇到的问题(报错)及解决方案:

1.swift build:SSLRead() return error -9806报错

brew remove git
brew remove curl
brew install openssl
brew install --with-openssl curl
brew install --with-brewed-curl --with-brewed-openssl git
swift build
Swift开发服务端应用完整资料:

Swift 服务端开发 Perfect、Vapor资料

坚持原创技术分享,您的支持将鼓励我继续创作!