Swift Style Guide
Table of Contents
Naming
Apple’s API Style Guidelines
Apple’s official Swift naming and API design guidelines hosted on swift.org are considered part of this style guide and are followed as if they were repeated here in their entirety.
Identifiers
Typically, identifiers contain only 7-bit ASCII characters. Unicode identifiers have clear, legitimate meanings in the problem area of the code base (e.g., Greek characters representing mathematical concepts) and are acceptable if the team that owns the code understands them well.
/// Wrong
let 😎 = "😎"
/// Right
let nice = "😎"
Initializers
For clarity, the initializer argument that corresponds directly to the stored property has the same property and name. Use explicit self.
to clearly distinguish between assignments.
/// Wrong
public struct Company {
public let name: String
public let location: String
public init(name: String, location place: String) {
name = name
location = place
}
}
/// Right
public struct Company {
public let name: String
public let location: String
public init(name: String, location: String) {
self.name = name
self.location = location
}
}
Static and Class Properties
Static and class properties that return instances of the declaring type are not suffixed with the name of the type.
/// Wrong
public class UIColor {
public class var blackColor: UIColor {
// ...
}
}
/// Right
public class UIColor {
public class var black: UIColor {
// ...
}
}
Global Constants
Like other variables, the global constant is lowerCamelCase
.
/// Wrong
let VERSION = "1.0.0"
/// Right
let version = "1.0.0"
Swift Style Rules
Access Control
Omit the internal keyword when defining types, properties, or functions with an internal access control level.
/// Wrong
internal class Base {
internal init() {
// ...
}
internal func run() {
// ...
}
}
/// Right
class Base {
init() {
// ...
}
func run() {
// ...
}
}
Attribute Annotations
Place the properties of the function, type, and calculated properties on the line above the declaration.
/// Wrong
@objc func tapAction() {
// ...
}
@discardableResult func run() {
// ...
}
/// Right
@objc
func tapAction() {
// ...
}
@discardableResult
func run() {
// ...
}
Collection Types
Add a trailing comma on the last element of a multi-line array.
/// Wrong
let color: [Color] = [
.black,
.red,
.blue
]
/// Right
let color: [Color] = [
.black,
.red,
.blue,
]
There should be no spaces inside the backets of collection literals.
/// Wrong
let constraints = [ top, right, left, bottom ]
/// Right
let constraints = [top, right, left, bottom]
Types
Don’t include easily deducible types.
/// Wrong
let status: Bool = true
let statusCode: String = 400
/// Right
let status = true
let statusCode = 400
Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.
/// Wrong
struct Command {
let bash: Alias = .init(executableURL: "/bin/bash", dashc: "-c")
func run() {
let command: Arguments = .init("ls")
bash.run(command)
}
}
/// Right
struct Command {
let bash = Alias(executableURL: "/bin/bash", dashc: "-c")
func run() {
let command = Arguments("ls")
bash.run(command)
}
}
Self Keyword
Don’t use self
unless it’s necessary for disambiguation.
/// Wrong
final class Server {
private let statusCode: Int32
private let result: Result
private var serverRequestSuccess: Bool
init(statusCode: Int32, result: Result) {
self.statusCode = statusCode
self.result = result
self.serverRequestSuccess = !result.success.isEmpty
}
}
/// Right
final class Server {
private let statusCode: Int32
private let result: Result
private var serverRequestSuccess: Bool
init(statusCode: Int32, result: Result) {
self.statusCode = statusCode
self.result = result
serverRequestSuccess = !result.success.isEmpty
}
}
Bind to self
when upgrading from a weak reference.
/// Wrong
final class AClass {
func run(completion: () -> Void) {
Server.run() { [weak self] result in
guard let strongSelf = self else { return }
// ...
completion()
}
}
}
/// Right
final class AClass {
func run(completion: () -> Void) {
Server.run() { [weak self] result in
guard let self else { return }
// ...
completion()
}
}
}
Space
Colons should always be followed by a space. but not preceded by a space.
/// Wrong
let token : Token = "#sidi3wjdia2sdpwd1"
/// Right
let token: Token = "#sidi3wjdia2sdpwd1"
Place a space on either side of a return arrow for readability.
/// Wrong
func request()->Result {
// ...
}
/// Right
func request() -> Result {
// ...
}
Parentheses
Omit unnecessary parentheses.
/// Wrong
if (count > 0) { // ... }
let event = allEvents.map() { // ... }
/// Right
if count > 0 { // ... }
let event = allEvents.map { // ... }
Enum Type
Omit enum associated values from case statements when all arguments are unlabeled.
/// Wrong
if case .response(_) = Response { // ... }
switch result {
case .success(_, _):
// ...
}
/// Right
if case .response = Response { // ... }
switch result {
case .success:
// ...
}
When destructuring an enum case or a tuple, place the let
keyword inline, adjacent to each individual property assignment.
/// Wrong
switch result {
case let .success(value):
// ...
case let .error(statusCode, description):
// ...
}
/// Right
switch result {
case .success(let value):
// ...
case .error(let statusCode, let description):
// ...
}
Declaration
Place simple attributes for stored properties on the same line as the rest of the declaration. Complex attributes with named arguments, or more than one unnamed argument, should be placed on the previous line.
/// Wrong
struct BaseView: View {
@StateObject
var object = ObservableObject()
@available(*, unavailable, message: "No longer in production") var oldView: some View {
// ...
}
}
/// Right
struct BaseView: View {
@StateObject var object = ObservableObject()
@available(*, unavailable, message: "No longer in production")
var oldView: some View {
// ...
}
}
Empty Space
Include a single space in an empty set of braces({ }
).
/// Wrong
extension UIView: Buildable {}
/// Right
extension UIView: Buildable { }
Functions
Omit Void
return types from function definitions.
/// Wrong
func run() -> Void {
// ...
}
/// Right
func run() {
// ...
}
Separate long function declarations with line breaks before each argument label, and before the return signature or any effects (async
, throws
).
class Company {
/// Wrong
func addMember(name: String, email: String, number: String) -> Member {
// ...
}
/// Wrong
func addMember(
name: String,
email: String,
number: String)
-> Member {
// ...
}
/// Right
func addMember(
name: String,
email: String,
number: String
)
-> Member
{
// ...
}
}
Long function invocations should also break on each argument.
/// Wrong
Company.addMember(name: "Zepa", email: "official@pelagornis.com", number: "010-1234-4567")
Company.addMember(
name: "Zepa",
email: "official@pelagornis.com",
number: "010-1234-4567"
)
/// Right
Company.addMember(
name: "Zepa",
email: "official@pelagornis.com",
number: "010-1234-4567")
Remove blank lines between chained functions.
/// Wrong
var item: [String] {
datas
.filter { $0.fetch() }
.map { $0.id }
}
/// Right
var item: [String] {
datas
.filter { $0.fetch() }
.map { $0.id }
}
Closure
Favor Void
return types over () in closure declarations.
/// Wrong
func run(completion: () -> ()) {
// ...
}
/// Right
func run(completion: () -> Void) {
// ...
}
Name unused closure parameters as underscores(_
).
/// Wrong
run() { environment, command in
Task.run(command)
}
/// Right
run() { _, command in
Task.run(command)
}
Omit Void
return types from closure expressions.
run() { command -> Void in
// ...
}
/// Right
run() { command in
// ...
}
Prefer trailing closure syntax for closure arguments with no parameter name.
// WRONG
member.map({ $0.id })
// RIGHT
member.map { $0.id }
Swift Formatting Rules
Indentation
Indent by 2 spaces at a time.
When writing a colon (:
), leave spaces only on the right side of the colon.
/// Wrong
let item:[Int:String]?
/// Right
let item: [Int: String]?
Maximum line length
Each line should have a maximum column width of 100 characters.
- Xcode’s
Prefeneces
->Text Editing
->Display
and set it to 100 characters.
Empty line
Make sure there are no spaces in the blank line, and all files must end in blank lines. The MARK syntax requires spaces below.
/// Wrong
// MARK: Property
let name: String = ""
/// Right
// MARK: Property
let name: String = ""
Semicolons
Semicolons (;) are not used, either to terminate or separate statements.
/// Wrong
func run() {
let company = "Pelagornis";
print("Hello \(company)");
}
/// Right
func run() {
let company = "Pelagornis";
print("Hello \(company)")
}
Import
The module import is sorted alphabetically, import the built-in framework first, separated by blank lines, and import the third-party framework.
import UIKit
import Builder
import Snapkit
Comment
Use ///
to leave comments used for documentation.
/// Banner image exposed at the top of the main screen
final class BannerView: UIView {
/// Image to float banner image View
var bannerImageView: ImageView!
}
Use // MARK:
to separate codes by type.
// MARK: Properties
let nameLabel: UILabel!
let dismissButton: UIButton!
// MARK: Initalizer
override init(frame: CGRect) {
// ...
}
// MARK: Actions
override func dismissButtonDidTap() {
// ...
}
Patterns
Unwrapping
Prefer to initialize properties at init time, if possible, without using the force unwrapping option.
/// Wrong
final class Company: NSObject {
var name: String!
init() {
super.init()
name = "Pelagornis"
}
}
/// Right
final class Company: NSObject {
var name: String
init() {
name = ""
super.init()
}
}
Optional Binding
guard else
is written on the same line if it does not impair readability or does not exceed 100 lines.
And Prefer to use guard
statements rather than if
statements to minimize overlap.
Defer
Consider using defer block
if you need a cleanup code at multiple end points.
Blank
Only one blank line is allowed
/// Wrong
func run() {
let name = ""
print(name)
}
/// Right
func run() {
let name = ""
print(name)
}
Spacing
Give a space around the curly brackets.
/// Wrong
value.filter{true}.map{$0}
/// Right
value.filter { true }.map { $0 }
Access Modifier
Access control should be as strict as possible, preferring public
to open
and private
to fileprivate
unless that level is required.
Global Function
It prefers to define methods in the type definition section, and does not define possible global functions
/// Wrong
func age(_ person: Person) {
// ...
}
/// Right
struct Person {
var age: Int {
// ...
}
}
Property
Extract complex property observers into methods. The purpose is to reduce overlap, to separate side effects from attribute declarations, and to make the use of implicitly forwarded parameters explicitly oldValue
.
/// WRONG
public class TextField {
public var text: String? {
didSet {
guard oldValue != text else {
return
}
}
}
}
/// RIGHT
public class TextField {
public var text: String? {
didSet { textDidUpdate(from: oldValue) }
}
private func textDidUpdate(from oldValue: String?) {
guard oldValue != text else {
return
}
}
}
Static Type
If a method needs to be redefined, the default type method should be static
because the author must choose a static
type instead of using the class
keyword.
/// Wrong
class Company {
class func checkStaff(_ member: Member, time: Date) {
// ...
}
}
/// Right
class Company {
static func checkStaff(_ member: Member, _ time: Date) {
// ...
}
}
Final Keyword
If inheritance is unnecessary, add the final
keyword to the default class.
/// Wrong
class Repository {
// ...
}
/// Right
final class Repository {
// ...
}
Immutable or Computed Static Properties
Prefer immutable or computed static
properties over mutable ones whenever possible. Use stored static let
properties or computed static var
properties over stored static var
properties whenever possible, as stored static var
properties are global mutable state.
/// Wrong
enum Color {
static var red = DynamicColor(...)
}
/// Right
enum Color {
static let red = DynamicColor(...)
}
/// Wrong
struct Timmer {
var count: Int
static var start = Timmer(count: 0)
}
/// Right
struct Timmer {
var count: Int
static var start: Timer {
Timmer(count: 0)
}
}
Enum Type
Preferred to list all cases for accuracy consideration rather than using default
cases when switching enumeration types.
/// Not Preferred
enum Color {
case red
case green
case blue
var rawValue: String {
switch self {
case .red:
return "Red"
case .green:
return "Green"
case .blue:
return "Blue"
default:
return "Not provided Color"
}
}
}
/// Preferred
enum Color {
case red
case green
case blue
var rawValue: String {
switch self {
case .red:
return "Red"
case .green:
return "Green"
case .blue:
return "Blue"
}
}
}
Return Keyword
Omit the unnecessary return
keyword.
/// Wrong
var size: CGSize {
return CGSize(
width: 100.0,
height: 100.0
)
}
/// Right
var size: CGSize {
CGSize(
width: 100.0,
height: 100.0
)
}
AnyObject Type
Use AnyObject
instead of class
in protocol definitions.
// WRONG
protocol Request: class {
// ...
}
// RIGHT
protocol Request: AnyObject {
// ...
}
Extension
Extension specifies access control for each declaration individually.
/// Wrong
public extension Company {
func addStaff(_ member: Member) {
// ...
}
}
/// Right
extension Company {
public func addStaff(_ member: Member) {
// ...
}
}
Logging
Prefer dedicated logging systems like os_log
or swift-log
over writing directly to standard out using print(…)
, debugPrint(…)
, or dump(…)
.
Equatable Type
Prefer using the generated Equatable
implementation when comparing properties of all types.
Void Type
Avoid using ()
as a type and prefer to use Void
.
/// Wrong
let response: (Response<(), Error>) -> ()
/// Right
let response: (Response<Void, Error>) -> Void
Avoid using Void()
and prefer to use ()
let response: (Response<Void, Error>) -> Void
/// Wrong
response(.success(Void()))
/// Right
response(.success(()))