Getting Started with Firebase for Server-Side Swift
Connecting Swift Vapor to Firebase’s Cloud Firestore Database
The Firebase suite of tools is awesome. It’s great to have so many useful development services bundled under one roof, and the ability to get started for free is great! Firebase offers SDKs for several platforms to communicate with their services, including iOS. However, the iOS SDK is heavily coupled to the iOS application lifecycle and thus not usable in a server-side Swift environment. Luckily, Firebase offers a REST API which gives us an avenue to use their services when developing a server in Swift. Having done just that recently, this post outlines the steps needed to get started with the Firebase REST API in a server-side Swift environment. I’ll be connecting a Swift Vapor app to Firebase’s new Cloud Firestore technology, but the general process should apply to any other server-side Swift framework and Firebase service.
The Tech Stack
Cloud Firestore
Cloud Firestore is Firebase’s “new and improved” NoSQL cloud database. As with their previous product, Firebase Realtime Database, Cloud Firestore syncs data in realtime across clients and offers first-class support for offline mode.
Cloud Firestore vs Realtime Database
If you’ve used Firebase’s Realtime Database product in the past, you may be wondering how Cloud Firestore is different. The Firebase documentation goes into more detail, but it basically boils down to Firebase improving upon the successes of the original Realtime Database product. The older Realtime Database required denormalization and data flattening techniques to prevent bottlenecks as your database grew and evolved. Cloud Firestore improves upon this by offering richer, faster queries and better scalability.
So if Cloud Firestore is an improved version of Realtime Database, is Realtime Database even relevant anymore? The answer is yes. The real strength of the Realtime Database continues to be its speed. If you need to optimize your product for efficient, low-latency syncing, then you will probably want to use Realtime Database instead of Cloud Firestore. That’s not to say that Cloud Firestore won’t do the job. It still syncs things super fast (within milliseconds).
Vapor 2.x
Vapor is one of the many server-side Swift libraries on the market. I feel they’ve done a great job keeping things simple to understand and making it easy to get started with server-side Swift development. They even recently launched Vapor Cloud, which makes it ridiculously easy to deploy your server into the real world. It’s even free for small projects! For this project, I used Vapor 2.x since Vapor 3 was still in beta when I started.
The Sample Project
I’ll be using these technologies to implement a simple proof-of-concept for utilizing server-side Swift to implement custom backend logic while relying on Firebase for realtime updates to client apps. For this demo, the server will simply be generating a random number every 60 seconds and then publishing that number to the Cloud Firestore database along with the date that it will generate the next random number. When everything is up and running, the client app will look something like this:
Setting up Firebase
The first thing you’ll want to do after creating your Firebase project is to enable Cloud Firestore. This is easy enough to do. Open your Firebase project and navigate to Develop → Database and turn it on:
Once enabled, you’ll see an empty database that looks something like this:
Connecting to Firebase
Now that we’ve got Cloud Firestore enabled for our project, how can we connect to it? As I mentioned above, the Firebase iOS SDK is off the table. Perhaps the SDK will someday be compatible with server-side Swift, but the Firebase team currently has no concrete plans in place.
Since we can’t use the iOS SDK, our options for connecting to Cloud Firestore from Vapor are either using the REST API or the RPC API. Since I don’t know enough about RPC yet, we’ll be using the REST API for this example.
If you’re using the older Realtime Database product, I should mention that you actually have another option. There exists an open source Swift library called FirebaseSwift that provides some conveniences for connecting to Realtime Database from a server-side Swift environment. However, you’ll still need to manually generate and manage your Google OAuth token, which I’ll talk about in a bit.
Cloud Firestore REST API
Using the Cloud Firestore REST API is as simple as:
- Authenticating with Google to receive a server-to-server access token.
- Using that access token to read/write data to the Cloud Firestore database.
Google Server-to-Server OAuth
Authentication Options
There are three ways that you can authenticate with Google to receive a server-to-server access token:
- Use one of the 10 official client libraries.
- Manually follow Google’s server-to-server OAuth 2.0 steps.
- Use the
gcloud
command-line tool.
Although Google strongly encourages you to use one of their client libraries, Swift is not supported yet. 😔
This means that we’ll need to manually implement the OAuth process. I haven’t looked into the gcloud
command-line tool, but it seems to me that it’d only really be useful for generating one-off tokens for testing.
Authentication Prerequisites
In order to authenticate with Google, you’ll need to use a Google “service account”. A service account is an account that belongs to your application itself rather than an individual user of your app. A service account is composed of two things:
- A unique email address, which is auto-generated for you by Google.
- A public/private key pair.
Obtaining a Google Access Token
So we’ve established that we’ll need to manually follow Google’s OAuth process. How does that work? The process can be summarized in three steps:
- Create a JSON Web Token (JWT), signed with your service account’s private key.
- Use the JWT to request an access token from the Google OAuth 2.0 Authorization Server.
- Handle the JSON response from the Authorization Server.
A sequence diagram of this flow looks something like this:
I should note that Google’s documentation again calls out that they’d really like for you to use one of their client libraries to authenticate. I think they’re trying to tell us something…🤔
Since there’s no Swift-compatible Google authentication library available, we’ll just have to ignore Google’s persistent attempts to dissuade us from manually implementing their OAuth flow. But first, a quick crash course on JWTs…
JWT — A Quick Refresher
A JWT, pronounced “jot”, is basically just a piece of data composed of three parts:
- A “header”, which specifies the format of the token and the signing algorithm used.
- A “payload” (or “claims”) which holds information about the token, including things like which user the token represents, what permissions (scopes) they have available, and the token lifetime.
- A “signature”, which is a cryptographic signature of the header and claims appended together.
Each part of the JWT is base64 url-encoded and then appended together with periods to form the final representation of the JWT. Overall, the process looks something like this:
It’s important to note that even though it may look like it, a JWT is not encrypted. This means that anyone can read the contents of the “claims”. If you are ever creating JWTs for your own purposes, don’t store any sensitive information in them. The cryptographic signature of the JWT simply allows the entity that issued the JWT to verify the integrity of the data. If a party tries to modify any of the information in the “header” or “claims”, then the signature will indicate that the data is invalid and the issuer can reject the token.
Generating the Google OAuth JWT
So how do we generate the Google OAuth JWT using Vapor? In order to generate the JWT, we need to satisfy two prerequisites:
- Obtain the Google Service Account credentials that I mentioned earlier (specifically the private key).
- Configure the Vapor project by adding the
JWTProvider
helper library and configuring aSigner
with the service account’s private key.
Google Service Account Credentials
You can manage serve accounts on the Google Service Accounts page, but we can also access the service account associated with our Firebase project through the Firebase Console, which is the process that we’ll follow right now.
It’s important to note that even though Google generates the public/private key pair for your service account, they don’t keep a copy of the private key. It’s up to you to keep track of it and store it securely.
Downloading the Credentials
In order to download your service account credentials, first open your project’s settings on Firebase by tapping the gear icon next to “Project Overview” and then selecting “Project Settings”:
Next, navigate over to the “Service Accounts” tab and then click on the “Generate New Private Key” button at the bottom:
This will download a JSON file to your computer, which is Google’s preferred format for distributing these private keys.
Credentials JSON Format
Open up your service account credentials JSON file. It should look something like this:
Notice the private_key
field. This is what the Signer
in our Vapor app will need to generate and sign the JWTs used for Google’s server-to-server OAuth process.
Transforming the Private Key
Unfortunately, the private key in its current form will not work for us. The main issue with this version of the key is that it’s just a regular “private key”. What we need is an “RSA private key”. This small detail caused me hours of pain until a friend enlightened me to the fact that there are different types of private keys.
Create a File for the Key
The first thing we need to do is copy the value of the private_key
field in the credentials JSON to its own file. Save this file as GooglePrivateKey.key
.
Clean up Whitespace
If you look at the private key right now, you might notice that there are several \n
newline characters:
-----BEGIN PRIVATE KEY——\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDBsMABtSYcQBX1\n03xsgbc36ekABgnfKdoJlOVkstpLlv1zpev2grxvCS+VR9D1dbrZJ9QJKfHoyjqT\nabvhnxMTdvwVQ94Ohoo0lShSvN6NO94a6Hy8iWSHDZuM4LScPGp9H3eNjdMysQ+L\n95xi4qioPndyJzwPcOOzM8WirWe5Osk+vFy4sSvVQFpwaMqvQ35abVVVAydWOF6G\nlmliKRiGB5Ueb.C..qdYE3j+o3WhweBw==\n——END PRIVATE KEY-----\n
We need to get rid of those by converting them into actual newlines. Go ahead and highlight each \n
occurrence and press “Enter” on your keyboard to replace it with a newline. You should end up with something like this:
-----BEGIN PRIVATE KEY——MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDBsMABtSYcQBX1
03xsgbc36ekABgnfKdoJlOVkstpLlv1zpev2grxvCS+VR9D1dbrZJ9QJKfHoyjqT
abvhnxMTdvwVQ94Ohoo0lShSvN6NO94a6Hy8iWSHDZuM4LScPGp9H3eNjdMysQ+L
95xi4qioPndyJzwPcOOzM8WirWe5Osk+vFy4sSvVQFpwaMqvQ35abVVVAydWOF6G
lmliKRiGB5Ueb.C..qdYE3j+o3WhweBw==
——END PRIVATE KEY——
If you know of a better way to do this instead of manually, please let me know! I think there’s a way to do this with the sed
command on macOS, but I haven’t had time to experiment with it yet.
Convert the Private Key into an RSA Private Key
Next, use OpenSSL to generate an RSA private key from your newly cleaned up private key:
openssl rsa -in GooglePrivateKey.key -out GooglePrivateKey_RSA.key
Prepare RSA Private Key for Use in Vapor
We’re almost done transforming this private key into the format we want. The next thing we need to do is convert this RSA private key into a single line. Open up GooglePrivateKey_RSA.key
in a text editor and remove all of the newlines so that the entire key is on one line.
Next, remove the -----BEGIN RSA PRIVATE KEY-----
and -----END RSA PRIVATE KEY-----
so that we just have the contents of the key itself in a single line. You should end up with something like this:
MIIEogIBAAKCAQEAwbDAAbUmHEAV9dN8bIG3N+npAAYJ3ynaCZTlZLLaS5b9c6Xr9oK8bwkvlUfQ9XW62SfUCSnx6Mo6k2m74Z8TE3b8FUPeDoaKNJUoUrzejTveGuh8vIlkhw2bjOC0nDxqfR93jY3TMrEPi/ecYuKoqD53cic8D3DjszPFoq1nuTrJPrxcuLEr1UBacGjKr0N+Wm1VVQMnVjhehpZpYikYhgeVHmwvMPay2ylcerNgQnb81tF8WN8IskLU5igseY/Ayi4Vxvmo2NjfYY5ppROsM/DDZcKgroOhjNLfzeYSHIh3qfI4...Vno4pV3a7cD7aaZjJL+gr1+ipSHiu8BSE+FL+K3hitR6bMGQxZWVc/jfsB+gjx4I4WqLAoGAU+9+PkwKqVqhyR4MrnRrCcpSfhrkD7jkvOWOYoGhBka5r0GvRITP+BKYfcvooDzfXjGgBPFXzIdPlfrNwsUyKf1zARrwLExg+6/PMrWiVVubIuGYzhzrLtYQtF9sBhtgsNNYIkaH2mybgOZ2k45smk98Vug2KnWBN4/qN1ocHgc=
Setting Up Vapor
Now it’s almost time to start writing our server-side Swift code. Aren’t you excited?! There’s just a few setup items that we need to take care of first. I’m assuming that you’ve already got Vapor installed and a project created. If not, follow Vapor’s getting started instructions.
Adding JWTProvider
Add the JWTProvider
dependency to your Package.swift
file:
Next, regenerate your Xcode project:
swift package generate-xcodeproj
or:
vapor xcode
If it’s not already open, go ahead and open the Xcode project as well.
Initialize JWTProvider
Open the Config+Setup.swift
file and initialize the JWTProvider
in the setupProviders()
method:
/// Configure providersprivate func setupProviders() throws {
try addProvider(FluentProvider.Provider.self)
try addProvider(JWTProvider.Provider.self)
}
JWT Config
The final step involved with setting up JWTProvider
is to create a jwt.json
configuration file. Go ahead and make a file called jwt.json
in the Config
directory:
Populate Config/jwt.json
with the following contents:
{
"signers": {
"googleOAuth": {
"type": "rsa",
"algorithm": "rs256",
"key": "$GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY"
}
}
}
Here’s what’s going on in the JSON file:
- A top-level object called
signers
must be present. - For this example, we only have one signer called
googleOAuth
. - The
googleOAuth
signer specifies the algorithm and key used to perform the signing.
Notice that we did not paste in that single-line RSA private key that we just created. More on this in just a second, but we’re actually using the syntax for injecting environment variables into the JSON file. There are other options for how you configure your jwt.json
file as well. If you’d like to know more about that or JWTProvider
, feel free to check out the Vapor JWT documentation.
Vapor Development Process
Before we go any further, I’d like to take a moment to discuss my Vapor development process. I’ve found that it’s easiest to get started by putting all of your logic into the Routes.swift
file. This way, you can test/trigger your logic by issuing a request to your route using something like Postman. If you’re route is a GET
, then I suppose you could trigger it via your browser as well.
For this example, I started by creating a POST
route called “nextRandom”:
import Vapor
import JWT
import Foundationextension Droplet {
func setupRoutes() throws { post("nextRandom") { request in
do {
// ...
} catch {
debugPrint("\(error)")
return Response(status: .internalServerError,
body: "\(error)")
}
}
}
}
Securing the Private Key
As I mentioned previously, we need to be extra careful with our private key. In the words of the wise wizard Gandolf:
Just Say No to Source Control
I’ll say this just once, but I’ll say it loud:
NEVER COMMIT YOUR PRIVATE KEY INTO SOURCE CONTROL!
Consider yourself warned.
Setup the Private Key Environment Variable
As I mentioned above, the value of the key
in our Config/jwt.json
file is set to an environment variable. This allows us to keep the key out of source control by injecting it at runtime.
To set this up, edit your app’sRun
scheme and add an environment variable called GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
. Next, paste your single-line RSA private key as the value for GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
:
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY environment variable
Please note that if you’re using a “shared” scheme, do not do this! Shared schemes are often committed into source control so you wouldn’t be doing yourself any favors by setting up this environment variable.
Instead, if you’re using a shared scheme, create your jwt.json
file inside of the Config/secrets
directory. Just make sure to double-check that the Config/secrets
directory is included in your .gitignore
file so that it’s excluded from source control. The default Vapor .gitignore
already has this directory excluded:
# Created by https://www.gitignore.io/api/vapor### Vapor ###
Config/secrets### Vapor Patch ###
Packages
.build
xcuserdata
*.xcodeproj
DerivedData/
.DS_Store# End of https://www.gitignore.io/api/vapor
Private Key Gotchas
Just a few “gotchas” to be aware of involving your private key:
- Make sure to securely store a copy of your cleaned, single-line RSA private key. This is mainly just a convenience so that you don’t have to reconvert the key from its original JSON file format if you ever need to reconfigure your Xcode project.
- Deleting the Xcode project will wipe out the
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
environment variable. However, simply regenerating the Xcode project is fine. - Your app will crash on launch if the private key in your
Config/jwt.json
file is missing or invalid.
That last point is important. JWTProvider
will fail to initialize if it can’t generate a valid RSA private key based on the values in your jwt.json
configuration file. If this happens, your Vapor app will crash with a JWT error: Could not create key
error:
Authenticating with Google using Vapor
Finally, we’ve got all the setup out of the way and can begin writing our server-side Swift code! 🎉 Go ahead and open Routes.swift
and get ready to start filling in the implementation of the POST /nextRandom
endpoint. If you want to peek ahead to what this route will look like when we’re done, then feel free to take a look at the Routes.swift file on the feature/basicExample branch.
Creating the Google OAuth JWT
Use Vapor’s JSON
type to create the JWT headers:
// Google OAuth - Create the JWT headersvar headersJSON = JSON()try headersJSON.set("alg", "RS256")try headersJSON.set("typ", "JWT")
Up next, we’ll create the claims:
// Google OAuth - Create the JWT claimslet issuedTime = Date()let timeToLive: TimeInterval = 60 * 30 // 30 minutes, max is 60let expirationTime = issuedTime.addingTimeInterval(timeToLive)var claimsJSON = JSON()try claimsJSON.set("iss", "firebase-adminsdk-notoj@vaporfirebasedemo.iam.gserviceaccount.com") // the value of "client_email" in the service account JSONtry claimsJSON.set("scope", "https://www.googleapis.com/auth/datastore")try claimsJSON.set("aud", "https://www.googleapis.com/oauth2/v4/token")try claimsJSON.set("exp", Int(expirationTime.timeIntervalSince1970))try claimsJSON.set("iat", Int(issuedTime.timeIntervalSince1970))
Notice that we’ve given our JWT a lifetime of 30 minutes. Feel free to change this value to suit your needs. A smaller value is arguably more secure, and the maximum value that Google will accept is 60 minutes.
To actually create the JWT, we need to get a reference to the Signer
object that represents the “googleOAuth” signer in our jwt.json
configuration file. From there, we can create the final, encoded JWT from the headers, claims, and Signer
:
// Google OAuth - Create the JWT signatureguard let oAuthSigner = self.signers?["googleOAuth"] else { return Response(status: .internalServerError,
body: "Unable to locate signer")}let accessTokenRequestJWT = try JWT(headers: headersJSON, payload: claimsJSON, signer: oAuthSigner)let jwtString = try accessTokenRequestJWT.createToken()
Sending the OAuth Request
We’ll use Droplet
’s client
property to perform a POST
request onto Google’s OAuth URL using the following URL-encoded form parameters:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion=<value of the JWT>
// Google OAuth - Authenticate with Google using the created JWTlet oAuthParams = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=\(jwtString)".urlQueryPercentEncoded
let authResponse = try self.client.post("https://www.googleapis.com/oauth2/v4/token", query: [:], [.contentType: "application/x-www-form-urlencoded"], oAuthParams, through: [])
// Google OAuth - Handle the response from Googleswitch authResponse.status {case .ok: guard let accessToken: String = try authResponse.json?.get("access_token") else { return Response(status: .internalServerError,
body: "Google auth response did not include an access token") } // ...default: return Response(status: .internalServerError,
body: "Unexpected Google auth response: '\(authResponse.status)")}
A successful response from Google will contain an access_token
, which is what we’ll need to provide in our requests to Firebase:
{
"access_token" : "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
"token_type" : "Bearer",
"expires_in" : 3600
}
Storing Data in Firebase Cloud Firestore
Now that we’ve got authentication with Google out of the way, we’ll be sending data to Firebase in no time! But first, a little about the data structures used in Cloud Firestore.
Cloud Firestore Data Structures
In Firestore, everything is a Document
, which are organized into Collection
s. A Document
is basically just JSON that follows a certain structure. Collection
s and Document
s are also created implicitly. Just write the data you want where you want and Cloud Firestore will take care of the rest! Here’s a peek at how our Firestore database will look once the root collection and its root Document
are added:
Cloud Firestore Document
So what does a Firestore Document
look like? At a minimum, it must contain a name
and a fields
property. The fields
property describes your data using JSON. Firestore also manages createTime
and updateTime
properties for you:
{
"name": string,
"fields": {
string: {
object(Value)
},
...
},
"createTime": string,
"updateTime": string,
}
And here’s what you have at your disposal when creating your fields
object:
{
// Union field value_type can be only one of the following:
"nullValue": null,
"booleanValue": boolean,
"integerValue": string,
"doubleValue": number,
"timestampValue": string,
"stringValue": string,
"bytesValue": string,
"referenceValue": string,
"geoPointValue": {
object(LatLng)
},
"arrayValue": {
object(ArrayValue)
},
"mapValue": {
object(MapValue)
},
// End of list of possible types for union field value_type.
}
Feel free to reference Firestore’s documentation if you’d like to learn more about the Firestore Document
model.
Authenticating with Firestore
In order to authenticate your requests to Cloud Firestore, simply set the value of the Authorization
header to the access token obtained during the Google OAuth process:
Authorization: Bearer {YOUR_TOKEN}
As a side note, remember that the scope
specified in the “claims” of the JWT that we created had a value of https://www.googleapis.com/auth/datastore
. This scope
is what tells ultimately tells Firebase that it’s okay for you to access the Firestore database.
Accessing Firestore Data
The Firestore REST API follows common CRUD operational patterns. CRUD is an acronym for Create Read Update Delete. The Firestore documentation contains details on the full list of operations available:
In order to form a Cloud Firestore request, you simply append the path to the Document
in your database onto the Firestore base URL. For example, the base URL https://firestore.googleapis.com/v1beta1/
and document path /projects/YOUR_PROJECT_ID/databases/(default)/documents/cities/LA
generate a final request URL that looks like this:
https://firestore.googleapis.com/v1beta1/projects/YOUR_PROJECT_ID/databases/(default)/documents/cities/LA
Creating a Firestore Document
For this example, we need to create a Document
that will represent a random number. It will also include the date that the next random number will be drawn. The JSON representation for this Document
will end up looking something like this:
{
"name": "projects/vaporfirebasedemo/databases/(databaseId)/documents/randomNumbers/theRandomNumber",
"fields": {
"number": {
"integerValue": 4
},
"nextUpdate": {
"timestampValue": "2018-02-26T16:34:46+00:00"
}
}
}
And the code that builds this:
// Generate a new random numberlet randomNumber = arc4random_uniform(100) + 1 // Generates a random number between [1-100]// Firebase - Build the Cloud Firestore resource namelet projectId = "vaporfirebasedemo"let databaseId = "(default)"let documentPath = "randomNumbers/theRandomNumber"let resourceName = "projects/\(projectId)/databases/\(databaseId)/documents/\(documentPath)"// Firebase - Create a Firestore document object to represent the random numbervar documentJSON = JSON()try documentJSON.set("name", "\(resourceName)") // The Firestore resource name of the document, for example projects/{projectId}/databases/{databaseId}/documents/{document_path}var numberValueJSON = JSON()try numberValueJSON.set("integerValue", randomNumber)var numberJSON = JSON()try numberJSON.set("number", numberValueJSON)try documentJSON.set("fields", numberJSON)
Sending the Document to the Firestore Database
First, as mentioned above, we need to form the URL that points to our Document
in the Firestore database:
let firestoreBaseURL = "https://firestore.googleapis.com/v1beta1"let firestoreDocumentURL = "\(firestoreBaseURL)/\(resourceName)"
Next, we’ll use the patch method to instruct Firebase to update or insert the Document that we send:
// Firebase - PATCH onto the resource name to insert or update the documentlet firestoreResponse = try self.client.patch(firestoreDocumentURL, query: [:], [.authorization: "Bearer \(accessToken)"], documentJSON, through: [])switch firestoreResponse.status {case .ok: return firestoreResponsedefault: return Response(status: .internalServerError, body: "Error saving document to Firestore: \(firestoreResponse)")}
Finally, go ahead and make a call to your POST /nextRandom
route. With any luck, you should get back the Firestore response representing the Document
that was just created.
Completed Sample Project
Up until this point, we’ve put all of our logic inside of the Routes.swift
file. However, in a production app this is highly undesirable. Feel free to check out the completed sample project on GitHub to see how I organized the sample project to keep the Routes.swift
file a little more lean. The completed sample also starts generating random numbers and sending them to Firestore right away. I left the implementation of the POST /nextRandom
route in there, but technically it’s not necessary to call it anymore.
I’ve also posted the iOS client app on GitHub. The client app simply observes the “randomNumbers/theRandomNumber” Document
in the Firestore database. Each time a new update comes in, it displays the random number and then begins counting down the seconds until the next random number is posted based on the nextUpdate
property.
Future Improvements
There are still some improvements that could be made to this example. One thing that you will most certainly want to take care of if you decide to use this technique in a production app is to cache the Google access token instead of performing the Google server-to-server OAuth process each time you communicate with Firebase. The access tokens you receive from Google expire after one hour so you’ll need to keep track of the expiration date of your token based on the value of the expires_in
property.
Another improvement might be realized by utilizing the Firebase RPC API. From what I understand, gRPC utilizes HTTP/2 and enables realtime bidirectional streaming which could really speed things up in the communication channel between your server and Firebase. In the future, I may try to incorporate RPC as the next evolution of this demo app.
Conclusion
Firebase and server-side Swift are some of my favorite new tools in my toolbox as a mobile application developer. While connecting to Firebase from a server-side Swift environment isn’t quite as streamlined as connecting to Firebase from an iOS app, it’s still simple enough to do that it remains a viable option if you‘re looking to incorporate Firebase into your Swift backend.