This document outlines the clean code principles followed in the Layover project.
print() statements in production codeLogger with appropriate subsystems and categories// ✅ Good
import OSLog
@MainActor
final class MyService {
private let logger = Logger(subsystem: "com.bholsinger.LayoverLounge", category: "MyService")
func doSomething() {
logger.info("Operation started")
logger.debug("Debug information: \(details)")
logger.error("Error occurred: \(error.localizedDescription)")
}
}
// ❌ Bad
func doSomething() {
print("Operation started")
print("Error: \(error)")
}
logger.info() - General information about app flowlogger.debug() - Detailed debugging informationlogger.warning() - Potential issues that don’t prevent executionlogger.error() - Errors that need attention// ✅ Good
enum RoomConstants {
static let defaultMaxParticipants = 20
static let minParticipants = 2
static let maxPossibleParticipants = 100
}
struct Room {
var maxParticipants: Int = RoomConstants.defaultMaxParticipants
}
// ❌ Bad
struct Room {
var maxParticipants: Int = 20 // Magic number
}
Sources/Models/@MainActor in Sources/ViewModels/Sources/Views/Sources/Services/// ✅ Good - Testable with protocol
protocol RoomServiceProtocol: LayoverService {
func createRoom(name: String, host: User, activityType: RoomActivityType) async throws -> Room
}
final class RoomService: RoomServiceProtocol {
// Implementation
}
// In tests, inject mock
let viewModel = RoomListViewModel(
roomService: mockRoomService,
sharePlayService: mockSharePlayService
)
createRoom, startSharePlay, loadContent)currentUser, isLoading, rooms)is, has, can prefixes (isActive, hasPermission, canJoin)// ✅ Good
var currentUser: User
var participantCount: Int
// ❌ Bad
var currUsr: User
var pCount: Int
// ✅ Good
enum RoomError: LocalizedError {
case roomNotFound
case insufficientPermissions
case maxParticipantsReached
var errorDescription: String? {
switch self {
case .roomNotFound:
return "Room not found"
case .insufficientPermissions:
return "Insufficient permissions to perform this action"
case .maxParticipantsReached:
return "Room has reached maximum participant capacity"
}
}
}
// ✅ Good
do {
try await service.performAction()
} catch let error as RoomError {
logger.error("Room error: \(error.localizedDescription)")
errorMessage = error.localizedDescription
} catch {
logger.error("Unexpected error: \(error.localizedDescription)")
errorMessage = "An unexpected error occurred"
}
// ❌ Bad
try? await service.performAction() // Silently fails
// ✅ Good - Single responsibility
func createRoom(name: String, host: User, activityType: RoomActivityType) async {
isLoading = true
defer { isLoading = false }
do {
let room = try await roomService.createRoom(name: name, host: host, activityType: activityType)
rooms.append(room)
await shareRoomViaSharePlay(room)
} catch {
handleRoomCreationError(error)
}
}
private func shareRoomViaSharePlay(_ room: Room) async {
logger.info("📤 SharePlay: Sharing room '\(room.name)' with participants")
await sharePlayService.shareRoom(room)
}
private func handleRoomCreationError(_ error: Error) {
logger.error("Failed to create room: \(error.localizedDescription)")
errorMessage = error.localizedDescription
}
/// Creates a new room with the specified parameters
/// - Parameters:
/// - name: The name of the room
/// - host: The user who will host the room
/// - activityType: The type of activity for this room
/// - Returns: The newly created room
/// - Throws: `RoomError.invalidName` if the name is empty
func createRoom(name: String, host: User, activityType: RoomActivityType) async throws -> Room
// ❌ Bad
// Set isLoading to true
isLoading = true
// ✅ Good - Code is self-documenting
isLoading = true
@Test("Create room adds it to the list")
func testCreateRoom() async throws {
// Arrange
let service = RoomService()
let host = User(username: "TestHost")
// Act
let room = try await service.createRoom(
name: "Test Room",
host: host,
activityType: .appleTVPlus
)
// Assert
#expect(room.name == "Test Room")
#expect(service.rooms.count == 1)
}
// ✅ Good
@Test("Joining full room throws max participants error")
@Test("Host leaving room deletes it")
@Test("SharePlay state synchronizes across platforms")
// ❌ Bad
@Test("Test 1")
@Test("Join room test")
// ✅ Good
func loadData() async {
async let rooms = roomService.fetchRooms()
async let users = userService.fetchUsers()
self.rooms = await rooms
self.users = await users
}
// ❌ Bad
func loadData() async {
self.rooms = await roomService.fetchRooms() // Sequential, slower
self.users = await userService.fetchUsers()
}
// ✅ Good - Service and ViewModel are MainActor isolated
@MainActor
protocol RoomServiceProtocol: LayoverService { }
@MainActor
final class RoomListViewModel: LayoverViewModel { }
// ✅ Good - Multiple observers supported
protocol SharePlayServiceProtocol {
func addSessionStateObserver(_ observer: @escaping (Bool) -> Void)
}
private var sessionStateObservers: [(Bool) -> Void] = []
func addSessionStateObserver(_ observer: @escaping (Bool) -> Void) {
sessionStateObservers.append(observer)
observer(isSessionActive) // Immediate callback with current state
}
// ❌ Bad - Only one observer allowed
var onSessionStateChanged: ((Bool) -> Void)?
// ✅ Good for expensive operations
lazy var expensiveResource: ExpensiveType = {
return ExpensiveType()
}()
Following these clean code principles ensures: