Build a Command Line Tool with Swift and MongoDBBuild a Command Line Tool with Swift and MongoDB
Michael LynnPublished Feb 17, 2022 • Updated Sep 23, 2022
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.
Rather have a video run-through of this content? Check out the Youtube Video where my colleague Nic Raboy, and I talk through this very same content.
Here are the goals for this article.
- Increase your familiarity with MongoDB Atlas
- Introduce you to the Swift Language, and the Xcode Development Environment
- Introduce you to the MongoDB Swift Driver
- Introduce you to the Swift Package Manager
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
- Use Swift Package Manager to:
- Create a basic project.
- Integrate the MongoDB Swift Driver into your project
- Create an exectuable on your Mac.
Before we begin, let's clarify some of the things you'll have to have in place to get started.
- A Mac & MacOS (not an iOS device). You may be reading this on your Windows PC or an iPad. Sorry folks this tutorial was written for you to follow along on your Mac machine: MacBook, MacBook Pro, iMac, etc. You may want to check out macincloud if you're interested in a virtual Mac experience.
- Xcode. You should have Xcode Installed - Visit Apple's App Store to install on your Mac.
- Swift Installed - Visit Apple's Developer Site to learn more.
- Access to a MongoDB Database - Visit MongoDB Atlas to start for free. Read more about MongoDB Atlas.
If you haven't had much experience with Xcode or MacOS Application Development, check out the guides on Apple's Developer Hub. Getting started is very easy and it's free!
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)
- Create a (
for loop) using (String) to access, and print out the data in your
example.csvfile. (See csvread.swift)
- Modify your package to pull in the MongoDB Swift Driver. (See Package.swift)
- Test. (
swift build; swift run) Errors? See FAQ section below.
- Modify your code to incorporate the MongoDB Swift Driver, and write documents. (See Sources/command-line-swift-mongodb/main.swift)
- 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.
You'll need to create a new cluster and load it with sample data. My colleague Maxime Beugnet has created a video tutorial to help you out, but I also explain the steps below:
- Click "Start free" on the MongoDB homepage.
- 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".
Once your cluster launches, be sure that you add a Network Exception entry for your current IP and then add a database username and password. Take note of the username and password - you'll need these shortly.
We'll start on our journey by creating a Swift Package using Swift Package Manager. This tool will give us a template project and establish the directory structure and some scaffolding we'll need to get started. We're going to use the swift command line tool with the
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 String 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 contentsOfFile 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 1: We'll use the Foundation core library. This gives us access to some basic string, character and comparison methods. The import declaration gives us access to native, as well as third party libraries and modules.
- 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.
Navigate in the project explorer to find the Package.swift file. Replace the contents with the Package.swift file from the repo:
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.
Next we've included references to the packages we'll need in our software to insert, and manipulate MongoDB data. In this example, we'll concentrate on an asynchronous implementation. Namely, the mongo-swift-driver.
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. Codeables 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: Encodable, and Decodable. 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 JSONEncoder and JSONDecoder 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.
I've prepared a section below titled Troubleshooting in case you come across some common errors. I've tried my best to think of all of the usual issues you may find. However, if you do find another, issue, please let me know. The best way to do this is to Sign Up for the MongoDB Community and be sure to visit the section for Drivers and ODMs.
Use this section to help solve some common problems. If you still have issues after reading these common solutions, please visit me in the MongoDB Community.
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.