Last time I wrote a few tips for writing APIs; this time I'd like to write a few for consuming them.
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;enums for your endpoints and/or staging(s); it's much easier to reason with: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
}
// ...
}
}startDate, don't have other objects use startingDate;Bools use the is/has nomenclature, use it everywhere. For example, don't have some flags isAvailable and hasExpirationDate, but others available and expirationDatePresent;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 :)
*/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: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 */ }