Build a price tracker for Bitcoin, Ethereum, & more in Swift (Part 2)
This is part 2 of my Ethereum/Bitcoin price tracking app tutorial. You can read part 1 here if you’d like to get caught up. I wrote this tutorial so you can follow it even if you didn’t read part 1, however.
I wrote the first part of this tutorial on October 10th 2017. Since then the Bitcoin price has gone from $4,805 to $16,764 (as of December 21st, 2017) over a 348% increase!
I would say now is a great time to add Bitcoin support to our price tracker. Now that we’ll be adding a second currency it’d be nice to make our solution scalable to many currencies. When we’re done the app will look like this:
Lets jump right in. Download the starter project here then open “CryptoTracker.xcodeproj”. You’ll need Xcode 9 to open it up since the project is built in Swift 4.
Open up “Assets.xcassets” from the projects pane on the left-hand side, then expand the “Currencies” folder. You’ll see the project already has logos for the six currencies we’ll be supporting in this tutorial.
Now, select “Main.storyboard” from the projects pane and you’ll see a UITableViewController
with an embedded UITableViewCell
. Expand from the left-hand panel “Crypto Table View Controller Scene → Crypto Table View Controller → Table View →CryptoTableViewCell →Content View” then select CryptoTableViewCell
. From there you should tap the “Connections Inspector” which is the last button on the top right in the panel on the right-hand side. (Looks like an → symbol inside a circle):
The Table View is of type CryptoTableViewController
and the cell is of type CryptoTableViewCell
. These correspond to those two files within the projects pane. The cell has 3 subviews. The first is a UIImageView
which will show the icon for a currency, named currencyImageView
. The bold text that says “Cryptocurrency Name” is a UILabel
named currencyName
. The text underneath that that says “$0.00” will show our price in USD, named currencyPrice
.
Open up “CryptoTableViewCell.swift” and you’ll see it has @IBOutlet
’s for the currencyImageView
, currencyName
, and currencyPrice
. This is connected to the UIImageView
, and two UILabel
s we just looked at in “Main.storyboard”.
A data model for Cryptocurrencies
Now its time to start adding support for some Cryptocurrencies. Create a new file (⌘+n), select “Swift File” as the type and click next in the bottom right. In the “Save as” prompt name the file “CurrencyType” and click “Create” in the bottom right of the prompt.
Add the following enum on line 10 of the new file:
CurrencyType
is an enum
which we will use to represent six different Cryptocurrencies, Bitcoin (BTC), Ethereum (ETH), Litecoin (LTC), Ripple (XRP), Monero (XMR), and NEO (NEO). Its a String
type enum
so if you try to read its rawValue
it will return
the String
its associated with. For instance, CurrencyType.btc.rawValue
returns the String
“BTC”.
In part 1 we used the CryptoCompare API to get the price of Ethereum by performing a GET request at the following URL:
let apiURL = URL(string: “https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD")
Our enum
cases have a rawValue
associated with them mapping to each of the six Cryptocurrencies. We can use that to modify this URL
to get the value for each of them. To do so hit enter on line 17 then add the following on line 18 of the file:
By leveraging the rawValue
we can now get the respective URL
for any Cryptocurrency we add to the CurrencyType
enum
. For exampleCurrencyType.btc.apiURL
returns the URL
:
https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD
Going to this URL
will get back the BTC price in USD.
Now to display the name of these currencies within the cell, we create a variable called name
. Add the following to line 23 underneath the apiURL
var
:
This will allow us to get the name back for a CurrencyType
as a String
. For instance, CurrencyType.xrp.name
will return
the String
“Ripple”.
Next, we’ll want to be able to get the image for each of the currencies icons. To use the UIImage
API we must first import UIKit
, to do that replace import Foundation
on line 9 with the following:
import UIKit
Now that we can use UIImage
's API lets create a var
called image
by adding this on line 40:
#imageLiteral
extracts the image with the specified resourceName
seen above from the “Assets.xcassets” catalog we looked at earlier. It returns a guaranteed non-nil image since the name maps with that image’s name within the Asset catalog. If you try deleting #imageLiteral(resourceName: "Bitcoin")
above and start typing the word “Bitcoin” you should be able to select the “Bitcoin” #imageLiteral
from autocomplete:
And now we have an icon and a name for each of our Cryptocurrencies. Next step is to actually be able to get the current value for each of these. For this, we need to make a GET request to the CryptoCompare API.
Recall earlier that we already have a var
called apiURL
which maps to the correct URL
for each of our currencies prices. We can use that URL
to make a request to the API and store the price as a String
. Add the following func
on line 58 that we’ll use for this request:
Our requestValue
function has a completion block which will return
an NSNumber?
. We are using a completion block here because a URLRequest
is a background operation, meaning that the UI work of rendering the app occurs while the function is operating. Once this function completes its work in the background we can use the value
var
it gets back on the main thread through a completion handler.
The first step in the function is to safely unwrap apiURL
, recall earlier that the type of apiURL
was URL?
so to use it for requests it must be unwrapped first. Our guard statement does that and gives us an apiURL
instance of type URL
.
Now lets start the request. Hit enter on line 64 then add this on line 65:
First, we perform a dataTask
request that takes in the URL
, using Apple’s URLSession API. It returns Data
from the call, a response(which returns information about the call), and an optional Error
in the event of failure.
We do request.resume()
at the end which starts the call. At this point, we are not doing anything with the information returned. Let’s replace the TODO
with the following:
This confirms that the data we got back isn’t nil
and that there weren’t any errors. In the else
case we send back nil
since no data was found and try to print the Error
, for safety we unwrap it using ?
to get back a description of the Error
and if no description is found we print an empty String
. The Error
should never be nil
here but using optionals is a good habit to get into for writing safe code.
Now we have the Data
safely unwrapped and made sure there aren’t any errors, now its time to fetch the USD value from the JSON
response we got back from the server. To do so add the following on line 71 underneath the guard
block we just added:
To read the JSON
Data
, we use the JSONSerialization class from Apple, which takes JSON
Data
and converts it into a Dictionary
. Since this method throws an Error
in the case of failure, we need to add do
at the beginning and catch
to handle errors.
Once we have the Dictionary
we can start reading values, inside the JSON
Dictionary
is the key “USD” which contains the US price of the currency as a number so we cast that value to NSNumber
.
If for whatever reason we can’t get back the value we pass nil
to the completion block again and return
. If we do get the value back safely though we pass it to the completion block.
Setting up our cell to display Cryptocurrencies
Alright, now that the CurrencyType
stores a name, image, and value for each of our currencies we can now use it in our CryptoTableViewCell
, open up “CryptoTableViewCell.swift” from the projects pane on the left-hand side.
To display the value we get back from the requestValue
function we need a way to format the value as US currency. Let’s write a private extension
for NSNumber
to do this. Add the following on line 18 of the file underneath the CryptoTableViewCell
:
By making this an extension of NSNumber
we can access the var
from an NSNumber
instance.
The formattedCurrencyString
uses NumberFormatter, a class from Apple that takes numbers and represents them in various text formats. By setting the locale
to en_us
and the numberStyle
to .currency
we can use the NumberFormatter
to represent the value in US dollars.
We then use the NumberFormatter
’s string(from:)
method by passing in self
which is our instance of NSNumber
. This returns an optional String?
instance formatting the number as US dollars.
Now let’s take care of formatting our cell with the CurrencyType
data model. To do so we need to set the currencyImageView
, currencyName
, and currencyPrice
with the values from a CurrencyType
instance. Add the following on line 16 right under those 3 @IBOutlet
s:
formatCell
is a function that takes in a CurrencyType
instance, sets the text for currencyName
equal to currencyType.name
and the image for currencyImageView
equal to currencyType.image
. It also performs the requestValue
function to fetch the value of the currency.
requestValue
has a completion block which returns with an instance of NSNumber?
which either holds the value of the currency or nil
. This happens in the background so we use Apple’s Dispatch API to set the text for currencyPrice on the main thread using DispatchQueue.main.async
.
Since value
is optional we can unwrap it as value?
and then get the US currency String
using the formattedCurrencyString
variable we wrote. Since this operation could return
nil
we handle the failure case using the ??
operator. ??
means if value?.formattedCurrencyString
is nil
it will return
whats on the right-hand side instead.
In this case, we return
the String
“Failed to get price”. It’s a good idea when working with API’s to assume errors can happen, and to handle these without force unwrapping using !
which would crash the app if one of our currency API calls failed.
Populate the table view with our Cryptocurrencies
Now that the cell is able to display a Cryptocurrency we need to hook up the CryptoTableViewController
to pass in some CurrencyType
instances to display. Open up “CryptoTableViewController.swift” then add the following on line 12:
The currencies
Array
stores an instance of each of the six Cryptocurrencies we support. reuseIdentifier
is a String
describing CryptoTableViewCell.self
which will return
the name of the class “CryptoTableViewCell”. The reuseIdentifier
will be used when we dequeue CryptoTableViewCell
instances later on.
Next, let’s implement numberOfRowsInSection
which tells the UITableView
how many rows we will display. This should be equal to the number of currencies. To do so hit enter under the reuseIdentifier
then add the following on line 15:
currencies.count
returns the number of items in the currencies
array, which is six.
Now let’s populate the cells with our currencies. To do so we will be using the cellForRowAtIndexPath
method. Hit enter on line 18 then add the following on line 19:
cellForRowAtIndexPath
expects back an instance of UITableViewCell
which is ready to be displayed. We use the UITableView
function dequeueReusableCell
which uses the reuseIdentifier
and the current IndexPath
to get an instance of our CryptoTableViewCell
.
Since dequeueReusableCell
returns CryptoTableViewCell
as its super class UITableViewCell
we have to then cast our tableViewCell
instance as a CryptoTableViewCell
. From there we can call the formatCell
function we wrote to actually populate the cell with the CurrencyType
for the current row, which we get back by using currencies[indexPath.row]
.
Finally, we return
the tableViewCell
instance so the UITableView
can prepare to display it.
Giving it a test drive
Now that everything is set up let’s try running the app (⌘+R). You should see six cells populate our table view like this:
Finishing Touches
The app looks good but can be even better with a few finishing touches. By adding a header and footer to our table view we can provide a bit more info to the user.
Let’s start by adding a simple header. Hit enter on line 28 then add the following on line 29:
titleForHeaderInSection
allows you to add a String
that will appear above all the rows in our UITableView
. Since we only have a single section we don’t need to worry about the section
Int
that this function provides us.
Something else that would be nice to provide users is a timestamp for when the request happened. We can put this in the footer of the table. Hit enter on line 32 then add the following to line 33 to add that:
DateFormatter
is an Apple API which allows you to take a Date
instance and display it in a variety of different formats. To create a simple human-readable Date
we set the dateStyle
to .long
and the timeStyle
to .medium
.
We then get an instance of Date()
which returns the current Date
. Like NumberFormatter
we use the string(from:)
method to extract a String
from the formatter. Finally, we add the String
“Updated on ” to appear before the formatted date String
.
Now run the app (⌘+R) again and it should look like this:
Conclusion
There you have it! If you had any trouble getting the app working you can download the final version here. It has some added comments and documentation as well.
Thanks so much for taking the time to read this. If this tutorial helped you I would appreciate you holding down that 👏 button below! If you’re a Github user a ⭐️ on the CryptoTracker Github page would be great too!