Merge branch 'optimize-serialization'
This commit is contained in:
commit
fafc34180f
|
@ -57,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
func applicationWillResignActive(_ application: UIApplication) {
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||||
TransientStore.shared.serialize() // synchronize
|
TransientStore.shared.serialize(withProfiles: true) // synchronize
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||||
|
|
|
@ -421,7 +421,7 @@ extension OrganizerViewController {
|
||||||
|
|
||||||
extension OrganizerViewController: ConnectionServiceDelegate {
|
extension OrganizerViewController: ConnectionServiceDelegate {
|
||||||
func connectionService(didAdd profile: ConnectionProfile) {
|
func connectionService(didAdd profile: ConnectionProfile) {
|
||||||
TransientStore.shared.serialize() // add
|
TransientStore.shared.serialize(withProfiles: false) // add
|
||||||
|
|
||||||
reloadModel()
|
reloadModel()
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
|
@ -444,14 +444,14 @@ extension OrganizerViewController: ConnectionServiceDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectionService(didRename oldProfile: ConnectionProfile, to newProfile: ConnectionProfile) {
|
func connectionService(didRename oldProfile: ConnectionProfile, to newProfile: ConnectionProfile) {
|
||||||
TransientStore.shared.serialize() // rename
|
TransientStore.shared.serialize(withProfiles: false) // rename
|
||||||
|
|
||||||
reloadModel()
|
reloadModel()
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectionService(didRemoveProfileWithKey key: ConnectionService.ProfileKey) {
|
func connectionService(didRemoveProfileWithKey key: ConnectionService.ProfileKey) {
|
||||||
TransientStore.shared.serialize() // delete
|
TransientStore.shared.serialize(withProfiles: false) // delete
|
||||||
|
|
||||||
splitViewController?.serviceViewController?.hideProfileIfDeleted()
|
splitViewController?.serviceViewController?.hideProfileIfDeleted()
|
||||||
}
|
}
|
||||||
|
@ -459,13 +459,13 @@ extension OrganizerViewController: ConnectionServiceDelegate {
|
||||||
// XXX: deactivate + activate leads to a redundant serialization
|
// XXX: deactivate + activate leads to a redundant serialization
|
||||||
|
|
||||||
func connectionService(willDeactivate profile: ConnectionProfile) {
|
func connectionService(willDeactivate profile: ConnectionProfile) {
|
||||||
TransientStore.shared.serialize() // deactivate
|
TransientStore.shared.serialize(withProfiles: false) // deactivate
|
||||||
|
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectionService(didActivate profile: ConnectionProfile) {
|
func connectionService(didActivate profile: ConnectionProfile) {
|
||||||
TransientStore.shared.serialize() // activate
|
TransientStore.shared.serialize(withProfiles: false) // activate
|
||||||
|
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,8 +115,6 @@ class ConnectionService: Codable {
|
||||||
|
|
||||||
private var cache: [ProfileKey: ConnectionProfile]
|
private var cache: [ProfileKey: ConnectionProfile]
|
||||||
|
|
||||||
private var pendingRemoval: Set<ProfileKey>
|
|
||||||
|
|
||||||
private(set) var activeProfileKey: ProfileKey? {
|
private(set) var activeProfileKey: ProfileKey? {
|
||||||
willSet {
|
willSet {
|
||||||
if let oldProfile = activeProfile {
|
if let oldProfile = activeProfile {
|
||||||
|
@ -160,7 +158,6 @@ class ConnectionService: Codable {
|
||||||
preferences = EditablePreferences()
|
preferences = EditablePreferences()
|
||||||
|
|
||||||
cache = [:]
|
cache = [:]
|
||||||
pendingRemoval = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
@ -181,7 +178,6 @@ class ConnectionService: Codable {
|
||||||
preferences = try container.decode(EditablePreferences.self, forKey: .preferences)
|
preferences = try container.decode(EditablePreferences.self, forKey: .preferences)
|
||||||
|
|
||||||
cache = [:]
|
cache = [:]
|
||||||
pendingRemoval = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
func encode(to encoder: Encoder) throws {
|
||||||
|
@ -232,42 +228,42 @@ class ConnectionService: Codable {
|
||||||
|
|
||||||
func saveProfiles() {
|
func saveProfiles() {
|
||||||
let encoder = JSONEncoder()
|
let encoder = JSONEncoder()
|
||||||
|
ensureDirectoriesExistence()
|
||||||
|
|
||||||
|
for profile in cache.values {
|
||||||
|
saveProfile(profile, withEncoder: encoder, checkDirectories: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func ensureDirectoriesExistence() {
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
try? fm.createDirectory(at: providersURL, withIntermediateDirectories: false, attributes: nil)
|
try? fm.createDirectory(at: providersURL, withIntermediateDirectories: false, attributes: nil)
|
||||||
try? fm.createDirectory(at: hostsURL, withIntermediateDirectories: false, attributes: nil)
|
try? fm.createDirectory(at: hostsURL, withIntermediateDirectories: false, attributes: nil)
|
||||||
|
}
|
||||||
for key in pendingRemoval {
|
|
||||||
let url = profileURL(key)
|
private func saveProfile(_ profile: ConnectionProfile, withEncoder encoder: JSONEncoder, checkDirectories: Bool) {
|
||||||
try? fm.removeItem(at: url)
|
if checkDirectories {
|
||||||
if let cfg = configurationURL(for: key) {
|
ensureDirectoriesExistence()
|
||||||
try? fm.removeItem(at: cfg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for entry in cache.values {
|
do {
|
||||||
if let profile = entry as? ProviderConnectionProfile {
|
let url = profileURL(ProfileKey(profile))
|
||||||
do {
|
var optData: Data?
|
||||||
let url = profileURL(ProfileKey(.provider, entry.id))
|
if let providerProfile = profile as? ProviderConnectionProfile {
|
||||||
let data = try encoder.encode(profile)
|
optData = try encoder.encode(providerProfile)
|
||||||
try data.write(to: url)
|
} else if let hostProfile = profile as? HostConnectionProfile {
|
||||||
log.debug("Saved provider '\(profile.id)'")
|
optData = try encoder.encode(hostProfile)
|
||||||
} catch let e {
|
} else if let placeholder = profile as? PlaceholderConnectionProfile {
|
||||||
log.error("Could not save provider '\(profile.id)': \(e)")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if let profile = entry as? HostConnectionProfile {
|
|
||||||
do {
|
|
||||||
let url = profileURL(ProfileKey(.host, entry.id))
|
|
||||||
let data = try encoder.encode(profile)
|
|
||||||
try data.write(to: url)
|
|
||||||
log.debug("Saved host '\(profile.id)'")
|
|
||||||
} catch let e {
|
|
||||||
log.error("Could not save host '\(profile.id)': \(e)")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if let placeholder = entry as? PlaceholderConnectionProfile {
|
|
||||||
log.debug("Skipped \(placeholder.context) '\(placeholder.id)'")
|
log.debug("Skipped \(placeholder.context) '\(placeholder.id)'")
|
||||||
|
} else {
|
||||||
|
fatalError("Attempting to add an unhandled profile type: \(type(of: profile))")
|
||||||
}
|
}
|
||||||
|
guard let data = optData else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try data.write(to: url)
|
||||||
|
log.debug("Serialized \(profile.context) profile '\(profile.id)'")
|
||||||
|
} catch let e {
|
||||||
|
log.warning("Could not serialize \(profile.context) profile '\(profile.id)': \(e)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,13 +332,14 @@ class ConnectionService: Codable {
|
||||||
func addOrReplaceProfile(_ profile: ConnectionProfile, credentials: Credentials?) {
|
func addOrReplaceProfile(_ profile: ConnectionProfile, credentials: Credentials?) {
|
||||||
let key = ProfileKey(profile)
|
let key = ProfileKey(profile)
|
||||||
cache[key] = profile
|
cache[key] = profile
|
||||||
pendingRemoval.remove(key)
|
|
||||||
try? setCredentials(credentials, for: profile)
|
try? setCredentials(credentials, for: profile)
|
||||||
if cache.count == 1 {
|
if cache.count == 1 {
|
||||||
activeProfileKey = key
|
activeProfileKey = key
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate?.connectionService(didAdd: profile)
|
delegate?.connectionService(didAdd: profile)
|
||||||
|
|
||||||
|
// serialization (can fail)
|
||||||
|
saveProfile(profile, withEncoder: JSONEncoder(), checkDirectories: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult func renameProfile(_ key: ProfileKey, to newId: String) -> ConnectionProfile? {
|
@discardableResult func renameProfile(_ key: ProfileKey, to newId: String) -> ConnectionProfile? {
|
||||||
|
@ -392,14 +389,27 @@ class ConnectionService: Codable {
|
||||||
guard let profile = cache[key] else {
|
guard let profile = cache[key] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.removeValue(forKey: key)
|
cache.removeValue(forKey: key)
|
||||||
removeCredentials(for: profile)
|
removeCredentials(for: profile)
|
||||||
pendingRemoval.insert(key)
|
|
||||||
if cache.isEmpty {
|
if cache.isEmpty {
|
||||||
activeProfileKey = nil
|
activeProfileKey = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate?.connectionService(didRemoveProfileWithKey: key)
|
delegate?.connectionService(didRemoveProfileWithKey: key)
|
||||||
|
|
||||||
|
// serialization (can fail)
|
||||||
|
do {
|
||||||
|
let fm = FileManager.default
|
||||||
|
if let cfg = configurationURL(for: key) {
|
||||||
|
try? fm.removeItem(at: cfg)
|
||||||
|
}
|
||||||
|
let url = profileURL(key)
|
||||||
|
try fm.removeItem(at: url)
|
||||||
|
log.debug("Deleted removed profile '\(profile.id)'")
|
||||||
|
} catch let e {
|
||||||
|
log.warning("Could not delete profile '\(profile.id)': \(e)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsProfile(_ key: ProfileKey) -> Bool {
|
func containsProfile(_ key: ProfileKey) -> Bool {
|
||||||
|
|
|
@ -77,8 +77,10 @@ class TransientStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialize() {
|
func serialize(withProfiles: Bool) {
|
||||||
try? JSONEncoder().encode(service).write(to: TransientStore.serviceURL)
|
try? JSONEncoder().encode(service).write(to: TransientStore.serviceURL)
|
||||||
service.saveProfiles()
|
if withProfiles {
|
||||||
|
service.saveProfiles()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue