---
title: "Tips for consuming APIs"
slug: tips-for-consuming-apis
section: tech
date: 2017-09-12T11:45:00.000Z
canonical: https://roland.leth.ro/blog/tech/tips-for-consuming-apis
---

[Last time](/tech/blog/tips-for-writing-apis) I wrote a few tips for writing APIs; this time I'd like to write a few for consuming them.

1. The most important one I can give is to always have a layer between your app and the network; this will ensure you always have a single source of truth:
    * create models, no matter how few fields an object has, instead of reading from dictionaries/arrays all over the app. This will make adding/changing/removing fields a breeze if required, will ensure everything is type safe, and that you don't have to worry about typos every time you use a field; autocomplete is your friend;
    * have your own "manager" for any third party services. For example, don't call Google Analytics directly, but your own `Analytics` class, which in turn calls that. This way, if you ever need to switch services, there's just one place to do the changes;
    * create an API object, preferably with `enums` for your endpoints and/or staging(s); it's much easier to reason with:

```swift
struct API {

  static let baseURL = URL(string: "https://rolandleth.com")!

  enum Endpoint: String {
    case sessions
    case followers

    var url: URL {
      return API.baseURL.appendingPathComponent(rawValue)
    }
  }

  enum Environment: Int {
    case production
    case staging1
    case staging2

    static var current: Environment {
      let raw = UserDefaults.current.integer(forKey: "currentEnvironment")
      return Environment(rawValue: raw) ?? .production
    }
  }

  func userInfo(completion: @escaping ([String: Any]) -> Void) {
    var request = URLRequest(url: Endpoint.sessions.url)
    request.httpMethod = "GET"
    // ...
  }

  func login(email: String, password: String, completion: @escaping ([String: Any]) -> Void) {
    var request = URLRequest(url: Endpoint.sessions.url)
    request.httpMethod = "PUT"
    // ...
  }

  func followers(completion: @escaping (Int) -> Void) {
    // For stagings we want to easily test this feature,
    // so we just return a random number up to 200.
    guard Environment.current == .production else {
      completion(arc4random() % 200)
      return
    }
    // ...
  }
}
```

2. Be consistent:
    * if one object has a property `startDate`, don't have other objects use `startingDate`;
    * if some `Bools` use the `is`/`has` nomenclature, use it everywhere. For example, don't have some flags `isAvailable` and `hasExpirationDate`, but others `available` and `expirationDatePresent`;
    * if one object has a child object/wraps some keys in an object for better structure, do that everywhere else it applies as well:

```swift
struct Address {

  let city: String
  let street: String

}

struct User {

  let name: String
  let address: Address

}

struct Event {

  let name: String
  let address: Address

}

// Don't:
struct Event {

  let name: String
  let city: String
  let street: String

}
/*
Even if the json looks like this:
{
  "name": "Geneva International Motor Show",
  "city": "Geneva",
  "street": "Route François-Peyrot 30"
}
Just wrap the keys into the proper object yourself,
or refer your backend to my previous post about writing APIs :)
*/
```

3. Make use of `enums`. If a field can have a finite set of values, `enums` can make your life a bit easier, by providing type safety and autocompletion:

```swift
struct User {

  let name: String
  let address: Adress
  let role: Role

}

struct Role: String {

  case guest
  case sales
  case financial
  case administrator = "admin"

}

// [...]
if user.role == .guest { /* do something */ }
else if user.role == .administrator { /* do something else */ }
// vs having role be a String
if user.role == "guest" { /* do something */ }
else if user.role == "admin" { /* do something else */ }
// or worse, if the API was designed in a weird way
if user.role == "g" { /* do something */ }
else if user.role == "a" { /* do something else */ }
```