探索服务器端Swift

当Apple开源Swift时,许多人都为在其他平台上使用这种有趣的新语言而感到非常兴奋。 早期,Swift被移植到Linux,人们开始考虑使用Swift构建服务器。 最近,随着IBM在新的Server API项目中扮演了重要角色,与IBM的现有合作伙伴关系日益加深。 作为ustwo的iOS开发人员,我决定带出服务器端Swift在Linux上试一试。

为了探索在Linux和Swift Package Manager上编写Swift的方法,我决定创建一个名为Mockingbird的小样本。 Mockingbird是模拟服务器的开端,该服务器采用Swagger规范并存入定义的各种端点。

我之所以选择使用IBM的Kitura框架是因为他们与Apple的关系,并且因为它们本身提供了云平台即服务,但是还有许多值得考虑的替代方案(Perfect,Vapor和Zewo仅举几例)。

为什么用Swift编写服务器?

除了对Swift语言的好奇心或强烈的偏好之外,为什么还要使用Swift构建服务器? 我想到了一些原因,但是最终您需要确定它们是否在您的上下文中有意义。

除了iOS或macOS应用程序(将来可能还会有Android和Windows!)之外,用Swift编写服务器还可以使两者共享代码和框架。 在维护方面,只需要编写一次代码就可以节省短期和长期的时间。 它还减少了测试方面的开销。 所有这些对独立开发者特别有吸引力,但对大型企业也可能有利。

与任何语言一样,Swift都是自以为是的,旨在以特定方式解决某些问题。 例如,Swift支持静态类型。 Swift也更喜欢简短,表达力强的代码,而不是冗长的代码。 恰恰相反,并不是说Swift在这些方面比其他语言要好。 每种语言都是一种工具,并且有自己的位置。 但是,如果这些东西吸引您,您可能会对使用Swift感兴趣。 另外,如果您打算在iOS,tvOS或watchOS上运行,则具有第一方支持和框架的语言选择将受到限制。 最近,Apple的重点肯定是Swift,而不是Objective-C或其他语言。

使用Swift构建Linux服务器

使用Kitura和IBM开发的各种相关软件包来构建Mockingbird,使其非常容易启动并将基本逻辑绑定到HTTP服务器。 编写的大部分代码用于文件管理和解析。 只需很少的代码即可构建服务器本身。

我在开发此微型服务器时遇到的最大挑战分为两类。 首先,Apple维护其Foundation框架的两个版本,这是大多数Apple开发人员使用的关键框架之一。 Darwin上的Foundation框架(即macOS,iOS等)和Linux上的Foundation框架是不同的实现。 换句话说,当您为它们提供相同的输入而获得不同的结果时,这些实现并不总是产生相同的输出。 这些实现也不完全与API兼容。 其次,Kitura在可测试性方面还有很多需要改进的地方。

在很多情况下,我都会在macOS上完美构建该软件包,但发现我启动Docker容器时,它耗尽了运行测试或启动服务器的能力。 在整个代码中,您会看到一些片段,在这些片段中,我不得不提供条件编译块,例如:

  #if os(Linux) 
 让regexObj =尝试吗?  RegularExpression(模式:Endpoint.kituraPathRegexString,选项:[]) 

#其他
 让regexObj =尝试吗?  NSRegularExpression(模式:Endpoint.kituraPathRegexString) 
  #万一 

虽然我在小型实现中并不经常遇到它,但苹果的swift-corelibs-foundation(Foundation的开源版本)仍然有很多部分尚未实现(在存储库中搜索NSUnimplemented() )。 对于在Linux上使用Swift的任何人,我强烈建议您为该存储库加注星标,因为您可能需要参考已实现的内容以及在Linux和macOS上已实现的不同内容。

对于我的Kitura实施,这也是非平凡的写作测试。 由于无法对路由器的elements属性进行内部作用域确定,并且也没有可公开访问的迭代器(甚至是只读的),因此无法遍历服务器定义的API端点。 Kitura也不提供删除所有端点测试所共有的样板的测试框架(有关如何执行此操作的良好示例,请参阅ProcedureKit)。 因此,我在EndpointTests.swift编写了自己的简化版本,以简化测试。

使用Swift包管理器

Swift软件包管理器(SwiftPM)是Apple的新跨平台依赖项管理器。 与Swift本身具有明确的发行目标和给定版本中的(某些)稳定性不同,SwiftPM仍处于早期开发阶段,并且正在迅速变化。 目前,它也不支持iOS,watchOS或tvOS。 因此,请勿暂时停止使用CocoaPods或Carthage。 在处理Xcode时,它还带有许多约束/限制。 编写SwiftPM Package.swift文件非常容易,并且使用类似JSON的语法。 它尝试做一些聪明的事情,例如将测试与适当的源文件进行匹配,链接到常见的Apple框架(例如Foundation或Dispatch),并可以为您生成Xcode项目文件。 不幸的是,这就是它在这一点上所做的一切(尽管公平地说,正如我所说的还处于初期)。 即使在这个小例子中,我在使用SwiftPM时也遇到了三个主要问题。

1. SR-2866 :SwiftPM当前没有办法指定要包含在软件包中的资源(例如资产,测试文件等)。 Mockingbird通过在Dockerfile添加COPY命令并提供代码中资源的绝对路径来解决此问题。 为了在Xcode中提供更好的支持, xcodeproj_after_install.rb脚本的一部分将资源添加到ResourceKit目标的“复制文件构建脚本”阶段。 因此, ResourceKit具有三种为给定资源生成适当文件url的方法-在Linux上构建时,使用Linux(Docker)文件布局的绝对路径;在Mac上使用Swift编译器时,使用macOS文件布局的绝对路径(即swift buildswift test ),或在Mac上使用Xcode时的资源包。 没有资源捆绑也将阻止ustwo的某些开源库(例如FormValidator-Swift)此时支持SwiftPM。

2. SR-3033 :SwiftPM当前无法测试可执行程序包(即带有main.swift文件的程序包)。 Mockingbird通过在库( MockServerKit )中放置尽可能多的代码,而在可执行文件( MockServer )中仅main.swift一个最小的main.swift文件来解决此问题。

3. SR-3583 :SwiftPM在构建时会创建一个.build文件夹。 构建时,操作系统没有区别。 因此,如果将.build文件夹从macOS版本复制到Linux服务器并运行,它可能会编译,也可能无法正确测试。 该GitHub PR旨在警告用户或将构建工件放置在指定操作系统的顶级文件夹中。 通过将.dockerignore文件夹添加到.dockerignore文件并在Linux服务器上进行干净构建,Mockingbird可以解决此问题。 (有关此问题的更多历史记录,请参见GitHub PR#807)。

SwiftPM的另一个怪癖是SourcesTests目录的所有顶级文件夹都定义模块。 首先让我的文件夹结构而不是配置文件或源文件中的声明定义我的名称空间有点令人不安。 尽管这既不是一件坏事,也不是挑战,但它与我过去为Apple平台开发的经验有所不同。

测试,尤其是持续集成也是一个挑战。 由于所概述的挑战以及各个平台上的框架之间的差异,设置Travis CI支持需要花费额外的精力。 非常感谢SwiftLint项目,其实现基于Mockingbird。

最后的想法

总的来说,我真的很喜欢在Linux上与Swift交流,并把SwiftPM带出去进行测试。 我觉得Swift和支持我们开发的各种框架已经开始成熟,以至于您可以轻松地作为独立开发人员来构建服务器。 仍然会有很多麻烦,但是与让您学习另一种语言及其所有标准库以使服务器正常运行相比,您可能更容易使用。

进一步阅读和查看

以下是有关使用Swift构建Linux服务器的其他演讲和博客文章。 我们每个人都遇到了不同的挑战,因此值得一读。

– Jeff Bergier 构建生产服务器Swift应用程序:经验教训 :这是关于Bergier构建Swift服务器经验的很好的讨论。 还有一些其他示例,说明了Darwin Foundation与Linux之间的区别。

Kitura演示(2016–09–20) :作为服务器端Swift年度长期会议的一部分的出色演示和视频教程。

– WWDC 2016:使用Swift开源进入服务器端:今年WWDC关于在服务器上使用Swift的演讲。

这篇文章是由 亚伦·麦克塔维什 Aaron McTavish )撰写的 ,最初发表在 ustwo.com上