Skip to content

Latest commit

 

History

History
306 lines (233 loc) · 6.54 KB

STYLE_GUIDE.md

File metadata and controls

306 lines (233 loc) · 6.54 KB

Braintree iOS Style Guide

While we use SwiftLint in the SDK to adhere to a general style, there are some guidelines that are not able to be captured by our linter rules. Those guidelines are defined in this document.

These rules are in addition to, and should not contradict, the official Swift API Design Guidelines.

Documentation Comments

Write a documentation comment for all declarations (ex: functions, methods, variables, constants, enumerations, structs, classes, and protocols).

Format documentation comments using Swift's dialect of Markdown. Prefer styling generated by Xcode's CMD+OPT+/.

Code Organization

Use // MARK: - to separate the contents of type definitions and extensions into the sections below, in order:

  • // MARK: - Public Properties for public and open properties.
  • // MARK: - Internal Properties for internal, fileprivate, and private properties.
  • // MARK: - Public Methods for public and open methods.
  • // MARK: - Internal Methods for internal, fileprivate, and private methods.
  • // MARK: - <PROTOCOL> for protocol conformance. Each protocol conformance gets a separate MARK.
  • // MARK: - <EXTENSION> for additional extensions.

Use a newline before and after a MARK.

Protocol Conformance

When conforming to a protocol, add a separate extension for the protocol methods.

Preferred:

class MyViewController: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDataSource

extension MyViewController: UITableViewDataSource {
  // table view data source methods
}

// MARK: - UIScrollViewDelegate

extension MyViewController: UIScrollViewDelegate {
  // scroll view delegate methods
}

Not Preferred:

class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
  // all methods
}

Nested structs

When defining struct model types that depend on other custom struct definitions, put both definitions in the same file.

Special cases can be made for massive structs (over 100 lines), which is unlikely to occur.

Preferred:

// Musician.swift //
struct Musician: Codable {
    let name: String
    let recordLabel: RecordLabel
}

struct RecordLabel: Codable {
    let name: String
    let address: Address
}

struct Address: Codable {
    ...
}

Not Preferred:

// Musician.swift file
struct Musician: Codable {
    let name: String
    let recordLabel: RecordLabel
}
// RecordLabel.swift file
struct RecordLabel: Codable {
    let name: String
    let address: Address
}
// Address.swift file
struct Address: Codable {
    ...
}

Spacing

Curly Brackets

Method braces and other braces (if/else/switch/while etc.) always open on the same line as the statement but close on a new line.

Preferred:

if user.isHappy {
  // Do something
} else {
  // Do something else
}

Not Preferred:

if user.isHappy
{
  // Do something
}
else {
  // Do something else
}

Colons

Colons have no space on the left and one space on the right. Exceptions are the ternary operator ? :, empty dictionary [:] and #selector syntax addTarget(_:action:).

Preferred:

class TestDatabase: Database {
  var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}

Not Preferred:

class TestDatabase : Database {
  var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}

Newlines

There should be one blank line between methods and up to one blank line between type declarations to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods.

There should be no blank lines after an opening brace or before a closing brace. An exception is when defining a class/struct/etc per our linter rules.

Preferred:

if isTrue {
    // do something
}

Not Preferred:

if isTrue {

    // do something

}

Parentheses

Long function invocations should line break on each argument. Closing parentheses should appear on a line by themselves.

Preferred:

let user = try await getUser(
  for: userID,
  on: connection
)

Not Preferred:

let user = try await getUser(
  for: userID,
  on: connection)

Types

Syntactic Sugar

Prefer the shortcut versions of type declarations over the full generics syntax.

Preferred:

var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?

Not Preferred:

var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>

Naming

Abbreviations

Acronyms and initialisms that commonly appear as all upper case in American English should be uniformly up- or down-cased according to case conventions.

Preferred:

var orderID
var dataUTF8
var requestHTTP

Not Preferred:

var orderId
var dataUtf8
var requestHttp

Formatting

Session Data Tasks

When launching session data tasks, prefer to resume the session inline vs creating a property for the task. This ensures that it is clear where we are kicking off the session to make the request.

Preferred:

session.dataTask(with: request) { [weak self] data, response, error in
    // Do request stuff
}.resume()

Not Preferred:

let task: URLSessionTask = session.dataTask(with: request) { [weak self] data, response, error in
    // Do request stuff
}

task.resume()

Implicit Return

We prefer to always rely on implicit returns to cleanup our codebase overall.

Preferred:

func sendAString() -> String {
    "my-very-cool-string"
}

var aString: String {
    "my-very-cool-string"
}

Not Preferred:

func sendAString() -> String {
    return "my-very-cool-string"
}

var aString: String {
    return "my-very-cool-string"
}

Use of Self

When not in an initalizer remove the use of self where possible. For initalizers we prefer the implicit self.

Preferred:

var myString: String
var myInt: Int

init(myString: String, myInt: Int) {
    self.myString = myString
    self. myInt = myInt
}

func sendMyStuff() -> [String] {
    [myString]
}

Not Preferred:

var myString: String
var myInt: Int

init(myString: String, myInt: Int) {
    myString = myString
    myInt = myInt
}

func sendMyStuff() -> [String] {
    [self.myString]
}