- 推荐个规范代码的库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)
}
}
欢迎大家修正,填补。