Swift代码规范

  • 推荐个规范代码的库SwiftLint, 有兴趣的同学可瞧瞧如何。
  • 相信我,关于代码规范,你需要仔细看看这篇文章最详尽的 Swift 代码规范指南,此文章笔者写的时候是Xcode7.3, 应该是Swift2.0时代.

关于SwiftLint

  • SwiftLint
  • realm/SwiftLint
  • swifting.io

目前项目中的.swiftLint.yml

disabled_rules: # these rules produce error
  # will be enabled later:
  - dynamic_inline
  - empty_count
  - force_cast
  - force_try
  - variable_name
  - type_name
  # always disabled:
  - legacy_constructor
  - line_length
  - valid_docs
  - trailing_comma
  - operator_usage_whitespace

opt_in_rules:
  - closure_end_indentation
  - closure_spacing

# configurable rules
weak_delegate:
  error
operator_whitespace:
  error
control_statement:
  error
implicit_getter:
  error
closure_end_indentation:
  error
closure_parameter_position:
  error
closure_spacing:
  error
unused_closure_parameter:
  warning
empty_parentheses_with_trailing_closure:
  error

# custom rules
custom_rules:
  # 1
  lazy_position:
    name: "Lazy Position"
    included: ".*.swift"
    regex: "lazy\s+(open|public|internal|fileprivate|private)"
    match_kinds: # SyntaxKinds to match. optional.
      - attribute.builtin
    message: "put 'access control keyword' before 'lazy'"
    severity: error

前些天我们leader发怒了,于是乎“enable closure related rules.
disable type_name for now”, 然后就开始忙起来了,哈哈。

  • fix closure_spacing

Bad

 static var provider: RxMoyaProvider {get}

Good

 static var provider: RxMoyaProvider { get }

Bad

return words.reduce("") {$0 + $1[0...0]}

Good

return words.reduce("") { $0 + $1[0...0] }
  • autocorrect

Bad

// MARK:  relodUI

Good

// MARK: relodUI

Bad

weakSelf!.requestData(.LockCourseComenBack);

Good

weakSelf!.requestData(.LockCourseComenBack)

Bad

guruH = NoHeight; listH = NoHeight;
guruMargin = Margin10; listMargin = Margin0; commentMargin = Margin0;

Good

guruH = NoHeight; listH = NoHeight
guruMargin = Margin10; listMargin = Margin0; commentMargin = Margin0

Bad

func HexRGBAlpha(rgbValue : UInt,alpha : Float) -> UIColor { }

Good

func HexRGBAlpha(rgbValue: UInt, alpha: Float) -> UIColor { }

Bad

weak var delegate: PadArticleListDelegate? = nil

Good

   weak var delegate: PadArticleListDelegate?

Bad

   public func timer(doing: (number: Int) -> (), endTime: () -> ()) {

Good(注意:明确闭包的返回值类型)

   public func timer(doing: (number: Int) -> Void, endTime: () -> Void) {

Bad

// MARK: --- private

Good

// MARK: - -- private
  • fix closure_end_indentation

Bad

searchBar.rx_searchButtonClicked.subscribeNext { [weak self]_ in
   self!.startSearch(self!.searchBar.text!)
   }.addDisposableTo(rx_disposeBag)

Good(注意:调用 Rx点语法的换行)

searchBar.rx_searchButtonClicked
    .subscribeNext { [weak self] in
        self!.startSearch(self!.searchBar.text!)
    }.addDisposableTo(rx_disposeBag)

Bad

searchBar.rx_searchButtonClicked.subscribeNext { _ in
               self.subject.onNext(self.searchBar.text)
           }.addDisposableTo(rx_disposeBag)

Good

searchBar.rx_searchButtonClicked
          .subscribeNext { _ in
              self.subject.onNext(self.searchBar.text)
          }.addDisposableTo(rx_disposeBag)

Bad

override func loadView() {
    super.loadView()
    self.view =
        SelectedScrollContentView.init(frame: self.view.bounds, targetViewController: self) {
            self.data.forEach { [weak self] item in
                let vc: EmploymentInformationViewController = EmploymentInformationViewController.init()
                vc.title = item.title
                vc.ID = item.id
                self?.addChildViewController(vc)
            }
        }!
}

Good(注意:闭包体括号括起来)

override func loadView() {
    super.loadView()
    self.view =
        (SelectedScrollContentView.init(frame: self.view.bounds, targetViewController: self) {
            self.data.forEach { [weak self] item in
                let vc: EmploymentInformationViewController = EmploymentInformationViewController.init()
                vc.title = item.title
                vc.ID = item.id
                self?.addChildViewController(vc)
            }
        }
    )!
}

Bad

 func requestLive() -> Observable {
    return LiveProvider.request(.List)
        .mapObject(ResultSingle).map { ret -> LiveSchedules? in
            xxx
        }
}

Good(注意:换行,以及将闭包体括起来)

func requestLive() -> Observable {
     return LiveProvider.request(.List)
         .mapObject(ResultSingle)
         .map ({ ret -> LiveSchedules? in
             xxx
         })
 }
  • fix shorthand_operator

Bad

balance = balance / 100

Good

balance /= 100

Bad

time = time - 1

Good

time -= 1
  • fix closure_parameter_position

Bad

backView.snp_makeConstraints {
         (make) in
         xxxxx
     }

Good

backView.snp_makeConstraints { (make) in
          xxxxx
      }

Bad

self.model?.cover.forEach({
         [weak self] cover in
        xxx
     })

Good

self.model?.cover.forEach({ [weak self] cover in
           xxx
        })
  • fix generic_position

Bad

NSMutableArray  *array = [NSMutableArray new];

Good

NSMutableArray *array = [NSMutableArray new];
  • �默认的访问控制修饰符是 internal, 如果需要使用internal 可以省略不写

Bad

public var verificationCode: RACReplaySubject = RACReplaySubject()

Good

var verificationCode: RACReplaySubject = RACReplaySubject()

Bad

public func getVerificationCode() { // xxx }

Good

func getVerificationCode() { // xxx }

(20170216)补充:

  • 善用 guard,会使你的逻辑更加清晰

    Bad

class MainRouter {
    static func jumpByType(naviVC: UINavigationController?, link: String?) {
        if let navi = naviVC {
            if let jumpUrl = link {
                let isValidUrl = StringHelper.verifyUrl(urlString: jumpUrl)
                if isValidUrl {
                    Logger.logInfo(message: "is valid url: \(isValidUrl)")
                        navi.pushViewController(WebViewController(url: link!, shareEnable: false), animated: true)
                }
            }
        }
    }
}

Good

class MainRouter {
    static func jumpByType(naviVC: UINavigationController?, link: String?) {
        guard let naviVC = naviVC, let link = link else {
            return
        }
        let isValidUrl = StringHelper.verifyUrl(urlString: link)
        if isValidUrl {
            Logger.logInfo(message: "is valid url: \(isValidUrl)")
            naviVC.pushViewController(WebViewController(url: link, shareEnable: false), animated: true)
        }
    }
}
  • 善用嵌套函数、访问修饰符、Extensions、 protocol,代码整体逻辑有没有更清晰。此控制器是VIPER架构中的V,有兴趣学习VIPER架构的同学可以看我的这篇文章。

Bad

import UIKit
import PullToRefresh

class HomeViewController: UIViewController, HomeViewInput {

    var output: HomeViewOutput!

    let refresher = PullToRefresh()

    lazy var carsouselView: CarouselViewController = {
        return CarouselViewController(path: "app-dept3-carousel")
    }()

    lazy var wikiCardView: WikiCardView = {
        return WikiCardView()
    }()

    lazy var scrollView: UIScrollView = {
        return UIScrollView()
    }()

    override func loadView() {
        super.loadView()

        view.backgroundColor = UIColor.HexRGB(rgbValue: 0xf5f5f5)

        view.addSubview(scrollView)

        let screenWidth = UIScreen.main.bounds.width
        let screenHeight = UIScreen.main.bounds.height
        let statusBarHeight = UIApplication.shared.statusBarFrame.height

        scrollView.frame = CGRect(x: 0, y: statusBarHeight, width: screenWidth, height: screenHeight)
        scrollView.contentSize = CGSize(width: 0, height: screenHeight + 1)

        scrollView.addSubview(carsouselView.view)

        carsouselView.view.snp.makeConstraints { make in
            make.width.equalTo(scrollView.snp.width)
            make.height.equalTo(160)
            make.top.equalTo(scrollView)
        }

        scrollView.addSubview(wikiCardView)

        wikiCardView.snp.makeConstraints { make in
            make.width.equalTo(scrollView.snp.width)
            make.height.equalTo(333)
            make.top.equalTo(carsouselView.view.snp.bottom).offset(10)
        }

        configViews()

        setupPullToRefresh()

        output.viewIsReady()
    }

    func configViews() {
        let homeConfigurator = HomeModuleConfigurator()
        homeConfigurator.configureModuleForViewInput(viewInput: self)

        let carsouselConfigurator = CarouselModuleConfigurator()
        carsouselConfigurator.configureModuleForViewInput(viewInput: carsouselView)
    }

    func setupPullToRefresh() {
        scrollView.addPullToRefresh(refresher) { [weak self] in
            print("PullToRefresh")
            self!.reloadData()
        }
    }

    // MARK: HomeViewInput
    func setupInitialState() {

    }

    func refreshBanner(banner: Banner) {
        Logger.logInfo(message: "refresh banner")
        carsouselView.setBanner(banner: banner)
    }

    func refreshWiki(course: CourseData) {
        Logger.logInfo(message: "refresh wiki")
        wikiCardView.setData(courseData: course)
    }

    func loadDataSuccess() {
        Logger.logInfo(message: "load data success")
        scrollView.endRefreshing(at: Position.top)
    }

    func reloadData() {
        output.reloadData()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        initNaviBar()
    }

    func initNaviBar() {
        if let naviVC = self.navigationController {
            naviVC.setNavigationBarHidden(true, animated: false)
        }
    }

    // MARK: Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    deinit {
        if let topPullToRefresh = scrollView.topPullToRefresh {
            scrollView.removePullToRefresh(topPullToRefresh)
        }
    }

}

Good

import UIKit
import PullToRefresh

final class HomeViewController: UIViewController {

    // MARK: Properties

    var output: HomeViewOutput!

    private let refresher = PullToRefresh()

    fileprivate lazy var carsouselView: CarouselViewController = {
        return CarouselViewController(path: "app-dept3-carousel")
    }()

    fileprivate lazy var wikiCardView: WikiCardView = {
        return WikiCardView()
    }()

    fileprivate lazy var scrollView: UIScrollView = {
        return UIScrollView()
    }()


    // MARK: Life cycle

    override func loadView() {
        super.loadView()

        view.backgroundColor = UIColor().backGroudColor_grad

        func addSubviews() {
            view.addSubview(scrollView)
            scrollView.addSubview(carsouselView.view)
            scrollView.addSubview(wikiCardView)
        }

        func configViews() {
            let homeConfigurator = HomeModuleConfigurator()
            homeConfigurator.configureModuleForViewInput(viewInput: self)

            let carsouselConfigurator = CarouselModuleConfigurator()
            carsouselConfigurator.configureModuleForViewInput(viewInput: carsouselView)
        }

        func layoutSubViews() {
            let screenWidth = UIScreen.main.bounds.width
            let screenHeight = UIScreen.main.bounds.height
            let statusBarHeight = UIApplication.shared.statusBarFrame.height

            scrollView.frame = CGRect(x: 0, y: statusBarHeight, width: screenWidth, height: screenHeight)
            scrollView.contentSize = CGSize(width: 0, height: screenHeight + 1)

            carsouselView.view.snp.makeConstraints { make in
                make.width.equalTo(scrollView.snp.width)
                make.height.equalTo(160)
                make.top.equalTo(scrollView)
            }

            wikiCardView.snp.makeConstraints { make in
                make.width.equalTo(scrollView.snp.width)
                make.height.equalTo(333)
                make.top.equalTo(carsouselView.view.snp.bottom).offset(10)
            }
        }

        func setupPullToRefresh() {
            scrollView.addPullToRefresh(refresher) { [weak self] in
                print("PullToRefresh")
                func reloadData() {
                    self?.output.reloadData()
                }
                reloadData()
            }
        }

        addSubviews()

        layoutSubViews()

        configViews()

        setupPullToRefresh()

        output.viewIsReady()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        func initNaviBar() {
            if let naviVC = self.navigationController {
                naviVC.setNavigationBarHidden(true, animated: false)
            }
        }
        initNaviBar()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    deinit {
        if let topPullToRefresh = scrollView.topPullToRefresh {
            scrollView.removePullToRefresh(topPullToRefresh)
        }
    }
}

extension HomeViewController: HomeViewInput {
    func setupInitialState() {

    }

    func refreshBanner(banner: Banner) {
        Logger.logInfo(message: "refresh banner")
        carsouselView.setBanner(banner: banner)
    }

    func refreshWiki(course: CourseData) {
        Logger.logInfo(message: "refresh wiki")
        wikiCardView.setData(courseData: course)
    }

    func loadDataSuccess() {
        Logger.logInfo(message: "load data success")
        scrollView.endRefreshing(at: Position.top)
    }
}


欢迎大家修正,填补。

你可能感兴趣的:(Swift代码规范)