spiegel-keyman/mac/Config/Installation/InstallationState.swift

133 lines
5.5 KiB
Swift

/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Shawn Schantz on 2026-06-01
*
* Class for tracking overall state of the installation.
* Includes functions for mapping to a Dictionary to store in the UserDefaults.
*/
import Foundation
import KeymanSettings
public class InstallationState {
// keys to the fields we are storing in the UserDefaults KMInstallationState dictionary
let kVersionKey = "version"
let kDateRestartRequestedKey = "dateRestartRequested"
let kRepairKey = "isRepair"
let kDisplayedInstallComplete = "displayedInstallComplete"
public let keymanVersion: String
public var dateRestartRequested: Date?
// indicates whether we are repairing a previous installation or doing a full installation
public let isRepair: Bool
// indicates whether the installation complete message has been displayed to the user
public var hasDisplayedInstallComplete: Bool
// this list of tasks that make up this installation
public var tasks: Set<InstallationTask>
/**
* The installation is complete if all its tasks are complete.
* Returns true if the tasks list is empty, but it should never be empty.
*/
public var isComplete: Bool {
tasks.allSatisfy(\.isComplete)
}
/**
* The installation is new if this is not a repair and no tasks are complete.
* Returns true if the tasks list is empty, but it should never be empty.
*/
public var isNew: Bool {
return !isRepair && tasks.allSatisfy{ !$0.isComplete }
}
/**
* The installation has at least one task complete and at least one remaining to complete.
* It does not matter if this is a repair or not.
*/
public var isInProgress: Bool {
return tasks.contains(where: { !$0.isComplete }) && tasks.contains(where: { $0.isComplete})
}
init(version: String, dateRestartRequested: Date? = nil, isRepair: Bool = false, hasDisplayedInstallComplete: Bool = false, tasks: Set<InstallationTask>) {
self.keymanVersion = version
self.dateRestartRequested = dateRestartRequested
self.isRepair = isRepair
self.hasDisplayedInstallComplete = hasDisplayedInstallComplete
self.tasks = tasks
}
/**
* create a new InstallationState with an existing InstallationState and a task to mark as completed
*/
public static func createCopyWithCompletedTask (from state: InstallationState, with completedTask: InstallationTaskType) -> InstallationState {
var newTaskList = state.tasks
let completedTask = InstallationTask(task: completedTask, completed: true)
newTaskList.update(with: completedTask)
return InstallationState(version: state.keymanVersion, dateRestartRequested: state.dateRestartRequested, isRepair: state.isRepair, hasDisplayedInstallComplete: state.hasDisplayedInstallComplete, tasks: newTaskList)
}
/**
* create a new InstallationState with an existing InstallationState and a task to mark as completed
*/
public static func createCopy (from state: InstallationState) -> InstallationState {
let newTaskList = state.tasks
return InstallationState(version: state.keymanVersion, dateRestartRequested: state.dateRestartRequested, isRepair: state.isRepair, hasDisplayedInstallComplete: state.hasDisplayedInstallComplete, tasks: newTaskList)
}
/**
* initialize using the dictionary from UserDefaults
*/
init?(from dictionary: Dictionary<String, Any>) {
self.keymanVersion = dictionary[kVersionKey] as? String ?? ""
self.dateRestartRequested = dictionary[kDateRestartRequestedKey] as? Date
self.isRepair = dictionary[kRepairKey] as? Bool ?? false
self.hasDisplayedInstallComplete = dictionary[kDisplayedInstallComplete] as? Bool ?? false
var installationTasks = Set<InstallationTask>()
// for every task flag found in dictionary, insert a task in the tasks array
if let taskFlag = dictionary[InstallationTaskType.prepareNewInstall.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .prepareNewInstall, completed: taskFlag))
}
if let taskFlag = dictionary[InstallationTaskType.enableInputMethod.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .enableInputMethod, completed: taskFlag))
}
if let taskFlag = dictionary[InstallationTaskType.requestAccess.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .requestAccess, completed: taskFlag))
}
if let taskFlag = dictionary[InstallationTaskType.confirmAccess.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .confirmAccess, completed: taskFlag))
}
if let taskFlag = dictionary[InstallationTaskType.requestRestart.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .requestRestart, completed: taskFlag))
}
if let taskFlag = dictionary[InstallationTaskType.confirmRestart.rawValue] as? Bool {
installationTasks.insert(InstallationTask(task: .confirmRestart, completed: taskFlag))
}
self.tasks = installationTasks
}
/**
* create a dictionary for storing the current InstallationState state in the UserDefaults
*/
public func toUserDefaultsDictionary() -> Dictionary<String, Any> {
var dictionary: Dictionary<String, Any> = [:]
dictionary[kVersionKey] = keymanVersion
if let dateRestartRequested = self.dateRestartRequested {
dictionary[kDateRestartRequestedKey] = dateRestartRequested
}
dictionary[kRepairKey] = self.isRepair
dictionary[kDisplayedInstallComplete] = self.hasDisplayedInstallComplete
for task in self.tasks {
dictionary[task.taskType.rawValue] = task.isComplete
}
return dictionary
}
}