Quids Beta 2

We've just released Quids Beta 2 🎉

Read all about the new features here and register for beta here!

Crypto Icon API Service

Originally posted on Medium.

We’ve just released (and open sourced!) the web service we built for serving the cryptocurrency icons maintained by the Cryptocurrency Icons project.

The service was originally built for use in Quids for Mac to avoid bundling every icon with the client and having to make a new release everytime we supported a new currency. Instead, we use this service to dynamically accept the SVG source and render a PNG, using any of the included icon styles provided and at any size we need.

We have also released the Swift library we use in Quids to build the URLs.

The service is completely free to use but is provided “as is”. If you wish to guarantee uptime (like we do for Quids), I recommend deploying your own instance. The Github README has a Heroku button to make deploying your own instance of the service really simple.


Bitrise + Bugsnag

Since Buddy Build was bought by Apple (congrats!) I've been keeping an eye out for a possible alternative. Not because Buddy Build has become rubbish since the acquisition (it's still awesome) but because if it is shut down and absorbed into Apple (e.g. TestFlight) then I would have to spend a ton of time migrating client projects somewhere else.

So for new projects I have been using Bitrise. It is a lot more configurable than Buddy Build and definitely took a bit longer to get setup, but once I got my head around it, it became super powerful.

One out of the box "integration" that was missing was uploading dSYM files to Bugsnag. I've written a previous post about how I got this working with Buddy Build. Getting it to work with Bitrise is very similar:

Firstly you need to head over to the Workflow Editor. After your Xcode Archive & Export step, insert a Script step.

Under the config menu, set the working directory to $BITRISE_XCARCHIVE_PATH.

Then set the script content to:

#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

ls **/*.dSYM/Contents/Resources/DWARF/* | while read line; do
    echo "Uploading $line"
    echo "Running: curl https://upload.bugsnag.com/ -F 'dsym=@$line'"
    curl https://upload.bugsnag.com/ -F "dsym=@$line"

Quids Beta


We've just sent out our first beta build to our Product Hunt subscribers! Full blog post here.

If you want to try Quids just sign up here 😺

Observe Screen Locking in macOS

In Quids, similar to 1Password, we lock the app when your Mac locks.

There are two notifications sent via the DistributedNotificationCenter. One when the screen is locked and the other when it is unlocked. I couldn't find any predefined variables so created this Notification.Name extension:

internal extension Notification.Name
    static let screenIsLocked = Notification.Name("com.apple.screenIsLocked")
    static let screenIsUnlocked = Notification.Name("com.apple.screenIsUnlocked")

Then all that is left to do is register an object as an observer:

DistributedNotificationCenter.default().addObserver(self, selector: #selector(...), name: Notification.Name.screenIsLocked, object: nil)

Send Money with the Coinbase Swift SDK

Photo by Thought Catalog / Unsplash

I recently added the ability to send money using the Coinbase Swift SDK that we're building alongside Quids.

When going through the OAuth flow you need to add the CoinbaseAPIClient.Scope.createTransactions to your list of scopes:

let authScopes: [CoinbaseAPIClient.Scope] = [
    .createTransactions(sendLimit: 500.0, currencyCode: "USD", period: .day)

Then once you have the required permission you will need to build a SendMoney request and pass that to your CoinbaseAPIClient:

let request = CoinbaseAPIClient.SendMoney(to: ethAddress, amount: 2.5, currencyCode: "ETH")

coinbase.send(money: request, from: accountID, twoFactorCode: nil) { (transaction, errors) in
    // ...

You'll notice this function can also take a 2FA code. If Coinbase returns a CoinbaseAPIClient.APIError.twoFactorRequired error, you should ask your user for a 2FA code and then re-call the send money function with the 2FA code.

Here is an example of how we're doing this in Quids:

Coinbase Swift SDK

Over the weekend I released the Coinbase Swift SDK I've been building alongside Quids. It is still under heavy development so expect breaking changes!



github "reddavis/Coinbase"



The framework gives you the flexibility of how you store the oAuth credentials. You will need to implement a class that conforms to the CoinbaseAPIClientAuthStore protocol. var isAuthenticated, var hasExpired and func delete() all have default implementations, so you will only need to implement var auth.

The library will handle the refreshing of tokens.

public protocol CoinbaseAPIClientAuthStore: class
    var auth: CoinbaseAPIClient.Auth? { get set }
    var isAuthenticated: Bool { get }
    var hasExpired: Bool { get }

    func delete()

public extension CoinbaseAPIClientAuthStore
    public var isAuthenticated: Bool {
        return self.auth != nil

    public var hasExpired: Bool {
        guard let unwrappedAuth = self.auth else
            return true

        return unwrappedAuth.expiresAt < Date()

    public func delete()
        self.auth = nil


Firstly you need to initialize the auth flow:

let scopes: [CoinbaseAPIClient.Scope] = [

let url = self.coinbaseAPIClient.authorizeURL(scopes: scopes)

Then you need to get the oAuth token:

let redirectURL = URL(string: "quids://coinbase/auth")!

self.coinbaseAPIClient.authenticate(code: code, redirectURL: redirectURL) { (success, error) in
    self.authCompletionHandler?(success, error)


Only a few requests are currently supported, more will be added as we add features to Quids.

public func fetchAccounts(_ completionHandler: @escaping (_ accounts: [Account]?, _ errors: [Error]?) -> Void)
public func fetchTransactions(accountID: String, completionHandler: @escaping (_ transactions: [Transaction]?, _ errors: [Error]?) -> Void)
public func fetchCurrentUser(_ completionHandler: @escaping (_ user: User?, _ errors: [Error]?) -> Void)
public func updateCurrentUser(edits: UserEdits, completionHandler: @escaping (_ user: User?, _ errors: [Error]?) -> Void)
public func createAddress(accountID: String, completionHandler: @escaping (_ address: Address?, _ errors: [Error]?) -> Void)
public func fetchExchangeRates(baseCurrencyCode: String, completionHandler: @escaping (_ exchangeRate: ExchangeRate?, _ errors: [Error]?) -> Void)