This is a Swift development code specification text

correctness

As much as possible, let your code compile without warning. This rule applies to a number of situations, such as the #selector type replacing applicable string literals.

named

Naming follows the following key points:

  • Use scenarios as clearly as possible
  • Clarity is more important than brevity
  • Use hump nomenclature
  • Type and protocol start with an uppercase letter and other lowercase letters
  • Include all the keywords you need and omit unnecessary ones
  • Use the role name, not the type
  • Weak reference types are commented out
  • Make good use of control flow
  • The factory method starts with make
  • Method naming follows these rules
    • Action methods follow rules such as -ed, -ing
    • Nominalization methods follow rules like formX
    • Boolean methods are named to be seen and used to make judgments
    • Description of protocols should be nominalized
    • Protocols If describing a “can……” “Should end with -able or ible
  • Use technical terms
  • Avoid abbreviations
  • Wherever possible, implement functionality through methods and attributes.
  • The naming should be binding
  • Be consistent with context acronyms
  • Avoid function return type overloading
  • Select a good parameter name to use as a document
  • Arguments to closures and tuples are identified
  • Make good use of default parameters

An introduction to Xcode’s method properties list

As you read through this list, clarity of meaning becomes even more important. You need to make it as easy as possible for others to refer to a method name.

  1. Write methods with no parameters. Such as:addTarget
  2. When writing a method, write the label of the parameter.

The Class prefix

Types in Swift are automatically added to their module’s namespace, so you don’t need to prefix them. If two names from different modules conflict, you can disambiguate it by pre-determining the type name using the module name. However, specify the module name only if there is a possibility of confusion. (However, in TDW projects, it is recommended to use the prefix, which is used as the TDW product logo)

import SomeModule                               
let myClass = MyModule.UsefulClass()
Copy the code

The agent

When you customize a proxy method, the first parameter should be an anonymous parameter. This parameter is the proxy source. (UIkit includes many such examples.) True:

func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
Copy the code

Wrong:

func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
Copy the code

Use context type inference

Write shorter and more concise code using compiler context type inference.

Correct:

let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
Copy the code

The wrong

let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
Copy the code

template

Generic types should be meaningfully described and written with a capital camel name. If you can’t find a word for this type, use one of the traditional 26 letters, capital letters, for example: T, U, V, etc. Correct:

struct Stack<Element> { ... }
func write<Target: OutputStream>(to target: inout Target)
func swap<T>(_ a: inout T, _ b: inout T)
Copy the code

Wrong:

struct Stack<T> { ... }
func write<target: OutputStream>(to target: inout target)
func swap<Thing>(_ a: inout Thing, _ b: inout Thing)
Copy the code

language

Use American English to describe the API correctly:

let color = "red"
Copy the code

Wrong:

let colour = "red"
Copy the code

Code organization

Use extensions to organize your code logic blocks. Each extension should use // MARK: – to illustrate the functionality of that extension.

Compliance with the Agreement

In particular, when you add an inheritance protocol, it is best to use the extension split protocol method.

Correct:

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
}
Copy the code

Wrong:

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

Useless code

Don’t comment out useless code if it should be deleted

interval

Xcode Settings:

The control flow

Correct:

if user.isHappy {
  // Do something
} else {
  // Do something else
}
Copy the code

Wrong:

if user.isHappy
{
  // Do something
}
else {
  // Do something else
}
Copy the code

On the use of Spaces:

Correct:

Class TestDatabase: Database {var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]}Copy the code

Wrong:

Class TestDatabase: Database {var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]}Copy the code

annotation

When needed, use comments to explain why a particular piece of code does something. Comments must be kept up to date or removed.

Classes and structures

Which one should I use?

Remember, structs have value semantics. Use structures for things that have no identity. An array containing a, B, and C is exactly the same as another array containing a, B, and C; they are completely interchangeable. It doesn’t matter if you use the first array or the second array, because they represent exactly the same thing. That’s why arrays are structures.

Classes have reference semantics. Use classes for things that have an identity or a specific lifecycle. You can model a person as a class because two objects are two different things. Just because two people have the same name and birthday doesn’t mean they are the same person. But this person’s birthday is a structure, because the date of March 3, 1950 is the same as any other date object of March 3, 1950. The date itself is not identified.

Examples of definitions

Here is an example of a well-designed class definition:

class Circle: Shape {
  var x: Int, y: Int
  var radius: Double
  var diameter: Double {
    get {
      return radius * 2
    }
    set {
      radius = newValue / 2
    }
  }

  init(x: Int, y: Int, radius: Double) {
    self.x = x
    self.y = y
    self.radius = radius
  }

  convenience init(x: Int, y: Int, diameter: Double) {
    self.init(x: x, y: y, radius: diameter / 2)
  }

  override func area() -> Double {
    return Double.pi * radius * radius
  }
}

extension Circle: CustomStringConvertible {
  var description: String {
    return "center = \(centerString) area = \(area())"
  }
  private var centerString: String {
    return "(\(x),\(y))"
  }
}
Copy the code

The above example follows the following design guidelines:

  • The specified types of attributes, variables, constants, parameter declarations, and other statements that have Spaces after (but not before) the colon, such as x: Int and Circle: Shape.
  • If you share a common purpose/context, define multiple variables and structures in a single line.
  • Indent getter and setter definitions and property observer.
  • Do not add modifiers such as internal when they are already the default. Similarly, when overriding a method, do not reuse modifiers.
  • Add additional functionality to the extension (e.g. printing).
  • Hide non-shared, implementation details such as centerString extension code that uses private access control internally.

The use of the Self

For brevity, avoid using ‘self’ when you don’t need to access an object’s properties or call its methods.

Use self only if the compiler requires that self be used.

Calculated attributes:

For brevity, omit the GET clause if a calculated property is read-only. The GET clause is required only if a set clause is provided.

Correct:

var diameter: Double {
    return radius * 2
}
Copy the code

Wrong:

var diameter: Double {
    get {
        return radius * 2
    }
}
Copy the code

Final

It is not allowed to inherit or re-manipulate the content it decorates. The final tag class or members may distract the topic and is not necessary. However, using final can sometimes clarify your intentions and is worthwhile. In the following example, Box has a special purpose and is not intended to allow customization in derived classes. Marking final makes this clear.

// Turn any generic type into a reference type using this Box class.
final class Box<T> {
    let value: T
    init(_ value: T) {
        self.value = value
    }
}
Copy the code

Function declaration

Use concise function declarations (including curly braces) on one line

func reticulateSplines(spline: [Double]) -> Bool {// Reticulate code goes here} For features with long signatures, add line breaks at appropriate points and an additional indentation func reticulateSplines on subsequent lines(spline: [Double], adjustmentFactor: Double, translateConstant: Int, comment: String) -> Bool { // reticulate code goes here }Copy the code

Closure expression

Trailing closures are used only if there is a single closure expression argument at the end of the argument list. The descriptive names of the closure parameters are given.

Correct:

Uiview.animate (withDuration: 1.0) {self.myview.alpha = 0} UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }, completion: { finished in self.myView.removeFromSuperview() })Copy the code

Wrong:

Uiview.animate (withDuration: 1.0, animations: {self.myview.alpha = 0}) UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }) { f in self.myView.removeFromSuperview() }Copy the code

For simple closure expressions that are context-clear, we can return them implicitly:

attendeeList.sort { a, b in
  a > b
}
Copy the code

Linking methods using trailing closures should be clear and readable in context. Spacing, line breaks, and when to use named and anonymous parameters are left to the discretion of the author.

let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)

let value = numbers
  .map {$0 * 2}
  .filter {$0 > 50}
  .map {$0 + 10}
Copy the code

type

Use Swift’s native type whenever possible. Swift provides bridge files for Objective-C, so you can still use all of its methods when you need them.

Correct:

// Double let widthString = (width as NSNumber).stringValue // StringCopy the code

Wrong:

Let width: NSNumber = 120.0 // NSNumber let widthString: NSString = width.stringValue // NSStringCopy the code

constant

Use the let keyword for constants and the var keyword for variables. Let is usually used instead of var if the value of the variable does not change.

Tip: A good idea is to use the let definition for everything and change it to var when the compiler alerts you

You can define constants on a type instead of defining instances of that type using type attributes. Use a static let to declare a type property that is only a constant. Type attributes declared this way are generally superior to global constants because they are easier to distinguish from instance attributes. Correct:

Enum Math {static let e = 2.718281828459045235360287 static let root2 = 1.41421356237309504880168872} let hypotenuse = Side * math.root2 note: The advantage of using this enumeration is that it can't be accidentally instantiated or is simply a namespaceCopy the code

Wrong:

Let e = 2.718281828459045235360287 / / pollutes the global namespace let root2 = 1.41421356237309504880168872 let hypotenuse = side * root2 // what is root2?Copy the code

Static method and variable type properties

Static methods and type attributes work like global functions and variables and should be used with caution. This is useful when functionality is limited to a particular type or requires interoperation with Objective-C.

Optional type

Declare variable and function return types as optional and it is acceptable to return a nil value.

Use only if the instance variable is sure to be initialized before use! Implicitly strong solution type declaration, such as the subview viewDidLoad to be set.

When accessing optional values, use optional links if the value is accessed only once, or if there are more than one option in the chain:

self.textContainer? .textLabel? .setNeedsDisplay()Copy the code

Optional bindings make it easier to open and perform multiple operations at once:

if let textContainer = self.textContainer {
  // do many things with textContainer
}
Copy the code

When naming optional variables and attributes, avoid naming them like optionalString or maybeView, because their optional arguments are already in the type declaration.

Hide the original name appropriately for optional bindings, rather than using names like unwrappedView or actualLabel.

Correct:

var subview: UIView?
var volume: Double?

// later on...
if let subview = subview, let volume = volume {
  // do something with unwrapped subview and volume
}
Copy the code

Wrong:

var optionalSubview: UIView?
var volume: Double?

if let unwrappedSubview = optionalSubview {
  if let realVolume = volume {
    // do something with unwrappedSubview and realVolume
  }
}
Copy the code

Lazy loading

Consider using lazy loading for fine-grained control of the object lifecycle. This is especially true for UIViewController loaded views. You can use the immediate closure () or invoke the private factory method.

lazy var locationManager: CLLocationManager = self.makeLocationManager()

private func makeLocationManager() -> CLLocationManager {
  let manager = CLLocationManager()
  manager.desiredAccuracy = kCLLocationAccuracyBest
  manager.delegate = self
  manager.requestAlwaysAuthorization()
  return manager
}
Copy the code

Note: [unowned self] There is no need to create a retention loop. Location managers have the side effect of pop-up UIs that require user permission, so fine-grained control makes sense here.

Type inference

You want clean code that lets the compiler infer the type of a constant or variable for a single instance. Type inference also applies to small (non-empty) arrays and dictionaries. Specify specific types such as CGFloat or Int16 as needed.

Correct:

Let message = "click the button" let currentBounds = computeViewBounds () var names = [" Mic ", "Sam", "Christine"] let maximumWidth: CGFloat = 106.5Copy the code

Wrong:

Let message: String = "click the button" let currentBounds: CGRect = computeViewBounds () let names = [String] ()Copy the code

Type annotations for empty arrays and dictionaries: For empty arrays and dictionaries, use type annotations. (Use type annotations for arrays or dictionaries that allocate a large number of lines of content.)

Correct:

var names: [String] = []
var lookup: [String: Int] = [:]
Copy the code

Wrong:

var names = [String]()
var lookup = [String: Int]()
Copy the code

Note: Following this guideline means that choosing descriptive names is more important than ever.

Syntactic sugar

Prefer the compact version of type declarations to the full generic syntax.

Correct:

var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
Copy the code

Wrong:

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

Functions and Methods

Unconstrained functionality that is not attached to a class or type should be used with caution. If possible, use ‘. ‘methods instead of unconstrained functionality. This helps readability and discoverability. Correct:

let sorted = items.mergeSorted()  // easily discoverable
rocket.launch()  // acts on the model
Copy the code

Wrong:

let sorted = mergeSort(items)  // hard to discover
launch(&rocket)
Copy the code

Free functional exceptions:

let tuples = zip(a, b)  // feels natural as a free function (symmetry)
let value = max(x, y, z)  // another free function that feels natural
Copy the code

Memory management

Code should not create reference loops, even in a non-production or tutorial demo. Analyze your object graph and use weak or unowned to prevent strong loops. Alternatively, use value types (struct, enum) to prevent loops entirely.

Extends the object life cycle

Extend the object lifecycle with the [weak self] and Guard let strongSelf = self else {return} statements. Weak self is not used when its advantage over unowned self is not obvious. Significantly longer service life, obviously better than optional expansion.

Recommended:

resource.request().onComplete { [weak self] response in
  guard let strongSelf = self else {
 '' return
  }
  let model = strongSelf.updateModel(response)
  strongSelf.updateUI(model)
}
Copy the code

Not recommended:

// might crash if self is released before response returns
resource.request().onComplete { [unowned self] response in
  let model = self.updateModel(response)
  self.updateUI(model)
}
Copy the code

Not recommended:

// deallocate could happen between updating the model and updating UI resource.request().onComplete { [weak self] response in let model = self? .updateModel(response) self? .updateUI(model) }Copy the code

Access control

Global access control comments in articles can distract from the topic and are not necessary. However, the proper use of private and Fileprivate increases clarity and facilitates encapsulation. Use private instead of Fileprivate whenever possible. Using the extension may require you to use Fileprivate.

Explicitly use open, public, and internal only when you need full access control specifications.

Use access control as the preferred property specifier. The only condition before access control is static, or attributes such as @ibAction, @ibOutlet, and @discardAbleresult.

Recommended:

private let message = "Great Scott!"

class TimeMachine {  
  fileprivate dynamic lazy var fluxCapacitor = FluxCapacitor()
}
Copy the code

Not recommended:

fileprivate let message = "Great Scott!"

class TimeMachine {  
  lazy dynamic fileprivate var fluxCapacitor = FluxCapacitor()
}
Copy the code

The control flow

Try to use for-in-style for loops instead of while-conditional-increment.

Recommended:

for _ in 0.. <3 { print("Hello three times") } for (index, person) in attendeeList.enumerated() { print("\(person) is at position #\(index)") } for index in stride(from: 0, to: items.count, by: 2) { print(index) } for index in (0... 3).reversed() { print(index) }Copy the code

Not recommended:

var i = 0
while i < 3 {
  print("Hello three times")
  i += 1
}

var i = 0
while i < attendeeList.count {
  let person = attendeeList[i]
  print("\(person) is at position #\(i)")
  i += 1
}
Copy the code

Golden Path

When coding with conditional statements, the code on the left should be the “golden” or “happy “path. That is, do not nest if statements. It is also possible to use multiple return statements, for which the Guard is built.

Recommended:

func computeFFT(context: Context? , inputData: InputData?) throws -> Frequencies { guard let context = context else { throw FFTError.noContext } guard let inputData = inputData else { throw FFTError.noInputData } // use context and input to compute the frequencies return frequencies }Copy the code

Not recommended:

func computeFFT(context: Context? , inputData: InputData?) throws -> Frequencies { if let context = context { if let inputData = inputData { // use context and input to compute the frequencies return frequencies } else { throw FFTError.noInputData } } else { throw FFTError.noContext } }Copy the code

Open multiple options, whether using Guard or if let, to reduce nesting and use compound versions where possible. Ex. :

Recommended:

 guard let number1 = number1,
       let number2 = number2,
       let number3 = number3 else {
   fatalError("impossible")
 }
 // do something with numbers
Copy the code

Not recommended:

 if let number1 = number1 {
   if let number2 = number2 {
     if let number3 = number3 {
       // do something with numbers
     } else {
       fatalError("impossible")
     }
   } else {
     fatalError("impossible")
   }
 } else {
   fatalError("impossible")
 }
Copy the code

The failure of the Guard

The Guard declaration needs to exit in some way. In general, this should be a simple one-line statement such as return, throw, break, continue, and fatalError(). Blocks with lots of code should be avoided. If you have multiple exit points that need to be cleared, consider using the defer(deferred) code block to avoid repeated cleanup.

A semicolon

Swift does not require a semicolon after any of your code statements. If you want to combine multiple statements in a single line, you need ‘; ‘. It is not recommended to write ‘; ‘separate multiple statements.

Recommended:

let swift = "not a scripting language"
Copy the code

Not recommended:

let swift = "not a scripting language";
Copy the code

Note: Swift is very different from JavaScript, where omiting semicolons is generally considered unsafe.

parentheses

Parentheses around if are not required and should be omitted. Recommended:

if name == "Hello" {
  print("World")
}
Copy the code

Not recommended:

if (name == "Hello") {
  print("World")
}
Copy the code

In expressions with a lot of code, parentheses make it easier for the code to read.

Recommended:

let playerMark = (player == current ? "X" : "O")
Copy the code

Organization and Bundle Identifier

Whatever xcode, organization should be set to ‘Ray Wenderlich, Bundle Identifier should be set to com. Razeware. TutorialName forms, including TutorialName is the name of the project.