Firebase服务器端Swift入门

Firebase工具套件很棒。 在一个屋顶下捆绑了这么多有用的开发服务真是太好了,而且免费上手的能力也很棒! Firebase为多个平台提供SDK,以与其服务(包括iOS)进行通信。 但是,iOS SDK与iOS应用程序生命周期紧密相关,因此无法在服务器端Swift环境中使用。 幸运的是,Firebase提供了REST API,这为我们提供了在Swift中开发服务器时使用其服务的途径。 最近,这篇文章概述了在服务器端Swift环境中开始使用Firebase REST API所需的步骤。 我将把Swift Vapor应用程序连接到Firebase的新Cloud Firestore技术,但是一般过程应适用于任何其他服务器端Swift框架和Firebase服务。

Cloud Firestore

Cloud Firestore是Firebase的“新的和改进的” NoSQL云数据库。 与以前的产品Firebase Realtime Database一样,Cloud Firestore在客户端之间实时同步数据,并为脱机模式提供一流的支持。

Cloud Firestore与实时数据库

如果您过去使用过Firebase的实时数据库产品,您可能想知道Cloud Firestore有何不同。 Firebase文档进行了更详细的介绍,但从根本上讲,它可以归结为Firebase,它可以在原始Realtime Database产品成功的基础上进行改进。 较早的实时数据库需要使用非规范化和数据展平技术来防止随着数据库的增长和发展而出现瓶颈。 Cloud Firestore通过提供更丰富,更快的查询和更好的可伸缩性来对此进行改进。

因此,如果Cloud Firestore是Realtime Database的改进版本,那么Realtime Database甚至不再相关吗? 答案是肯定的。 实时数据库的真正优势仍然在于其速度。 如果您需要优化产品以实现高效,低延迟的同步,则可能需要使用实时数据库而不是Cloud Firestore。 这并不是说Cloud Firestore无法完成这项工作。 它仍然可以超级快速地同步事物(以毫秒为单位)。

蒸气2.x

蒸气是市场上许多服务器端Swift库之一。 我觉得他们在使事情变得简单易懂并且使服务器端Swift开发的入门变得非常出色。 他们甚至最近推出了Vapor Cloud,这使将服务器部署到现实世界中非常容易。 小型项目甚至免费! 在此项目中,我使用了Vapor 2.x,因为当我开始时Vapor 3仍处于beta中。

我将使用这些技术来实现简单的概念验证,以便利用服务器端Swift来实现自定义后端逻辑,同时依靠Firebase实时更新客户端应用程序。 对于此演示,服务器将仅每60秒生成一个随机数,然后将该数字及其生成下一个随机数的日期发布到Cloud Firestore数据库。 当一切正常运行后,客户端应用程序将如下所示:

创建Firebase项目后,您要做的第一件事就是启用Cloud Firestore。 这很容易做到。 打开您的Firebase项目,然后导航到Develop→Database并打开它:

启用后,您将看到一个空数据库,如下所示:

现在我们已经为项目启用了Cloud Firestore,我们如何连接到它? 如上所述,Firebase iOS SDK不在桌面上。 SDK也许有一天会与服务器端Swift兼容,但是Firebase团队目前尚无具体计划。

由于我们无法使用iOS SDK,因此我们用于从Vapor连接到Cloud Firestore的选项是使用REST API或RPC API。 由于我对RPC的了解还不够,因此在此示例中将使用REST API。

如果您使用的是较早的Realtime Database产品,我应该提到您实际上还有另一种选择。 存在一个名为FirebaseSwift的开源Swift库,该库为从服务器端Swift环境连接到实时数据库提供了一些便利。 但是,您仍然需要手动生成和管理您的Google OAuth令牌,我将在稍后讨论。

使用Cloud Firestore REST API非常简单:

  • 与Google进行身份验证以接收服务器到服务器的访问令牌。
  • 使用该访问令牌将数据读/写到Cloud Firestore数据库。

验证选项

您可以通过三种方式向Google进行身份验证以接收服务器到服务器的访问令牌:

  • 使用10个官方客户端库之一。
  • 手动执行Google的服务器到服务器OAuth 2.0步骤。
  • 使用gcloud命令行工具。

尽管Google强烈建议您使用其客户端库之一,但尚不支持Swift。 😔

这意味着我们将需要手动实施OAuth流程。 我没有研究gcloud命令行工具,但在我看来,它仅对生成一次性令牌进行测试非常有用。

验证先决条件

为了向Google进行身份验证,您需要使用Google“服务帐户”。 服务帐户是属于您的应用程序本身而不是您的应用程序个人用户的帐户。 服务帐户由两部分组成:

  • 唯一的电子邮件地址,由Google自动为您生成。
  • 公钥/私钥对。

获取Google Access令牌

因此,我们已经确定需要手动遵循Google的OAuth流程。 这是如何运作的? 该过程可以概括为三个步骤:

  • 创建一个JSON Web令牌(JWT),并用您的服务帐户的私钥进行签名。
  • 使用JWT从Google OAuth 2.0授权服务器请求访问令牌。
  • 处理来自授权服务器的JSON响应。

该流程的序列图如下所示:

我应该注意,Google的文档再次指出,他们确实希望您使用其客户端库之一进行身份验证。 我认为他们正试图告诉我们一些事情……🤔

由于没有可用的与Swift兼容的Google身份验证库,我们只需要忽略Google一直在劝阻我们避免手动实施其OAuth流的尝试。 但是首先,关于JWT的速成课程…

JWT,发音为“ jot”,基本上只是由三部分组成的一段数据:

  • “标头”,它指定令牌的格式和所使用的签名算法。
  • “有效负载”(或“声明”)保存有关令牌的信息,包括令牌代表的用户,令牌具有哪些权限(范围)以及令牌生存期等信息。
  • “签名”,是标头和附加在一起的声明的加密签名。

JWT的每个部分都经过base64 url​​编码,然后加上句点以形成JWT的最终表示形式。 总体而言,该过程如下所示:

重要的是要注意,即使JWT看起来像它,也不会被加密 。 这意味着任何人都可以阅读“声明”的内容。 如果您出于个人目的创建JWT,请不要在其中存储任何敏感信息。 JWT的加密签名仅允许发布JWT的实体验证数据的完整性。 如果一方试图修改“标题”或“声明”中的任何信息,则签名将指示数据无效,发行者可以拒绝令牌。

那么我们如何使用Vapor生成Google OAuth JWT? 为了生成JWT,我们需要满足两个先决条件:

  • 获取我之前提到的Google服务帐户凭据(特别是私钥)。
  • 通过添加JWTProvider帮助程序库并使用服务帐户的私钥配置Signer来配置Vapor项目。

您可以在Google服务帐户页面上管理服务帐户,但是我们也可以通过Firebase控制台访问与我们的Firebase项目关联的服务帐户,这是我们现在要遵循的过程。

重要的是要注意,即使Google为您的服务帐户生成了公钥/私钥对,他们也没有保留私钥的副本。 由您来跟踪并安全地存储它。

下载凭证

为了下载您的服务帐户凭据,请先在Firebase上打开项目的设置,方法是点击“项目概述”旁边的齿轮图标,然后选择“项目设置”:

接下来,导航到“服务帐户”选项卡,然后单击底部的“生成新私钥”按钮:

这会将JSON文件下载到您的计算机,这是Google分发这些私钥的首选格式。

凭证JSON格式

打开您的服务帐户凭据JSON文件。 它看起来应该像这样:

请注意private_key字段。 这就是我们的Vapor应用程序中的Signer生成和签名用于Google的服务器到服务器OAuth流程的JWT所需要的。

不幸的是,当前形式的私钥对我们不起作用。 此版本的密钥的主要问题在于,它只是一个常规的“私钥”。 我们需要的是“ RSA私钥”。 这个小细节给我带来了数小时的痛苦,直到一个朋友使我意识到存在不同类型的私钥这一事实。

创建密钥文件

我们需要做的第一件事是将凭据JSON中的private_key字段的值复制到其自己的文件中。 将此文件另存为GooglePrivateKey.key

清理空白

如果您现在查看私钥,您可能会注意到有几个\n换行符:

  ----- BEGIN PRIVATE KEY-- \ n MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDBsMABtSYcQBX1 \ n 03xsgbc36ekABgnfKdoJlOVkstpLlv1zpev2grxvCS + VR9D1dbrZJ9QJKfHoyjqT \ n abvhnxMTdvwVQ94Ohoo0lShSvN6NO94a6Hy8iWSHDZuM4LScPGp9H3eNjdMysQ + L \ n 95xi4qioPndyJzwPcOOzM8WirWe5Osk + vFy4sSvVQFpwaMqvQ35abVVVAydWOF6G \ n lmliKRiGB5Ueb.C..qdYE3j + o3WhweBw == \ n --END PRIVATE KEY --- - \ n 

我们需要通过将它们转换为实际的换行符来摆脱它们。 继续并突出显示每个\n出现的内容,然后按键盘上的“ Enter”以换行符替换它。 您应该以如下形式结束:

  -----开始私钥——MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDBsMABtSYcQBX1 
03xsgbc36ekABgnfKdoJlOVkstpLlv1zpev2grxvCS + VR9D1dbrZJ9QJKfHoyjqT
abvhnxMTdvwVQ94Ohoo0lShSvN6NO94a6Hy8iWSHDZuM4LScPGp9H3eNjdMysQ + L
95xi4qioPndyJzwPcOOzM8WirWe5Osk + vFy4sSvVQFpwaMqvQ35abVVVAydWOF6G
lmliKRiGB5Ueb.C..qdYE3j + o3WhweBw ==
-结束私钥-

如果您知道代替手动操作的更好方法,请告诉我! 我认为有一种方法可以在macOS上使用sed命令执行此操作,但我还没有时间进行试验。

将私钥转换为RSA私钥

接下来,使用OpenSSL从新清理的私钥生成RSA私钥:

  openssl rsa -in GooglePrivateKey.key -out GooglePrivateKey_RSA.key 

准备用于蒸汽的RSA私钥

我们几乎已经完成了将该私钥转换为所需格式的操作。 我们需要做的下一件事是将此RSA私钥转换为单行。 在文本编辑器中打开GooglePrivateKey_RSA.key并删除所有换行符,以使整个密钥都在一行上。

接下来,删除-----BEGIN RSA PRIVATE KEY----------END RSA PRIVATE KEY-----以便仅在一行中包含密钥本身的内容。 您应该以如下形式结束:

  MIIEogIBAAKCAQEAwbDAAbUmHEAV9dN8bIG3N + npAAYJ3ynaCZTlZLLaS5b9c6Xr9oK8bwkvlUfQ9XW62SfUCSnx6Mo6k2m74Z8TE3b8FUPeDoaKNJUoUrzejTveGuh8vIlkhw2bjOC0nDxqfR93jY3TMrEPi / ecYuKoqD53cic8D3DjszPFoq1nuTrJPrxcuLEr1UBacGjKr0N + Wm1VVQMnVjhehpZpYikYhgeVHmwvMPay2ylcerNgQnb81tF8WN8IskLU5igseY / Ayi4Vxvmo2NjfYY5ppROsM / DDZcKgroOhjNLfzeYSHIh3qfI4 ... Vno4pV3a7cD7aaZjJL + GR1 + ipSHiu8BSE + FL + K3hitR6bMGQxZWVc / jfsB + gjx4I4WqLAoGAU + 9 + + PkwKqVqhyR4MrnRrCcpSfhrkD7jkvOWOYoGhBka5r0GvRITP BKYfcvooDzfXjGgBPFXzIdPlfrNwsUyKf1zARrwLExg + 6 / PMrWiVVubIuGYzhzrLtYQtF9sBhtgsNNYIkaH2mybgOZ2k45smk98Vug2KnWBN4 / qN1ocHgc = 

现在是时候开始编写我们的服务器端Swift代码了。 你不兴奋吗? 首先,我们需要处理几个设置项目。 我假设您已经安装了Vapor并创建了一个项目。 如果没有,请按照Vapor的入门说明进行操作。

添加JWTProvider

JWTProvider依赖项添加到Package.swift文件中:

接下来,重新生成您的Xcode项目:

 迅捷包generate-xcodeproj 

要么:

 蒸气Xcode 

如果尚未打开,请继续打开Xcode项目。

初始化JWTProvider

打开Config+Setup.swift文件,并在setupProviders()方法中初始化JWTProvider

  ///配置providerprivate func setupProviders()引发{ 
尝试addProvider(FluentProvider.Provider.self)
尝试addProvider(JWTProvider.Provider.self)
}

JWT配置

设置JWTProvider的最后一步是创建jwt.json配置文件。 继续,在Config目录中创建一个名为jwt.json的文件:

使用以下内容填充Config/jwt.json

  { 
“签名人”:{
“ googleOAuth”:{
“ type”:“ rsa”,
“ algorithm”:“ rs256”,
“密钥”:“ $ GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY”
}
}
}

这是JSON文件中发生的事情:

  • 必须存在一个称为signers顶级对象。
  • 在此示例中,我们只有一个签名者googleOAuth
  • googleOAuth签名者指定用于执行签名的算法和密钥。

请注意,我们没有粘贴刚刚创建的单行RSA私钥。 只需一秒钟,您将获得更多信息,但实际上我们正在使用将环境变量注入JSON文件的语法。 您还可以使用其他选项来配置jwt.json文件。 如果您想了解更多有关JWTProviderJWTProvider ,请随时查看Vapor JWT文档。

蒸气发展过程

在继续进行之前,我想花一点时间来讨论我的Vapor开发过程。 我发现,将所有逻辑放入Routes.swift文件是最容易上手的。 这样,您可以使用邮递员(Postman)向路由发出请求来测试/触发逻辑。 如果您的路线是GET ,那么我想您也可以通过浏览器触发它。

对于此示例,我首先创建了一个称为“ nextRandom”的POST路由:

 进口蒸气 
进口JWT
导入Foundationextension Droplet {
func setupRoutes()抛出{post(“ nextRandom”){
做{
// ...
} {
debugPrint(“ \(错误)”)
返回响应(状态:.internalServerError,
正文:“ \(错误)”)
}
}
}
}

如前所述,我们需要特别注意我们的私钥。 用聪明的巫师甘道夫的话说:

对源代码控制说不

我只说一次,但我会大声说:

切勿将您的私钥用于源代码控制!

考虑一下自己被警告。

设置私钥环境变量

如上所述,我们的Config/jwt.json文件中的key设置为环境变量。 这使我们可以通过在运行时注入密钥来使密钥脱离源代码控制。

要进行此设置,请编辑您应用的Run方案并添加一个名为GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY的环境变量。 接下来,将您的单行RSA私钥粘贴为GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY的值:

请注意,如果您使用的是“共享”方案, 请不要这样做! 共享方案通常会提交到源代码管理中,因此通过设置此环境变量不会对您有所帮助。

相反,如果您使用共享方案,请在Config/secrets目录中创建jwt.json文件。 只要确保仔细检查.gitignore文件中是否包含 Config/secrets目录,即可将其从源代码管理中排除 。 默认的Vapor .gitignore已经排除了此目录:

  #由https://www.gitignore.io/api/vapor创建###蒸气### 
Config / secrets ###蒸气补丁###
配套
。建立
数据
* .xcodeproj
衍生数据/
.DS_Store#https://www.gitignore.io/api/vapor的结尾

私钥陷阱

仅需注意一些涉及您的私钥的“陷阱”:

  • 确保安全地存储已清理的单行RSA私钥的副本。 这主要只是为了方便,因此,如果您需要重新配置Xcode项目,则不必从其原始JSON文件格式重新转换密钥。
  • 删除Xcode项目将清除GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY环境变量。 但是,只需重新生成 Xcode项目就可以了。
  • 如果Config/jwt.json文件中的私钥丢失或无效,则应用程序将在启动时崩溃。

最后一点很重要。 如果JWTProvider无法基于jwt.json配置文件中的值生成有效的RSA私钥,则它将无法初始化。 如果发生这种情况,您的Vapor应用将因JWT error: Could not create key崩溃JWT error: Could not create key错误:

最后,我们完成了所有设置,可以开始编写服务器端Swift代码! ahead继续打开Routes.swift ,准备开始填写POST /nextRandom端点的实现。 如果您想了解一下完成后这条路线的样子,请随时查看feature / basicExample分支上的Routes.swift文件。

创建Google OAuth JWT

使用Vapor的JSON类型创建JWT标头:

  // Google OAuth-创建JWT标头var headersJSON = JSON()try标头JSON.set(“ alg”,“ RS256”)try标头JSON.set(“ typ”,“ JWT”) 

接下来,我们将创建声明:

  // // Google OAuth-创建JWT ClaimletletTimes = Date()let timeToLive:TimeInterval = 60 * 30 // 30分钟,最大为60let expirationTime = issueTime.addingTimeInterval(timeToLive)var ClaimsJSON = JSON()try ClaimsJSON.set(“ iss”,“ firebase-adminsdk-notoj@vaporfirebasedemo.iam.gserviceaccount.com”)//服务帐户JSONtry声明中的“ client_email”值JSONtry ClaimsJSON.set(“ scope”,“ https://www.googleapis.com / auth / datastore“)try ClaimsJSON.set(” aud“,” https://www.googleapis.com/oauth2/v4/token")try ClaimJSON.set(“ exp”,Int(expirationTime.timeIntervalSince1970))try ClaimsJSON.set(“ iat”,Int(issuedTime.timeIntervalSince1970)) 

请注意,我们给JWT设置了30分钟的生命周期。 随时更改此值以适合您的需求。 较小的值可以说更安全,Google接受的最大值为60分钟。

要实际创建JWT,我们需要在jwt.json配置文件中获得对Signer对象的引用,该对象代表“ googleOAuth”签名者。 在这里,我们可以根据标头,声明和Signer创建最终的编码JWT:

  // // Google OAuth-创建JWT signatureguard,让oAuthSigner = self.signers?[“ googleOAuth”] else {返回Response(状态:.internalServerError, 
正文:“无法找到签名者”)} let accessTokenRequestJWT =试试JWT(headers:headersJSON,有效载荷:claimsJSON,签署者:oAuthSigner)let jwtString = try accessTokenRequestJWT.createToken()

发送OAuth请求

我们将使用Dropletclient属性,通过以下URL编码的表单参数对Google的OAuth URL执行POST请求:

  • grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
  • assertion=
  // Google OAuth-使用创建的JWTlet向Google进行身份验证oAuthParams =“ grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer&assertion = \(jwtString)”。urlQueryPercentEncoded 
让authResponse =试试self.client.post(“ https://www.googleapis.com/oauth2/v4/token”,查询:[:],[。contentType:“ application / x-www-form-urlencoded”] ,oAuthParams,通过:[])
// // Google OAuth-处理来自Googleswitch的响应authResponse.status {case .ok:Guard let accessToken:String = try authResponse.json?.get(“ access_token”)else {return Response(status:.internalServerError,
正文:“ Google身份验证响应未包含访问令牌”)} // ...默认值:返回Response(状态:.internalServerError,
正文:“意外的Google身份验证响应:'\(authResponse.status)”)}

Google的成功响应将包含一个access_token ,这是我们在向Firebase发出的请求中需要提供的内容:

  { 
“ access_token”:“ 1 / 8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M”,
“ token_type”:“承载者”,
“ expires_in”:3600
}

现在,我们已经可以通过Google进行身份验证了,我们将立即将数据发送到Firebase! 但首先,介绍一下Cloud Firestore中使用的数据结构。

Cloud Firestore数据结构

在Firestore中,所有内容都是一个Document ,它们被组织成CollectionDocument基本上只是遵循某种结构的JSON。 CollectionDocument也是隐式创建的。 只需将所需的数据写入所需的位置,其余的内容由Cloud Firestore负责! 以下是添加根集合及其根Document Firestore数据库的外观:

Cloud Firestore文件

那么,Firestore Document什么样的? 至少必须包含一个name和一个fields属性。 fields属性使用JSON描述您的数据。 Firestore还为您管理createTimeupdateTime属性:

  { 
“名称”:字符串,
“字段”:{
字符串:{
对象(值)
},
...
},
“ createTime”:字符串,
“ updateTime”:字符串,
}

这是创建fields对象时可以使用的:

  { 
//联合字段value_type只能是以下之一:
“ nullValue”:null,
“ booleanValue”:布尔值,
“ integerValue”:字符串,
“ doubleValue”:数字,
“ timestampValue”:字符串,
“ stringValue”:字符串,
“ bytesValue”:字符串,
“ referenceValue”:字符串,
“ geoPointValue”:{
对象(LatLng)
},
“ arrayValue”:{
对象(ArrayValue)
},
“ mapValue”:{
对象(MapValue)
},
//联合字段value_type的可能类型列表的结尾。
}

如果您想了解有关Firestore Document模型的更多信息,请随时参考Firestore的文档。

通过Firestore进行身份验证

为了验证您对Cloud Firestore的请求,只需将Authorization标头的值设置为在Google OAuth过程中获得的访问令牌:

 授权:不记名{YOUR_TOKEN} 

另外,请记住,在我们创建的JWT的“声明”中指定的scope的值为https://www.googleapis.com/auth/datastore 。 这个scope可以告诉您最终告诉Firebase您可以访问Firestore数据库。

访问Firestore数据

Firestore REST API遵循常见的CRUD操作模式。 CRUD是“创建读取更新删除”的首字母缩写。 Firestore文档包含有关可用操作的完整列表的详细信息:

为了形成Cloud Firestore请求,您只需将数据库中Document的路径附加到Firestore基本URL上即可。 例如,基本URL https://firestore.googleapis.com/v1beta1/和文档路径/projects/YOUR_PROJECT_ID/databases/(default)/documents/cities/LA生成如下所示的最终请求URL:

  https://firestore.googleapis.com/v1beta1/projects/YOUR_PROJECT_ID/databases/(默认)/ documents / cities / LA 

创建Firestore文档

对于此示例,我们需要创建一个表示随机数的Document 。 它还将包括绘制下一个随机数的日期。 该Document的JSON表示形式最终看起来像这样:

  { 
“ name”:“项目/基于vaporfire的emo /数据库/(databaseId)/ documents / randomNumbers / theRandomNumber”,
“字段”:{
“数字”:{
“ integerValue”:4
},
“ nextUpdate”:{
“ timestampValue”:“ 2018-02-26T16:34:46 + 00:00”
}
}
}

和构建此代码:

  //生成一个新的随机数randomNumber = arc4random_uniform(100)+ 1 //生成一个介于[1-100]之间的随机数// Firebase-构建Cloud Firestore资源namelet projectId =“ vaporfirebasedemo” let databaseId =“(默认值)”让documentPath =“ randomNumbers / theRandomNumber” let resourceName =“ projects / \(projectId)/ databases / \(databaseId)/ documents / \(documentPath)” // Firebase-创建一个Firestore文档对象来表示随机数var documentJSON = JSON ()try documentJSON.set(“ name”,“ \(resourceName)”)//文档的Firestore资源名称,例如projects / {projectId} / databases / {databaseId} / documents / {document_path} var numberValueJSON = JSON()尝试numberValueJSON.set(“ integerValue”,randomNumber)var numberJSON = JSON()尝试numberJSON.set(“ number”,numberValueJSON)尝试documentJSON.set(“ fields”,numberJSON) 

将文档发送到Firestore数据库

首先,如上所述,我们需要在Firestore数据库中形成指向Document的URL:

 让firestoreBaseURL =“ https://firestore.googleapis.com/v1beta1”让firestoreDocumentURL =“ \(firestoreBaseURL)/ \(resourceName)” 

接下来,我们将使用patch方法来指示Firebase更新或插入我们发送的文档:

  // Firebase-将PATCH插入资源名称以插入或更新文档let firestoreResponse =试试self.client.patch(firestoreDocumentURL,query:[:],[.authorization:“ Bearer \(accessToken)”],documentJSON,通过:[ ])switch firestoreResponse.status {case .ok:返回firestoreResponsedefault:返回Response(状态:.internalServerError,正文:“将文档保存到Firestore时出错:\(firestoreResponse)”)) 

最后,继续呼叫您的POST /nextRandom路由。 运气好的话,您应该取回代表刚刚创建的Document的Firestore响应。

到现在为止,我们已将所有逻辑放入Routes.swift文件中。 但是,在生产应用程序中,这是非常不希望的。 随时在GitHub上查看完成的示例项目,以了解我如何组织示例项目以使Routes.swift文件保持精简。 完成的样本还将开始生成随机数,并将其立即发送到Firestore。 我把POST /nextRandom路由的实现留在了那里,但是从技术上讲,不再需要调用它了。

我还已经在GitHub上发布了iOS客户端应用程序。 客户端应用程序仅观察Firestore数据库中的“ randomNumbers / theRandomNumber” Document 。 每次有新更新出现时,它都会显示随机数,然后开始递减秒数,直到基于nextUpdate属性发布下一个随机数为止。

未来的改进

此示例仍可以进行一些改进。 如果您决定在生产应用程序中使用此技术,那么您最想照顾的一件事就是缓存Google访问令牌,而不是每次与Firebase通信时都执行Google服务器到服务器的OAuth流程。 您从Google收到的访问令牌会在一小时后过期,因此您需要根据expires_in属性的值来跟踪令牌的过期日期。

利用Firebase RPC API可能会实现另一项改进。 据我了解,gRPC利用HTTP / 2并启用了实时双向流传输,这实际上可以加快服务器与Firebase之间的通信通道的速度。 将来,我可能会尝试将RPC合并为该演示应用程序的下一个版本。

作为移动应用程序开发人员,Firebase和服务器端Swift是我工具箱中我最喜欢的一些新工具。 虽然从服务器端Swift环境连接到Firebase并没有从iOS应用程序连接到Firebase那样简化,但它仍然足够简单,如果您希望将Firebase整合到Swift后端中,它仍然是可行的选择。