Swift使用规范

Posted 崔小花o

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift使用规范相关的知识,希望对你有一定的参考价值。

一. 格式规范

1.1 使用4个空格进行缩进

推荐

if value == 1 
    print("")

1.2 二元运算符(+, ==, 或->)的前后都需要添加空格

推荐

let value = 1 + 2
                    
if value == 1 
    /* ... */

                    
func test(with value: TestClass) -> returnValue 
    /* ... */

1.3 一般情况下,在逗号和冒号后面加一个空格

推荐

let array = [1, 2, 3, 4, 5]

let dic: [String: Any] = [:]

不推荐

let array = [1,2,3,4,5]

let dic : [String :Any] = [:]

1.4 switch 每个case结尾留一行空行, 最后一行不留. if同理.

推荐

switch i 
case .a:
    print("a")

case .b:
    print("b")
    
default:
    default


if isTrue 
    /* ... 结尾空行 */

 else if isFlase 
    /* ... */

 else 
    /* ... 最后一行不需要空行 */

不推荐

switch i 
case .a:
    print("a")
case .b:
    print("b")
default:
    default



if isTrue 
    /* ... */
 else if isFlase 
    /* ... */
 else 
    /* ... */


1.5 代码结尾不要使用分号;

推荐

print("Hello World")

不推荐

print("Hello World");

1.6 左大括号不要另起一行

推荐

// 1. Class Define
class TestClass 
    /* ... */


// 2. if
if value == 1 
    /* ... */


// 3. if else
if value == 1 
    /* ... */
 else 
    /* ... */


// 4. while
while isTrue 
    /* ... */


// 5. guard
guard let value = value else  
    /* ... */

不推荐

// 1. Class Define
class TestClass 

    /* ... */


// 2. if
if value == 1

    /* ... */


// 3. if else
if value == 1

    /* ... */

else

    /* ... */


// 4. while
while isTrue 

    /* ... */


// 5. guard
guard let value = value else 

    /* ... */

1.7 判断语句不用括号

推荐

if value == 1 
    /* ... */


if value == 1 && string == "test" 
    /* ... */

不推荐

if (value == 1) 
    /* ... */


if ((value == 1) && (string == "test")) 
    /* ... */

1.8 不建议使用self. ,除非方法参数与属性同名或其他必要情况

推荐

func setup() 
    label = UILabel()


不推荐

func setup() 
    self.label = UILabel()

1.9 善用类型推导, 不写多余代码

推荐

func set(color: UIColor) 
    /* ... */


set(color: .black)

不推荐

func set(color: UIColor) 
    /* ... */


set(color: UIColor.black)

1.10 添加有必要的注释,尽可能使用Xcode注释快捷键(⌘⌥/)

推荐

/// <#Description#>
///
/// - Parameter test: <#test description#>
/// - Returns: <#return value description#>
func test(string: String?) -> String? 
    /* ... */

不推荐

/// <#Description#>
func test(string: String?) -> String? 
    /* ... */

1.11 注释符//后加空格,如果//跟在代码后面,前面也加一个空格

推荐

// 注释
let aString = "xxx" // 注释

1.12 使用// MARK: -,按功能和协议 / 代理分组

/// MARK顺序没有强制要求,但System API & Public API一般分别放在第一块和第二块。

// MARK: - Public

// MARK: - Request

// MARK: - Action

// MARK: - Private

// MARK: - xxxDelegate

1.13 对外接口不兼容时,使用@available(ios x.0, *)标明接口适配起始系统版本号

@available(iOS x.0, *)
class MyClass 
    /* ... */


@available(iOS x.0, *)
func myFunction() 
    /* ... */

1.14 方法间合理换行

推荐

extension XXX  
    // 扩展开始 增加一个换行
    func xxxx1() 
        /* ... */
    
    // 方法之间 增加一个换行
    func xxxx2() 
        /* ... */
    
 // 结尾不要有多余换行

二. 命名规范

2.1 建议不要使用前缀, 善用命名空间.

推荐

HomeViewController
Bundle

不推荐

NEHomeViewController
NSBundle

2.2 不要缩写、简写、单个字母来命名

推荐

let frame = view.frame
let image = imageView.image
let backgroundColor = view.backgroundColor 

不推荐

let f = view.frame
let img = imageView.image
let bgColor = view.backgroundColor 

2.3 如非必要, 不要声明全局常量/变量/函数, 应该根据类型适当归类包装, 合理利用命名空间.

推荐

enum Main 
    static let color = UIColor(red: 0.1, green: 0.2, blue: 0.3, alpha: 1.0)
    static let count = 13
    static func font(_ size: CGFloat) -> UIFont 
        return UIFont(name: "xxx", size: size) ?? .systemFont(ofSize: size)
    

不推荐

let mainColor = UIColor(red: 0.1, green: 0.2, blue: 0.3, alpha: 1.0)
let mainCount = 13
func mainFont(_ size: CGFloat) -> UIFont 
    return UIFont(name: "xxx", size: size) ?? UIFont.systemFont(ofSize: size)


2.4 变量命名

  • 使用小驼峰,首字母小写

  • 变量命名应该能推断出该变量类型,如果不能推断,则需要以变量类型结尾

推荐

class TestClass: class 
    // UIKit的子类,后缀最好加上类型信息
    let coverImageView: UIImageView
    @IBOutlet weak var usernameTextField: UITextField!

    // 作为属性名的firstName,明显是字符串类型,所以不用在命名里不用包含String
    let firstName: String

    // UIViewContrller以ViewController结尾
    let fromViewController: UIViewController

    // 集合类型以复数形式命名
    var datas: [Data] = []
    var items: [Item] = []

不推荐

class TestClass: class 
    // image不是UIImageView类型
    let coverImage: UIImageView
    // or cover不能表明其是UIImageView类型
    var cover: UIImageView

    // String后缀多余
    let firstNameString: String

    // UIViewContrller不要缩写
    let fromVC: UIViewController
    
    // 集合类型多余后缀和描述
    var dataList: [Data] = []
    var itemArray: [Item] = []

2.5 类型命名:使用大驼峰表示法,首字母大写

2.6 方法命名:使用参数标签让方法语义更清楚, 参数标签和参数需要表达正确的语义(Public接口及基础组件必须遵循) (from Swift API Design Guidelines)

2.6.1 省略所有的冗余的外部参数标签

推荐

func min(_ number1: Int, _ number2: Int) 
    /* ... */


min(1, 2)

不推荐

func min(number1: Int, number2: Int) 
    /* ... */


min(number1: 1, number2: 2)

2.6.2 进行安全值类型转换的构造方法可以省略参数标签,非安全类型转换则需要添加参数标签以表示类型转换方法

推荐

extension UInt32 
  /// 安全值类型转换,16位转32位,可省略参数标签
  init(_ value: Int16)
  
  /// 非安全类型转换,64位转32位,不可省略参数标签
  /// 截断显示
  init(truncating source: UInt64)
  
  /// 非安全类型转换,64位转32位,不可省略参数标签
  /// 显示最接近的近似值
  init(saturating valueToApproximate: UInt64)

2.6.3 当第一个参数构成整个语句的介词时(如,at, by, for, in, to, with 等),为第一个参数添加介词参数标签

推荐

// 添加介词标签having
func removeBoxes(having length: Int) 
    /* ... */


let length = 23
x.removeBoxes(having: length)

例外情况是,当后面所有参数构成独立短语时,则介词提前。

推荐

// 介词To提前
a.moveTo(x: b, y: c)

// 介词From提前
a.fadeFrom(red: b, green: c, blue: d)

不推荐

a.move(toX: b, y: c)

a.fade(fromRed: b, green: c, blue: d)

2.6.4 当第一个参数构成整个语句一部分时,省略第一个参数标签,否则需要添加第一个参数标签.

推荐

// 参数构成语句一部分,省略第一个参数标签
view.addSubview(tempView)

// 参数不构成语句一部分,不省略第一个参数标签
dismiss(animated: false)

2.6.5 其余情况下,给除第一个参数外的参数都添加标签

2.7 方法命名:不要使用冗余的单词,特别是与参数及参数标签重复

推荐

func remove(_ member: Element) -> Element?

不推荐

func removeElement(_ member: Element) -> Element?

2.8 命名出现缩写词,缩写词要么全部大写,要么全部小写,以首字母大小写为准

推荐

let urlRouterString = "https://xxxxx"

let htmlString = "xxxx"

class HTMLModel 
    /* ... */


struct URLRouter 
    /* ... */

不推荐

let uRLRouterString = "https://xxxxx"

let hTMLString = "xxxx"

class HtmlModel 
    /* ... */


struct UrlRouter 
    /* ... */

2.9 Bool类型命名:用is为前缀

推荐

var isString: Bool = true

2.10 枚举定义尽量简写,不要包括类型前缀

推荐

public enum UITableViewRowAnimation: Int 
    case fade

    case right // slide in from right (or out to right)

    case left

    case top

    case bottom

    case none // available in iOS 3.0

    case middle // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy

    case automatic // available in iOS 5.0.  chooses an appropriate animation style for you

2.11 协议命名 (from Swift API Design Guidelines)

2.11.1 如果协议描述的是协议做的事应该命名为名词(eg. Collection)

推荐

protocol TableViewSectionProvider 
    func rowHeight(at row: Int) -> CGFloat
    var numberOfRows: Int  get 
    /* ... */

2.11.2 如果协议描述的是能力,需添加后缀able或 ing (eg. Equatable、 ProgressReporting)

推荐

protocol Loggable 
    func logCurrentState()
    /* ... */


protocol Equatable 
    func ==(lhs: Self, rhs: Self) -> Bool 
        /* ... */
    

2.11.3 如果已经定义类,需要给类定义相关协议,则添加Protocol后缀

推荐

protocol InputTextViewProtocol 
    func sendTrackingEvent()
    func inputText() -> String
    /* ... */

2.11.4 如果已经定义类,需要给类定义相关委托协议,则添加Delegate后缀

推荐

public protocol UITabBarControllerDelegate: NSObjectProtocol 
    @available(iOS 3.0, *)
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool

三. 语法规范

3.1 多使用let,少使用var

3.2 少用!去强制解包

3.3 可选类型拆包取值时,使用if let判断

推荐

if let optionalValue = optionalValue 
    /* ... */

杜绝

if  optionalValue != nil 
    let value = optionalValue!
    /* ... */

3.4 多个可选类型拆包取值时,将多个if let合并, 除非特殊逻辑需要.

推荐

var subview: UIView?
var volume: Double?

if let subview = subview, let volume = volume 
    /* ... */

不推荐

var optionalSubview: UIView?
var volume: Double?

if let unwrappedSubview = optionalSubview 
    if let realVolume = volume 
        /* ... */
    

3.5 不要使用 as! 或 try!

推荐

// 使用if let as?判断
if let text = text as? String 
    /* ... */


// 使用if let try 或者 try?
if let test = try aTryFuncton() 
    /* ... */

3.6 数组和字典变量定义,定义时需要标明泛型类型,并使用更简洁的语法.

推荐

var names: [String] = []
var lookup: [String: Int] = [:]

不推荐

var names = [String]()
var names: Array<String> = [String]() / 不够简洁

var lookup = [String: Int]()
var lookup: Dictionary<String, Int> =Dictionary<String, Int>() // 不够简洁

3.7 数组访问尽可能使用 .first 或 .last, 推荐使用 for item in itemsitems.forEach 而不是 for i in 0...X

推荐

items.first
items.last

for item in items 
    /* ... */


items.forEach  
    /* ... */

不推荐

items[0]
items[items.count - 1]

for i in 0 ..< items.count 
    let item = items[i]
    /* ... */

3.8 如果变量能够推断出类型,则不建议声明变量时指明类型

推荐

let value = 1 

let text = "xxxx"

不推荐

let value: Int = 1 

let text: String = "xxxx"

3.9 判断相等

3.9.1 使用==!=判断内容上是否一致

推荐

// String类型没有-isEqualToString方法,用==判断是否相等
let str1 = "netease"
let str2 = "netease"
if str1 == str2 
    // is true
    /* ... */


// 对于自定义类型,判断内容是否一致,需要实现Equatable接口
class BookItem 
    let bookId: String
    let title: String
    
    init (bookId: String, title: String) 
        self.bookId = bookId
        self.title = title
    


extension BookItem: Equatable 

    static func ==(lhs: BookItem, rhs: BookItem) -> Bool 
        // 具体判断规则根据实际需要进行
        return lhs.bookId == rhs.bookId
    

3.9.2 使用===!==判断class类型对象是否同一个引用,而不是用==!=

推荐

if tenEighty === alsoTenEighty 
    /* ... */


if tenEighty !== notTenEighty 
    /* ... */

3.10 协议

3.10.1 当实现protocol时,如果确定protocol的实现不会被重写,建议用extension将protocol实现分离

推荐

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

不推荐

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

3.12 当方法最后一个参数是Closure类型,调用时建议使用尾随闭包语法, 但只在只存在一个闭包参数时才使用尾闭包。

推荐

UIView.animateWithDuration(1.0) 
    self.myView.alpha = 0


UIView.animateWithDuration(1.0,
    animations: 
        self.myView.alpha = 0
    ,
    completion:  finished in
        self.myView.removeFromSuperview()
    
)

不推荐

UIView.animateWithDuration(1.0, animations: 
    self.myView.alpha = 0
)

UIView.animateWithDuration(1.0,
    animations: 
        self.myView.alpha = 0
    )  finished in
        self.myView.removeFromSuperview()

3.13 高阶函数推荐最简化语法

推荐

array.sort(by: <)

array.sort  $0.age < $1.age 

不推荐

array.sort  (l, r) -> Bool in
    l < r


array.sort  (l, r) -> Bool in
    return l < r

3.14 访问控制 (优先考虑最低级)

  • private

  • fileprivate

  • internal (默认忽略不写)

  • public

  • open

访问控制权限关键字应该写在最前面,除了@IBOutlet@IBAction@discardableResult@objc等关键字.

3.15 如调用者可以不使用方法的返回值,则需要使用@discardableResult标明

推荐

@discardableResult
func print(message: String) -> String 
    let output = "Output : \\(message)"
    print(output)
    return output

3.16 Golden Path,最短路径原则

推荐

func test(_ number1: Int?, _ number2: Int?, _ number3: Int?) 
    guard
        let number1 = number1, 
        number2 = number2, 
        number3 = number3 else  
        fatalError("impossible") 
    
    /* ... */


func login(with username: String?, password: String?) throws -> LoginError 
    guard let username = username else  
        throw .noUsername 
    
    guard let password = password else  
        throw .noPassword
    

    /* login code */

不推荐

func test(_ number1: Int?, _ number2: Int?, _ number3: Int?) 
    if let number1 = number1 
        if let number2 = number2 
            if let number3 = number3 
                /* ... */
             else 
                fatalError("impossible")
            
         else 
            fatalError("impossible")
        
     else 
        fatalError("impossible")
    


func login(with username: String?, password: String?) throws -> LoginError 
    if let username = username 
      if let password = password 
          /* login code */
       else 
          throw .noPassword
      
     else 
        throw .noUsername
    

3.17 循环引用

3.17.1 使用委托和协议时,避免循环引用,定义属性的时候使用weak修饰

推荐

public weak var dataSource: UITableViewDataSource?

public weak var delegate: UITableViewDelegate?

3.17.2 在逃逸Closures中使用self时避免循环引用

推荐

request(.list)  [weak self] (result: Result<[Model]>) in
    guard let self = self else  return 

    self.items = self.result.value
    self.tableView.reloadData()

不推荐

request(.list)  [unowned self] (result: Result<[Model]>) in
    self.items = self.result.value
    self.tableView.reloadData()

不推荐

request(.list)  [weak self] (result: Result<[Model]>) in
    self?.items = self?.result.value
    self?.tableView.reloadData()

3.18 空判断

推荐

if array.isEmpty 
    /* ... */


if string.isEmpty 
    /* ... */

不推荐

if array.count == 0 
    /* ... */


if string.count == 0 
    /* ... */

3.19 单例

推荐

class TestManager 
    static let shared = TestManager()

    /* ... */

四. 项目规范

核心思想

简化一切繁琐代码, 复杂逻辑多方法分类拆分, 风格保持简洁优雅, 严谨易读 杜绝一切不安全情况.

复用封装视情况而定, 可以独立拆分的全部分离, 维护成本最小化, 保持整体工程高内聚低耦合, 业务尽可能模块化管理.

标准StoryBoard构建的ViewController结构示例

import UIKit

class SettingViewController: UIViewController 
    // 各种常量变量声明区域
    // 顺序通常为 public -> private -> lazy -> IB -> Other
    // 初始化优先使用Then
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        setup()
        setupLayout()
        setupNotification()

        loadData()
    

    // 其他IBAction

    // 其他重写属性或方法    

    override var preferredStatusBarStyle: UIStatusBarStyle 
        return .default
    
    
    static func instance() -> Self 
        return StoryBoard.setting.instance()
    

    deinit 
        // 释放处理
    


extension SettingViewController 
    // public方法


extension SettingViewController 

    private func setup() 
        // 设置 添加子视图等
    
    
    private func setupLayout() 
        // 设置布局
    
    
    private func setupNotification() 
        // 设置通知监听
    


extension SettingViewController 
    // 事件方法


extension SettingViewController 
    // 数据请求方法
    private func loadData() 
        /* ... */
    


extension SettingViewController 
    // 其他逻辑方法


extension SettingViewController 
    // 其他代理方法

图片等资源存储

优先使用Assets.xcassets

颜色 图片使用

推荐

// Color Literal
#colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)

// Image Literal
#imageLiteral(resourceName: <#T##String#>)

公共组件设计准则

  • 良好的可扩展性
  • 严谨的逻辑思维
  • 严格的访问权限
  • 优雅的语法特性
  • 合理的接口设计
  • 通用的业务兼容

以上是关于Swift使用规范的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Swift 3.1 中的 API 创建解析数据,API 以括号开头和结尾

《从零开始学Swift》学习笔记(Day 57)——Swift编码规范之注释规范:

命名规范.NET中的枚举类型,要以Enum结尾吗?

Swift 代码规范 (Swift-Code-Style)

《从零開始学Swift》学习笔记(Day 57)——Swift编码规范之凝视规范:文件凝视文档凝视代码凝视使用地标凝视

如何在 Swift 中检查字符串以(前缀)开头或以(后缀)结尾