Build a Command Line Tool with Swift and MongoDBBuild a Command Line Tool with Swift and MongoDB
Rate this code example
Building something with your bare hands gives a sense of satisfaction like few other tasks. But there's really no comparison to the feeling you get when you create something that not only accomplishes the immediate task at hand but also enables you to more efficiently accomplish that same task in the future. Or, even better, when someone else can use what you have built to more easily accomplish their tasks. That is what we are going to do today. We are going to build something that will automate the process of importing data into MongoDB.
An executable program is powerful because it's self contained and transportable. There's no requirement to compile it or ensure that other elements are present in the environment. It just runs. You can share it with others and assuming they have a relatively similar system, it'll just run for them too. We're going to focus on accomplishing our goal using Swift, Apple's easy-to-learn programming language. We'll also feature use of our brand new MongoDB Swift Driver that enables you to create, read, update and delete data in a MongoDB database.
Here are the goals for this article.
- Increase your familiarity with MongoDB Atlas
By the end of this article, if we've met our goals, you will be able to do the following:
- Use Xcode to begin experimenting with Swift
Before we begin, let's clarify some of the things you'll have to have in place to get started.
The task I'm trying to automate involves importing data into a MongoDB database. Before we get too far down the path of creating a solution, let's document our set of requirements for what we'll create.
Here's a quick run-down of the steps we'll work on to complete our task.
- Launch an Atlas Cluster.
- Add a Database user/password, and a network exception entry so you can access your database from your IP Address.
- Create a Swift project using Swift Package Manager (
swift package init --type=executable)
- Generate an Xcode project using Swift Package Manager (
swift package generate-xcodeproj)
- Test. (
swift build; swift run) Errors? See FAQ section below.
- Test. (
swift build; swift run) Errors? See FAQ section below.
- Create executable and release. (
swift package release)
- The solution must import a set of data that starts in CSV (or tabular/excel) format into an existing MongoDB database.
- Each row of the data in the CSV file should become a separate document in the MongoDB Database. Further, each new document should include a new field with the import date/time.
- It must be done with minimal knowledge of MongoDB - i.e. Someone with relatively little experience and knowledge of MongoDB should be able to perform the task within several minutes.
We could simply use mongoimport with the following command line:
If you're familiar with MongoDB, the above command line won't seem tricky at all. However, this will not satisfy our requirements for the following reasons:
- Requirement 1: Pass - It will result in data being imported into MongoDB.
- Requirement 2: Fail - While each row WILL become a separate document, we'll not get our additional date field in those documents.
- Requirement 3: Fail - While the syntax here may seem rather straight-forward if you've used MongoDB before, to a newcomer, it can be a bit confusing. For example, I'm using localhost here... when we run this executable on another host, we'll need to replace that with the actual hostname for our MongoDB Database. The command syntax will get quite a bit more complex once this happens.
So then, how will we build something that meets all of our requirements?
We can build a command-line executable that uses the MongoDB Swift Driver to accomplish the task. Building a program to accomplish our task enables us to abstract much of the complexity associated with our task. Fortunately, there's a driver for Swift and using it to read CSV data, manipulate it and write it to a MongoDB database is really straight forward.
- Enter your details, or just sign up with your Google account, if you have one.
- Accept the Terms of Service
- Create a Starter cluster.
- Select the cloud provider where you'd like to store your MongoDB Database
- Pick a region that makes sense for you.
- You can change the name of the cluster if you like. I've called mine "MyFirstCluster".
There are several variations that we can use. Before jumping in, let's example the difference in some of the flags.
This most basic variation will give us a general purpose project. But, since we're building a MacOS, executable, let's add the
--typeflag to indicate the type of project we're working on.
This will create a project that defines the "product" of a build -- which is in essense our executable. Just remember that if you're creating an executable, typically for server-side Swift, you'll want to incorporate the
Xcode is where most iOS, and Apple developers in general, write and maintain code so let's prepare a project so we can use Xcode too. Now that we've got our basic project scaffolding in place, let's create an Xcode project where we can modify our code.
To create an Xcode project simply execute the following command:
Then, we can open the
.xcprojectfile. Your mac should automatically open Xcode as a result of trying to open an Xcode Project file.
With our project scaffolding in place, let's turn our focus to the data we'll be manipulating with our executable. Let's look at the raw data first. Let's say there's a list of students that come out every month that I need to get into my database. It might look something like this:
In this example data, we have 3 basic fields of information: First Name, Last Name, and a Boolean value indicating whether or not the student has been assigned to a specific class.
We want to get this data from it's current form (CSV) into documents inside the database and along the way, add a field to record the date that the document was imported. This is going to require us to read the CSV file inside our Swift application. Before proceeding, make sure you either have similar data in a file to which you know the path. We'll be creating some code next to access that file with Swift.
Once we're finished, the data will look like the following, represented in a JSON document:
In order to get the rows and fields of names into MongoDB, we'll use Swift's built-in class. This is a powerhouse utility that can do everything from read the contents of a file to interpolate embedded variables and do comparisons between two or more sets of strings. The class method of the String class will access the file based on a filepath we provide, open the file and enable us to access its contents. Here's what our code might look like if we were just going to loop through the CSV file and print out the rows it contains.
You may be tempted to just copy/paste the code below. I would suggest that you type it in by hand... reading it from the screen. This will enable you to experience the power of auto-correct, and code-suggest inside Xcode. Also, be sure to modify the value of the
pathvariable to point to the location where you put your
Let's take a look at what's happening here.
- Line 3: Hard code a path variable to the CSV file.
- Lines 6-7: Use the String method to access the contents of the CSV file.
- Line 8: Loop through each row in our file and display the contents.
To run this simple example, let's open the
main.swiftfile that our that the command
swift package initcreated for us. To edit this file, in Xcode, To begin, let's open the main.swift file that our that the command
swift package initcreated for us. To edit this file, in Xcode, traverse the folder tree under Project->Sources-Project name... and open
main.swift. Replace the simple
hello worldwith the code above.
Running this against our
example.csvfile, you should see something like the following output. We'll use the commands
swift build, and
With this basic construct in place, we can now begin to incorporate the code necessary to insert a document into our database for each row of data in the csv file. Let's start by configuring Swift Package Manager to integrate the MongoDB Swift Driver.
We're including a statement that tells Swift Package Manager that we're building this executable for a specific set of MacOS versions.
Tip: If you leave this statement out, you'll get a message stating that the package was designed to be built for MacOS 10.10 or similar.
Now that we've included our dependencies, let's build the project. Build the project often so you catch any errors you may have inadvertently introduced early on.
You should get a response similar to the following:
Now let's modify our basic program project to make use of our MongoDB driver.
Line 2 imports the driver we'll need (mongo-swift).
Next, we configure the driver.
Remember to replace
<username>with the user you created in Atlas.
To read and write data from and to MongoDB in Swift, we'll need to leverage a Codable structure. are an amazing feature of Swift and definitely helpful for writing code that will write data to MongoDB. Codables is actually an alias for two protocols: , and . When we make our
Structconform to the Codable protocol, we're able to encode our string data into JSON and then decode it back into a simple
Structusing and respectively. We'll need this structure because the format used to store data in MongoDB is slightly different that the representation you see of that data structure in Swift. We'll create a structure to describe what our document schema should look like inside MongoDB. Here's what our schema
Structshould look like:
Notice we've got all the elements from our CSV file plus a date field.
We'll also need a few temporary variables that we will use as we process the data.
countand a special temporary variable I'll use when I determine whether or not a student is assigned to a class or not...
tempAssigned. Lastly, in this code block, I'll create a variable to store the state of our position in the file. header will be set to true initially because we'll want to skip the first row of data. That's where the column headers live.
Now we can create a reference to the collection in our MongoDB Database that we'll use to store our student data. For lack of a better name, I'm calling mine
personCollection. Also, notice that we're providing a link back to our
withTypeargument to the collection method. This ensures that the driver knows what type of data we're dealing with.
The next bit of code is at the heart of our task. We're going to loop through each row and create a document. I've commented and explained each row inline.
Importing data is a common challenge. Even more common is when we want to automate the task of inserting, or manipulating data with MongoDB. In this how-to, I've explained how you can get started with Swift and accomplish the task of simplifying data import by creating an executable, command-line tool that you can share with a colleague to enable them to import data for you. While this example is quite simple in terms of how it solves the problem at hand, you can certainly take the next step and begin to build on this to support command-line arguments and even use it to not only insert data but also to remove, and merge or update data.
This occurs when Swift was unable to build the
mongo-swift-drivermodule. This most typically occurs when a developer is attempting to use Xcode and has not specified a minimum target OS version. Review the attached image and note the sequence of clicks to get to the appropriate setting. Change that setting to 10.15 or greater.
Continuously Building and Hosting our Swift DocC Documentation using Github Actions and Netlify
May 10, 2022