Merge branch 'optimize-serialization'

This commit is contained in:
Davide De Rosa 2018-11-04 15:14:57 +01:00
commit fafc34180f
4 changed files with 56 additions and 44 deletions

View File

@ -57,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
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.
// 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) {

View File

@ -421,7 +421,7 @@ extension OrganizerViewController {
extension OrganizerViewController: ConnectionServiceDelegate {
func connectionService(didAdd profile: ConnectionProfile) {
TransientStore.shared.serialize() // add
TransientStore.shared.serialize(withProfiles: false) // add
reloadModel()
tableView.reloadData()
@ -444,14 +444,14 @@ extension OrganizerViewController: ConnectionServiceDelegate {
}
func connectionService(didRename oldProfile: ConnectionProfile, to newProfile: ConnectionProfile) {
TransientStore.shared.serialize() // rename
TransientStore.shared.serialize(withProfiles: false) // rename
reloadModel()
tableView.reloadData()
}
func connectionService(didRemoveProfileWithKey key: ConnectionService.ProfileKey) {
TransientStore.shared.serialize() // delete
TransientStore.shared.serialize(withProfiles: false) // delete
splitViewController?.serviceViewController?.hideProfileIfDeleted()
}
@ -459,13 +459,13 @@ extension OrganizerViewController: ConnectionServiceDelegate {
// XXX: deactivate + activate leads to a redundant serialization
func connectionService(willDeactivate profile: ConnectionProfile) {
TransientStore.shared.serialize() // deactivate
TransientStore.shared.serialize(withProfiles: false) // deactivate
tableView.reloadData()
}
func connectionService(didActivate profile: ConnectionProfile) {
TransientStore.shared.serialize() // activate
TransientStore.shared.serialize(withProfiles: false) // activate
tableView.reloadData()
}

View File

@ -115,8 +115,6 @@ class ConnectionService: Codable {
private var cache: [ProfileKey: ConnectionProfile]
private var pendingRemoval: Set<ProfileKey>
private(set) var activeProfileKey: ProfileKey? {
willSet {
if let oldProfile = activeProfile {
@ -160,7 +158,6 @@ class ConnectionService: Codable {
preferences = EditablePreferences()
cache = [:]
pendingRemoval = []
}
// MARK: Codable
@ -181,7 +178,6 @@ class ConnectionService: Codable {
preferences = try container.decode(EditablePreferences.self, forKey: .preferences)
cache = [:]
pendingRemoval = []
}
func encode(to encoder: Encoder) throws {
@ -232,42 +228,42 @@ class ConnectionService: Codable {
func saveProfiles() {
let encoder = JSONEncoder()
ensureDirectoriesExistence()
for profile in cache.values {
saveProfile(profile, withEncoder: encoder, checkDirectories: false)
}
}
private func ensureDirectoriesExistence() {
let fm = FileManager.default
try? fm.createDirectory(at: providersURL, withIntermediateDirectories: false, attributes: nil)
try? fm.createDirectory(at: hostsURL, withIntermediateDirectories: false, attributes: nil)
for key in pendingRemoval {
let url = profileURL(key)
try? fm.removeItem(at: url)
if let cfg = configurationURL(for: key) {
try? fm.removeItem(at: cfg)
}
}
private func saveProfile(_ profile: ConnectionProfile, withEncoder encoder: JSONEncoder, checkDirectories: Bool) {
if checkDirectories {
ensureDirectoriesExistence()
}
for entry in cache.values {
if let profile = entry as? ProviderConnectionProfile {
do {
let url = profileURL(ProfileKey(.provider, entry.id))
let data = try encoder.encode(profile)
try data.write(to: url)
log.debug("Saved provider '\(profile.id)'")
} catch let e {
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 {
do {
let url = profileURL(ProfileKey(profile))
var optData: Data?
if let providerProfile = profile as? ProviderConnectionProfile {
optData = try encoder.encode(providerProfile)
} else if let hostProfile = profile as? HostConnectionProfile {
optData = try encoder.encode(hostProfile)
} else if let placeholder = profile as? PlaceholderConnectionProfile {
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?) {
let key = ProfileKey(profile)
cache[key] = profile
pendingRemoval.remove(key)
try? setCredentials(credentials, for: profile)
if cache.count == 1 {
activeProfileKey = key
}
delegate?.connectionService(didAdd: profile)
// serialization (can fail)
saveProfile(profile, withEncoder: JSONEncoder(), checkDirectories: true)
}
@discardableResult func renameProfile(_ key: ProfileKey, to newId: String) -> ConnectionProfile? {
@ -392,14 +389,27 @@ class ConnectionService: Codable {
guard let profile = cache[key] else {
return
}
cache.removeValue(forKey: key)
removeCredentials(for: profile)
pendingRemoval.insert(key)
if cache.isEmpty {
activeProfileKey = nil
}
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 {

View File

@ -77,8 +77,10 @@ class TransientStore {
}
}
func serialize() {
func serialize(withProfiles: Bool) {
try? JSONEncoder().encode(service).write(to: TransientStore.serviceURL)
service.saveProfiles()
if withProfiles {
service.saveProfiles()
}
}
}