Introducing Dyno đŚ
Update: the second article in this series is now available!
Dyno is a new Swift library under construction to:
Provide a functional, Reactive, safe and easy-to-use interface to the Amazon AWS DynamoDB database.
Donât worry if that doesnât make much sense â or if it makes sense but doesnât sound useful for you â because as well as practical steps Iâll also be discussing ideas and techniques which will hopefully be adaptable elsewhere.
Along the way, it will:
- integrate the official AWS Python boto3 libraries with Swift
- add robust, async handling
- output data via an reactive Observable stream
- provide RxSwift extensions to both macOS and iOS which integrate results into your apps in a simple, declarative way.
I will also look at adding some additional features:
- offline data storage and syncing
- extension to some of DynamoDBâs advanced capabilities.
The side aim is to show how the powerful functional tools that Iâve been looking at in prior articles can give great practical benefit.
Iâm also planning to develop this library as part of writing these articles. The Dyno source code is available on github (Iâll be continuing to adapt it as I go, branching the code to match these articles: this article is under the introduction branch) So if you are interested in even just some of these topics, follow on â and comment, PR and star as appropriate đ!
Linux / non-Apple / server-side folks, I havenât forgotten you: a lot of whatâs in here will be hopefully of relevance, even if some of the details will change.
This Article
In this article weâll do some basic introductions to the concepts; then get set up with AWS DynamoDB. This will be a lot of initial configuration, but then weâre done: and we will see how to use PythonKit to get access to our new database from Swift.
I canât wait to get started! đŚ
Meet the Characters
DynamoDB
DynamoDB is a highly available and resilient cloud-hosted NoSQL database from Amazon Web Services. It scales enormously, but is also free for small use cases, so itâs perfect to get started with.
Boto3
Boto3 is the official Amazon SDK for communicating with DynamoDB. It uses Python: other languages (like Java and Go) are available, but no Swift. What weâll do in Dyno is make use of the semi-official Python interoperability developed by the Google Tensorflow team (which itself relies on Swift 5 features, so these articles are Swift 5 only. Weâll discuss how to deal with that shortly).
I should also point out that there is AWS support for Swift and DynamoDB in the shape of the AppSync platform, which is a sophisticated interaction library and will be a great choice for some apps. However: it only exists for iOS; itâs very heavyweight and complex; and it doesnât use reactive programming techniques â so I am looking for something with more compatibility, lighter weight, and more âfunctionalâ.
Observables and Reactive programming
Reactive programming describes streams of data (âObservablesâ : which might be button clicks in a UI, data packets on a network â or results retrieved from a cloud data store) in a functional way, allowing us to manipulate them using operators . This allows us to use a declarative style to develop whole applications. There are a couple of reactive frameworks available for Swift; Iâm using RxSwift because Iâm more familiar with it.
By returning data from DynamoDB as a stream of Observables, we can then attach the results to a data table. But more than that: we can show spinners when the data is loading; failure messages in case of error; and many other neat features simply by observing the stream. The code does take a bit of getting used to, but itâs well worth it:
âŚthese 4 lines of code: check for the application either refreshing its data store or storing new data; show a spinner while data is being transferred; display any errors; and display the results of a refresh in a table view. Thatâs a lot of power for not much code!
Reactive Extensions
RxSwift adds a number of extensions to common UI objects, to allow them to interact with Observable streams. Dyno will add some more, including to make them work with common macOS UI elements and providing some handy operators (eg â->
to bind an Observable stream and ..>
to dispose)
Getting Started
Setting up AWS
To get started, youâll need to set up an account with AWS if you have not done so already. This is a bit of a convoluted process, taking you through a number of security options. As part of the setup, youâll be given an access key ID and a secret access key : keep note of these (you canât re-retrieve your secret access key) as weâll need them shortly. You might also need to set up a region, depending on where in the world youâre located. Finally youâll need to provide a credit card so they can charge you: but donât worry, we shouldnât be hitting any of the capacity charges and should remain in the free tier!
Setting up DynamoDB
Once thatâs done, and you have access to the AWS Management Console, search for DynamoDB and youâll get to a dashboard:
Click Create Table and type in a table name. Letâs call this Dinosaurs and put in a primary key called id. Everything else can stay the same. Click Create to actually create the table.
Note: itâs best practice to not use names as primary keys in databases â stick to meaningless identifiers instead
Youâll switch back to the dashboard and the table will be created â this can take a little while, but once thatâs done youâll jump to the Tables view with the Dinosaur table selected. I would recommend making a change on this view, to avoid billing costs:
- Go to the Capacity tab and change the Read/Write Capacity Units to 1
Thatâs all weâll need AWS for for now, letâs get cracking with some code!
Python and Boto3
To actually talk to the new database we just set up, weâre going to use the boto3 library from Amazon. There are some instructions here but basically you need to go to a Terminal and type:
> sudo easy_install pip
> pip install boto3 --user
(You might not need the first command if pip is already on your system; and in the second you might also need a flag --ignore-installed six
to ignore previously-installed packages)
Next, set up a credentials file. Create a directory ~/.aws
and create a file called credentials
in there which looks like this:
[default]
aws_access_key_id=[your access key id from earlier]
aws_secret_access_key=[your secret access key from earlier]
I also had to set up a config
file like this for the Region my table was installed in:
[default]
region=us-east-2
Once all that setup is done, letâs try this out.
> python
>>> import boto3
>>> dynamodb = boto3.resource("dynamodb")
>>> table = dynamodb.Table("Dinosaurs")
>>> table.scan()
{u'Count': 0, u'Items': [], u'ScannedCount': 0, 'ResponseMetadata': {'RetryAttempts': 0, 'HTTPStatusCode': 200, 'RequestId'...}}
⌠you should receive a message with an HTTPStatusCode
of 200 indicating that the request was OK, even though there were no rows returned. If this doesnât happen â check carefully, particularly your credentials settings.
Finally, some Swift (part 1)
The final thing weâll tackle in this article is getting this working in Swift. For that, we are going to leverage a library called PythonKit, which is a wrapper built by Pedro Vieto leveraging the Python interoperability library built by the Tensorflow team at Google. That team have introduced some features to the core Swift language to make working with Python much simpler â those features have made their way into Swift 5⌠and that means weâre going to need do some more setup and get hold of Swift 5 !
The easiest way to do that (assuming youâre reading this before the official release of Swift 5 in Spring 2019) is to download the latest Xcode Beta from the Apple developer site. Once thatâs downloaded and installed (itâs not quick, Iâm afraid) youâll need to switch the command-line Swift tools to use your new download:
> sudo xcode-select -s /Applications/Xcode-beta.app/Contents/Developer
Now, weâre going to use the Swift Package Manager to do the hard work of creating an XCode project with the two major dependencies weâll need: PythonKit and RxSwift.
So, create a new directory called Dyno ; go into that directory and type the below to set up a new SPM directory structure:
> swift package init --type executable
Weâre using an executable structure for now, so we can get started (hurray!)
Find the Package.swift
file that was created, and edit it so that it looks somewhat like this:
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Dyno",
dependencies: [
.package(url: "https://github.com/pvieito/PythonKit.git", .branch("master")),
.package(url: "https://github.com/ReactiveX/RxSwift.git", "4.0.0" ..< "5.0.0")
],
targets: [
.target(
name: "Dyno",
dependencies: ["PythonKit","RxSwift"]),
.testTarget(
name: "DynoTests",
dependencies: ["Dyno"]),
]
)
Then, fetch the dependencies and create/open an Xcode project using the below commands:
> swift package fetch
> swift package generate-xcodeproj
> open Dyno.xcodeproj
Finally, some Swift (part 2, with actual Swift this time)
In main.swift
add the following:
import PythonKit
let boto3 = Python.import("boto3")
let dynamodb = boto3.resource("dynamodb")
let table = dynamodb.Table("Dinosaurs")
print( table.scan() )
And amazingly that will work in the same way as the Python code previously, pulling the (admittedly non-existent) data from the table and displaying the connection information.
You might also want to look at how similar the code looks to the original Python version, and indeed experiment with the boto3
library to do some more operations on that table â wouldnât adding some dinosaurs be nice?
table.put_item(
Item:[
"id" : "1",
"name" : "Emojisaurus",
"colour" : "blue"
])
print( table.scan() )
đŚ
For Next Time
Phew! Thatâs a lot of setup and configuration out of the way, though we did get something interesting happening in the end. Next time weâre going to actually build more of a library with APIs to hide that Pythonnessss đ and provide a Swift interface! đ
UPDATE: the next article is here: https://medium.com/@JLHLonline/dyno-aws-swifter-1ecaa1249924