import Foundation enum RDEPUBNavigationState: Equatable { case idle case initialLoading case restoringLocation case preparingChapter(spineIndex: Int) case presentingWindow case reconcilingFullMap case repaginating } final class RDEPUBNavigationStateMachine { private let lock = NSLock() private(set) var state: RDEPUBNavigationState = .idle func transition(to newState: RDEPUBNavigationState) { lock.lock() let oldState = state state = newState lock.unlock() #if DEBUG validateTransition(from: oldState, to: newState) #endif } #if DEBUG private func validateTransition(from oldState: RDEPUBNavigationState, to newState: RDEPUBNavigationState) { let isValid: Bool switch (oldState, newState) { case (.idle, .initialLoading), (.idle, .preparingChapter), (.idle, .restoringLocation), (.idle, .idle): isValid = true case (.initialLoading, .preparingChapter), (.initialLoading, .presentingWindow), (.initialLoading, .idle), (.initialLoading, .initialLoading): isValid = true case (.restoringLocation, .preparingChapter), (.restoringLocation, .presentingWindow), (.restoringLocation, .idle), (.restoringLocation, .restoringLocation): isValid = true case (.preparingChapter, .presentingWindow), (.preparingChapter, .preparingChapter), (.preparingChapter, .idle): isValid = true case (.presentingWindow, .idle), (.presentingWindow, .reconcilingFullMap), (.presentingWindow, .preparingChapter), (.presentingWindow, .presentingWindow), (.presentingWindow, .repaginating): isValid = true case (.reconcilingFullMap, .presentingWindow), (.reconcilingFullMap, .idle), (.reconcilingFullMap, .reconcilingFullMap): isValid = true case (.repaginating, .presentingWindow), (.repaginating, .idle), (.repaginating, .repaginating): isValid = true default: isValid = false } if !isValid { RDEPUBBackgroundTrace.log( "NavigationStateMachine", "WARNING: unexpected transition \(oldState) → \(newState)" ) } } #endif }