Victor Micha, Datastax Polaris Intern
Build an app in Swift that connects to the Datastax Astra Database. By replicating this project, you will have an IOS app with fully functional backend and frontend.
It doesn't matter if you join our workshop live or you prefer to work at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop:
This sample app is coded in Swift and was developed on the Xcode IDE. It connects to the Astra DB using the Document API. It handles user accounts (signing up, signing in, changing password and deleting accounts) as well as manually entering an order, taking a picture of the receipt to post an order, and seeing all past orders.
GroceryHandler is an application for facilitating the accounting of splitting expenses with others. For example, if roommates buy groceries together in one order, this app would be able to indicate how much each person owes the buyer.
If you are new to databases, click here. If you are new to the Document API, click here.
- Download Xcode. The Xcode version for this application is version 13.4.1. It will take a while to download. While this is happening, move on to the next step.
- Create an Astra database account:
- After verifying the account, click on Create Database:
- Enter a database name, keyspace name, and region. Name them whatever you like. For the sample app, the keyspace is named gh_orders_userinfo. Then click on Create Database
- Click on Go To Database
- In the dashboard, click on the Connect tab:
- Create an application token:
- Select the role Administrator User then click on Generate Token
- Make sure to copy the Token somewhere for later
See -> Prerequisites first
Go to the directory where you would like your project to reside. If you are not sure what this means, open your Terminal app:
If you want your project to reside in your Desktop
sub-directory for example, run (run means type this command in your terminal window):
cd Desktop
Clone the project by running:
git clone https://github.com/datastaxdevs/workshop-IOS-Swift-Astra.git
This is all that is needed to strictly connect to the database. However, the sample app uses ML Kit Text Recognition API to decipher prices from receipts.
The Pods required for this are way too big to be stored on Github, so either follow the steps to integrate them in your project by clicking here or remove them from project by clicking here.
- In the same window, go to your project directory by running
cd workshop-IOS-Swift-Astra
- To install CocoaPods, run (if a password is required, use the one you use to log in to your laptop):
sudo gem install cocoapods
- To install the Pods directory, run:
pod install
Now the pods are installed and the project will build once opened on Xcode!
- After cloning the git repo, go to your project directory by running:
cd workshop-IOS-Swift-Astra
- Run these commands to remove the pods from the project (if a password is required, use the one you use to log in to your laptop):
sudo gem install cocoapods-deintegrate cocoapods-clean
pod deintegrate
pod cache clean --all
rm Podfile
- Make sure to comment out the whole MLTextRecognizer.swift file once you are in Xcode because the import statements will cause problems if the Pods were deleted successfully. Also comment out this line in the PictureReceipt.swift file:
prices = try await getPricesAsArray(image: image)
-
Click on the GroceryHandler.xcworkspace file and select Open
-
Now make sure the project builds successfully by doing command + B in Xcode.
-
To test the app, you will have to change the environment variables in Xcode and create the collections orders and userInfo. The instructions can be found here.
-
Make sure that the app will run on the iPhone 13 Pro Simulator:
-
Build the project and run the app by clicking the big play button at the top left of the Xcode window or by doing command + R. An iPhone Simulator window should pop up:
Click here or here to run it on your personal device instead. You will need a cable that connects to your laptop.
If you get an error when trying to sign in, create an account, delete an account, or change password, it may be because you missed step 4. Make sure to finish that step before testing the app.
That's it! You can now create accounts and post orders to your database! If you would like to populate your database with fake accounts and random orders, uncomment the DEV button in the ContentView.swift file by removing the /* and */ that surrounds this code:
Button("DEV"){
print("DEV")
//This is where you can test functions by running the app and clicking on this button
Task{
await populateUserInfoDB()
await populateOrdersDB(numNewOrders: 300)
}
}
.buttonStyle(CustomButton(color:Color(red: 0, green: 0, blue: 0.5)))
.padding(.all, 20)
Run the app on your phone and click this button!
If you would like to connect to your Astra DB from this app, you will need to change these environment variables in Xcode: ASTRA_DB_ID
, ASTRA_DB_REGION
, ASTRA_DB_TOKEN
, and ASTRA_DB_KEYSPACENAME
.
The ASTRA_DB_ID can be found in the dashboard of the astra website:
To change the values of the environment variables in Xcode, first click on Edit Scheme...
This will open the following window in which you can change the values of the environment variables:
The app accesses the environment variables in the GroceryHandlerApp.swift file:
//environment variables https://blog.eidinger.info/use-environment-variables-from-env-file-in-a-swift-package
public var ASTRA_DB_ID:String? {
ProcessInfo.processInfo.environment["ASTRA_DB_ID"]
}
public var ASTRA_DB_REGION:String? {
ProcessInfo.processInfo.environment["ASTRA_DB_REGION"]
}
public var ASTRA_DB_TOKEN:String? {
ProcessInfo.processInfo.environment["ASTRA_DB_TOKEN"]
}
public var ASTRA_DB_KEYSPACENAME:String? {
ProcessInfo.processInfo.environment["ASTRA_DB_KEYSPACENAME"]
}
Xcode sets up the environment variables, which means that the app can only be run from Xcode. Once you run it on your phone once, the icon will still be in your phone even when it isn't connected to your computer anymore. However, if you click the icon and try to log in or post orders, the app will crash because the environment variables will not be set up.
You can access Swagger UI from the Astra website:
To create an empty collection named newCol in keyspacename1 for example:
The collections for the sample app are named: userInfo and orders. They are both in the keyspace gh_orders_userinfo. If you want to customize the app by creating different collections, make sure to change using search and replace (in the DBController.swift file) the userInfo and orders to whatever you named them.
If you just want to test the app, you will have to create both of these collections in whatever keyspace you created.
The JSON to create the collection userInfo is the following:
{"name":"userInfo"}
The JSON to create the collection orders is the following:
{"name":"orders"}
To customize your app, you will need to come up with a model of what the data will look like in the database. Here is what the model looks like for a UserInfo: (this can be found in the GroceryHandlerApp.swift file)
struct UserInfo : Codable {
let userName : String
let password : String
}
Here is what the model looks like for an order:
struct Order : Codable {
let userName : String
let receipt : [Item]
var paid : Bool
let time : String
}
struct Item : Codable {
let price : Double
let users : [String]
}
The Codable makes it so that instances of these structs can be converted to JSON data objects, which is needed to post them to the database. To be explicitly clear, an Order is posted to the collection orders and a UserInfo is posted to the collection userInfo.
Click here and/or look at the MLTextRecognizer.swift file
Using the Document API for Astra means that data is stored in keyspaces. A keyspace can have multiple collections and a collection stores a list of JSON documents.
The picture below gives a graphical view of how data is stored for this sample app. There is only one keyspace and two collections. A JSON doc can be identified by its orange color.
Check out the Astra DB documentation for more information.
The following will help you become comfortable with HTTP requests, URLs, and JSON.
First off, what does HTTP stand for? From CodeAcademy:
"HTTP stands for Hypertext Transfer Protocol and is used to structure requests and responses over the internet.
HTTP requires data to be transferred from one point to another over the network.”
The HTTP request methods that we will be using in this app are GET, POST, PATCH, and DELETE. GET and DELETE are self explanatory. POST is used to persist data to the database and PATCH is used to update data that is already in the database.
To connect to Datastax astra using the Document API, a specific URL, which includes the database specific properties is needed as below:
https://ASTRA_DB_ID-ASTRA_DB_REGION.apps.astra.datastax.com/api/rest/v2/namespaces/ASTRA_DB_KEYSPACENAME/collections/{collection-id}
The api/rest in the URL is what triggers the Document API. There are other methods of access (including CQL, GraphQL, etc.)
This URL might be followed by something called a Query String. The URL and query string are separated by a question mark -> ?
We will see an example of a query string in the examples below.
However, an HTTP request is more than just a URL. It is a URL, headers, a request method (GET, POST, DELETE, PATCH), and depending on the request method a JSON body.
We can perform HTTP requests in the terminal app using a command line tool called cURL, or client URL.
Before using cURL, run these commands in your Terminal window so that you won't have to copy these values more than once:
export ASTRA_DB_ID=REPLACE_ME
export ASTRA_DB_REGION=REPLACE_ME
export ASTRA_DB_TOKEN=REPLACE_ME
export ASTRA_DB_KEYSPACENAME=REPLACE_ME
Make sure the variables were properly exported by running this command:
printenv | grep ASTRA_DB
Or if you want to do it one variable at a time:
echo $ASTRA_DB_REGION
Now let's go through a few examples.
Notice that the collection “orders” has to have been created in your database.
Run this command in your Terminal:
curl -X 'GET' "https://$ASTRA_DB_ID-$ASTRA_DB_REGION.apps.astra.datastax.com/api/rest/v2/namespaces/$ASTRA_DB_KEYSPACENAME/collections/orders"'?where=\{"userName":\{"$eq":"Andy1"\}\}&page-size=20' -H 'accept: application/json' -H "X-Cassandra-Token: $ASTRA_DB_TOKEN"
As we can see, the HTTP method (GET), URL, query string, and headers (-H) are all there.
The query string is :
where=\{"userName":\{"$eq":"Andy1"\}\}&page-size=20
where and page-size are parameters. The & is a query string separator to pass multiple parameters in the URL.
The page-size=20 is to specify that we want to GET a maximum of 20 orders (page-size cannot be greater than 20).
The header accept: application/json is to specify that we are expecting the format of the data to be retrieved to be in JSON format. If you are not familiar with JSON, a quick online research will be enough, it is simply a way of storing information.
The header X-Cassandra-Token: $ASTRA_DB_TOKEN is to specify the token generated by the person who created the database for security reasons. Only people with the token will be able to interact with the database.
Notice that the collection userInfo has to have been created in your database.
Run this command in your Terminal:
curl -X 'GET' "https://$ASTRA_DB_ID-$ASTRA_DB_REGION.apps.astra.datastax.com/api/rest/v2/namespaces/$ASTRA_DB_KEYSPACENAME/collections/userInfo"'?where=\{"userName":\{"$eq":"Andy1"\}\}' -H 'accept: application/json' -H "X-Cassandra-Token: $ASTRA_DB_TOKEN"
Notice there are only 2 differences between this cURL command and the one from the first example: There is no page-size (since there is a max of 1 user info per person no need to specify how many docs we want to retrieve) and the collection-id is changed from orders to userInfo.
The JSON that is returned is of the following form:
{"data":{"ec47a067-d0a7-4f3b-8606-966aa39ce8e9":{"password":"itsdrewnow","userName":"Andy1"}}}
3. How to POST a new user info with username testUsername and password testPassword in the userInfo collection:
Run this command in your Terminal:
curl -X 'POST' "https://$ASTRA_DB_ID-$ASTRA_DB_REGION.apps.astra.datastax.com/api/rest/v2/namespaces/$ASTRA_DB_KEYSPACENAME/collections/userInfo" -H 'accept: application/json' -H "X-Cassandra-Token: $ASTRA_DB_TOKEN" -H 'Content-Type: application/json' --data '
{
"userName":"testUsername",
"password":"testPassword"
}'
The main difference between this command and the ones in the other examples is the HTTP method, which is POST.
There is also an additional header Content-Type: application/json which is to specify that the data to be persisted to the database is in JSON format.
The data itself is passed after the --data
This cURL command can be used to POST an order as well. All that has to be changed is the collection-id from userInfo to orders as well as the data itself.
By now you should be able to perform HTTP requests and interact with your Astra Database from the command line. Doing this from a Swift app is very similar so understanding the cURL commands is going to be extremely helpful.
The structs/classes needed to perform HTTP requests from a Swift app are JSONEncoder, URLRequest, and URLSession.
The JSONEncoder class makes it easy to convert a struct into a JSON type which can then be posted to a collection of the Astra DB. The URLRequest struct encompasses the information regarding the HTTP request. This entails the URL, the HTTP method, and the HTTP headers. The URLSession class handles actually performing the request to the server.
Check out the Astra DB documentation for more information and lots more examples of using cURL to connect to your database using the Document API.
Congratulations, you made it to the end!
See you next time!
DataStax Developers