Swift代码规范

作者: HunterDude | 来源:发表于2017-02-13 14:57 被阅读348次
    • 推荐个规范代码的库SwiftLint, 有兴趣的同学可瞧瞧如何。
    • 相信我,关于代码规范,你需要仔细看看这篇文章最详尽的 Swift 代码规范指南,此文章笔者写的时候是Xcode7.3, 应该是Swift2.0时代.

    关于SwiftLint

    目前项目中的.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<T> {get}
    

    Good

     static var provider: RxMoyaProvider<T> { 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<LiveSchedules?> {
        return LiveProvider.request(.List)
            .mapObject(ResultSingle<Live>).map { ret -> LiveSchedules? in
                xxx
            }
    }
    

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

    func requestLive() -> Observable<LiveSchedules?> {
         return LiveProvider.request(.List)
             .mapObject(ResultSingle<Live>)
             .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 <CourseItem> *array = [NSMutableArray new];
    

    Good

    NSMutableArray<CourseItem> *array = [NSMutableArray<CourseItem> 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代码规范

        本文链接:https://www.haomeiwen.com/subject/vdxtwttx.html