Hướng dẫn Code Style
Hướng dẫn này bao gồm các quy ước code được sử dụng trong TablePro. Tuân theo các quy ước này đảm bảo code nhất quán, dễ đọc trong toàn bộ dự án.
Công cụ
TablePro sử dụng các công cụ tự động cho chất lượng code:
Công cụ Mục đích File cấu hình SwiftLint Linting và phân tích tĩnh .swiftlint.ymlSwiftFormat Code formatting .swiftformat
Chạy các công cụ
# Kiểm tra các vấn đề linting
swiftlint lint
# Tự động sửa các vấn đề linting
swiftlint --fix
# Format code
swiftformat .
# Kiểm tra formatting mà không áp dụng
swiftformat --lint .
Chạy các công cụ này trước khi commit. SwiftLint cũng chạy tự động trong quá trình build Xcode.
Nguyên tắc kiến trúc
Separation of Concerns
Giữ logic nghiệp vụ trong models/view models, không trong views
Views chỉ nên xử lý presentation
// Tốt: Logic trong ViewModel
class ConnectionViewModel : ObservableObject {
func validateConnection () -> Bool {
return ! host. isEmpty && port > 0
}
}
// Không tốt: Logic trong View
struct ConnectionView : View {
var body: some View {
Button ( "Connect" ) {
// Không đặt logic validation ở đây
}
. disabled (host. isEmpty || port <= 0 )
}
}
Value Types First
Ưu tiên struct hơn class trừ khi cần reference semantics:
// Tốt: Value type cho dữ liệu
struct DatabaseConnection : Codable {
let id: UUID
var name: String
var host: String
}
// Khi nào dùng class: shared state, identity quan trọng
@MainActor
class DatabaseManager : ObservableObject {
static let shared = DatabaseManager ()
}
Immutability
Sử dụng let mặc định; chỉ dùng var khi cần mutation:
// Tốt
let connection = DatabaseConnection ( ... )
let results = try await driver. execute ( query : sql)
// Chỉ dùng var khi cần
var currentPage = 1
currentPage += 1
Quy ước đặt tên
Types
Element Quy ước Ví dụ Classes/Structs UpperCamelCase DatabaseConnectionEnums UpperCamelCase DatabaseTypeEnum cases lowerCamelCase .postgresqlProtocols UpperCamelCase DatabaseDriver
Functions và Variables
Element Quy ước Ví dụ Functions lowerCamelCase executeQuery()Variables lowerCamelCase connectionStringConstants lowerCamelCase maxRetryAttemptsParameters lowerCamelCase tableName: String
Boolean Properties
Sử dụng prefix is/has/can:
var isConnected: Bool
var hasValidCredentials: Bool
var canExecuteQuery: Bool
Factory Methods
Sử dụng prefix make:
func makeConnection () -> DatabaseConnection
func makeDriver ( for type : DatabaseType) -> DatabaseDriver
Indentation
4 spaces (không bao giờ dùng tabs)
Cấu hình editor để chuyển tabs thành spaces
Độ dài dòng
Tối đa 120 ký tự mỗi dòng
Ngắt dòng dài để dễ đọc
// Tốt: Ngắt dòng để dễ đọc
let result = try await driver. executeParameterized (
query : "SELECT * FROM users WHERE email = ?" ,
parameters : [email]
)
// Tránh: Dòng dài đơn
let result = try await driver. executeParameterized ( query : "SELECT * FROM users WHERE email = ?" , parameters : [email])
Braces
K&R style - opening brace trên cùng dòng:
// Tốt
if condition {
doSomething ()
} else {
doSomethingElse ()
}
// Không tốt
if condition
{
doSomething ()
}
Spacing
// Khoảng trắng xung quanh operators
let sum = a + b
let isValid = count > 0 && name. isEmpty == false
// Không có khoảng trắng cho ranges
for i in 0 ..< 10 { }
// Khoảng trắng sau colon trong khai báo type
var name: String
func connect ( host : String , port : Int )
Access Control
Chỉ định rõ ràng
Luôn chỉ định access modifiers:
// Tốt: Access rõ ràng
private var connectionPool: [Connection] = []
internal func processResult ( _ result : QueryResult)
public func connect () async throws
// Tránh: Implicit internal
var connectionPool: [Connection] = [] // Implicit internal
Ưu tiên Private
Sử dụng access hạn chế nhất có thể:
class DatabaseManager {
// Public API
public func connect () async throws { ... }
// Internal helpers
private func validateConnection () -> Bool { ... }
private var activeDriver: DatabaseDriver ?
}
Extension Access
Chỉ định access trên extension, không phải các thành viên riêng lẻ:
// Tốt
public extension NSEvent {
var semanticKeyCode: KeyCode ? { ... }
}
// Tránh
extension NSEvent {
public var semanticKeyCode: KeyCode ? { ... }
}
Optionals
Tránh Force Unwrapping
Không bao giờ dùng ! để unwrap:
// Tốt
if let connection = connection {
use (connection)
}
guard let result = result else { return }
// Không tốt
let connection = connection ! // Crash nếu nil
Safe Casting
Sử dụng conditional casting:
// Tốt
if let value = param as? SQLFunctionLiteral {
return value. property
}
// Không tốt
let value = param as! SQLFunctionLiteral // Crash nếu sai type
Guard cho Early Exit
Sử dụng guard để giảm nesting:
// Tốt
func processResult ( _ result : QueryResult ? ) -> [Row] {
guard let result = result else { return [] }
guard result.rowCount > 0 else { return [] }
return result. rows . map { transform ( $0 ) }
}
// Tránh nesting sâu
func processResult ( _ result : QueryResult ? ) -> [Row] {
if let result = result {
if result.rowCount > 0 {
return result. rows . map { transform ( $0 ) }
}
}
return []
}
Collections
Ưu tiên Semantic Methods
// Tốt
if items. isEmpty { }
if items. contains (target) { }
if let first = items. first ( where : { $0 . isValid }) { }
// Tránh
if items. count == 0 { }
if items. filter ({ $0 == target }). count > 0 { }
if let first = items. filter ({ $0 . isValid }). first { }
Xóa Unused Enumeration
// Tốt
items. map { item in item. value }
items. map (\. value )
// Tránh (nếu index không được dùng)
items. enumerated (). map { _ , item in item. value }
Closures
Trailing Closure Syntax
Sử dụng trailing closures khi tham số cuối là closure:
// Tốt
items. filter { $0 . isActive }
. map { $0 . name }
// Tránh
items. filter ({ $0 . isActive })
. map ({ $0 . name })
Implicit Returns
Sử dụng implicit returns cho single-expression closures:
// Tốt
items. map { $0 . name }
items. filter { $0 . isActive }
// Cũng tốt khi rõ ràng hơn
items. map { item in
item. name
}
Unused Closure Arguments
Sử dụng _ cho các tham số không dùng:
// Tốt
button. onTap { _ in
handleTap ()
}
// Tránh
button. onTap { event in // event không được dùng
handleTap ()
}
// Tốt: Giải thích "tại sao", không phải "cái gì"
// Chúng ta dùng delay 1.5 giây để đảm bảo SSH tunnel được thiết lập
// trước khi thử kết nối database
try await Task. sleep ( nanoseconds : 1_500_000_000 )
// Tránh: Comments hiển nhiên
// Lấy tên user
let name = user. name
Sử dụng /// cho public APIs:
/// Thực thi SQL query và trả về kết quả.
///
/// - Parameter query: SQL query cần thực thi
/// - Returns: `QueryResult` chứa rows và columns
/// - Throws: `DatabaseError` nếu query thất bại
func execute ( query : String ) async throws -> QueryResult
Tổ chức code với MARK comments:
// MARK: - Properties
private var connection: DatabaseConnection
private var driver: DatabaseDriver ?
// MARK: - Lifecycle
init ( connection : DatabaseConnection) {
self . connection = connection
}
// MARK: - Public Methods
func connect () async throws { ... }
// MARK: - Private Helpers
private func validateConnection () -> Bool { ... }
Đặc Thù SwiftUI
Cấu trúc View
struct ContentView : View {
// MARK: - Properties
@StateObject private var viewModel = ContentViewModel ()
@State private var isLoading = false
// MARK: - Body
var body: some View {
VStack {
headerView
contentView
footerView
}
}
// MARK: - Subviews
private var headerView: some View {
Text ( "Header" )
}
private var contentView: some View {
// ...
}
}
Thứ tự Property Wrappers
struct MyView : View {
// Environment
@Environment (\. dismiss ) private var dismiss
@EnvironmentObject private var appState: AppState
// State Objects
@StateObject private var viewModel = MyViewModel ()
// State
@State private var isEditing = false
@Binding var selection: Item ?
// Regular properties
let title: String
}
Giới hạn
Giới hạn SwiftLint
Metric Warning Error Độ dài Function body 160 dòng 250 dòng Độ dài Type body 1100 dòng 1500 dòng Độ dài File 1200 dòng 1800 dòng Cyclomatic complexity 40 60
Xử lý File dài
Trích xuất extensions vào các file riêng:
MainContentCoordinator.swift
MainContentCoordinator+RowOperations.swift MainContentCoordinator+Pagination.swift MainContentCoordinator+Filtering.swift
Tài nguyên
Bước tiếp theo