Cache pool models, do not rebuild each time

Invalidate on new infrastructure.
This commit is contained in:
Davide De Rosa 2019-04-11 23:14:18 +02:00
parent d264c0089d
commit cf8f0984a3
7 changed files with 161 additions and 59 deletions

View File

@ -26,36 +26,6 @@
import UIKit import UIKit
import Passepartout_Core import Passepartout_Core
private class PoolModel {
let title: String
var poolsByGroup: [PoolGroup: [Pool]] = [:]
private(set) var sortedGroups: [PoolGroup] = []
var isEmpty: Bool {
return sortedGroups.isEmpty
}
init(title: String) {
self.title = title
}
func addPool(_ p: Pool) {
let group = p.group()
if var existingPools = poolsByGroup[group] {
existingPools.append(p)
poolsByGroup[group] = existingPools
} else {
poolsByGroup[group] = [p]
}
}
func sort() {
sortedGroups = poolsByGroup.keys.sorted()
}
}
protocol ProviderPoolViewControllerDelegate: class { protocol ProviderPoolViewControllerDelegate: class {
func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool) func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool)
} }
@ -69,28 +39,19 @@ class ProviderPoolViewController: UIViewController {
weak var delegate: ProviderPoolViewControllerDelegate? weak var delegate: ProviderPoolViewControllerDelegate?
func setPools(_ pools: [Pool], currentPoolId: String?) { func setModels(_ models: [PoolModel], currentPoolId: String?) {
let freeModel = PoolModel(title: L10n.Provider.Pool.Sections.Free.header) self.models = models
let paidModel = PoolModel(title: L10n.Provider.Pool.Sections.Paid.header)
// XXX: uglyyy
for m in models {
for pools in m.poolsByGroup.values {
for p in pools { for p in pools {
if p.isFree ?? false {
freeModel.addPool(p)
} else {
paidModel.addPool(p)
}
if p.id == currentPoolId { if p.id == currentPoolId {
currentPool = p currentPool = p
return
} }
} }
freeModel.sort()
paidModel.sort()
models = []
if !freeModel.isEmpty {
models.append(freeModel)
} }
if !paidModel.isEmpty {
models.append(paidModel)
} }
} }
@ -140,7 +101,7 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
return nil return nil
} }
let model = models[section] let model = models[section]
return model.title return model.isFree ? L10n.Provider.Pool.Sections.Free.header : L10n.Provider.Pool.Sections.Paid.header
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

View File

@ -152,7 +152,7 @@ class ServiceViewController: UIViewController, TableModelHost {
case .providerPoolSegueIdentifier: case .providerPoolSegueIdentifier:
let vc = destination as? ProviderPoolViewController let vc = destination as? ProviderPoolViewController
vc?.setPools(uncheckedProviderProfile.pools(), currentPoolId: uncheckedProviderProfile.poolId) vc?.setModels(InfrastructureCache.shared.poolModels(for: uncheckedProviderProfile), currentPoolId: uncheckedProviderProfile.poolId)
vc?.delegate = self vc?.delegate = self
case .endpointSegueIdentifier: case .endpointSegueIdentifier:
@ -289,14 +289,19 @@ class ServiceViewController: UIViewController, TableModelHost {
} }
private func refreshProviderInfrastructure() { private func refreshProviderInfrastructure() {
let name = uncheckedProviderProfile.name
let hud = HUD() let hud = HUD()
let isUpdating = InfrastructureFactory.shared.update(uncheckedProviderProfile.name, notBeforeInterval: AppConstants.Web.minimumUpdateInterval) { (response, error) in let isUpdating = InfrastructureFactory.shared.update(name, notBeforeInterval: AppConstants.Web.minimumUpdateInterval) { (response, error) in
hud.hide() hud.hide()
guard let response = response else { guard let response = response else {
return return
} }
self.lastInfrastructureUpdate = response.1 self.lastInfrastructureUpdate = response.1
self.tableView.reloadData() self.tableView.reloadData()
// invalidate current pool cache
InfrastructureCache.shared.removePoolModels(for: name)
} }
if !isUpdating { if !isUpdating {
hud.hide() hud.hide()

View File

@ -89,7 +89,7 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
guard let provider = selectedProfile as? ProviderConnectionProfile else { guard let provider = selectedProfile as? ProviderConnectionProfile else {
return return
} }
vc.setPools(provider.pools(), currentPoolId: nil) vc.setModels(InfrastructureCache.shared.poolModels(for: provider), currentPoolId: nil)
vc.delegate = self vc.delegate = self
} }

View File

@ -79,6 +79,8 @@
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; }; 0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; };
0E58BF65224152F9006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; 0E58BF65224152F9006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
0E58BF68224305A8006FB157 /* Countries.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E58BF6A224305A8006FB157 /* Countries.strings */; }; 0E58BF68224305A8006FB157 /* Countries.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E58BF6A224305A8006FB157 /* Countries.strings */; };
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolModel.swift */; };
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */; };
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; }; 0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; };
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; }; 0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; };
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; }; 0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
@ -217,6 +219,8 @@
0E5E5DDE215119AF00E318A3 /* VPNStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNStatus.swift; sourceTree = "<group>"; }; 0E5E5DDE215119AF00E318A3 /* VPNStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNStatus.swift; sourceTree = "<group>"; };
0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConfiguration.swift; sourceTree = "<group>"; }; 0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConfiguration.swift; sourceTree = "<group>"; };
0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GracefulVPN.swift; sourceTree = "<group>"; }; 0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GracefulVPN.swift; sourceTree = "<group>"; };
0E66A26F225FE25800F9C779 /* PoolModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoolModel.swift; sourceTree = "<group>"; };
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructureCache.swift; sourceTree = "<group>"; };
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = "<group>"; }; 0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = "<group>"; };
0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogViewController.swift; sourceTree = "<group>"; }; 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogViewController.swift; sourceTree = "<group>"; };
0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; }; 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; };
@ -476,7 +480,9 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */, 0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */,
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */,
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */, 0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */,
0E66A26F225FE25800F9C779 /* PoolModel.swift */,
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */, 0E79D14021919F5600BB5FB2 /* ProfileKey.swift */,
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */, 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */,
); );
@ -1005,6 +1011,7 @@
files = ( files = (
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */, 0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */, 0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */,
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */,
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */, 0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
0E533B162258E03B00EF94FC /* PoolGroup.swift in Sources */, 0E533B162258E03B00EF94FC /* PoolGroup.swift in Sources */,
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */, 0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
@ -1035,6 +1042,7 @@
0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */, 0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */,
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */, 0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */,
0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */, 0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */,
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */,
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */, 0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */,
0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */, 0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */,
0E3152D4223FA05400F61841 /* Preferences.swift in Sources */, 0E3152D4223FA05400F61841 /* Preferences.swift in Sources */,

View File

@ -0,0 +1,74 @@
//
// InfrastructureCache.swift
// Passepartout
//
// Created by Davide De Rosa on 4/11/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public class InfrastructureCache {
public static let shared = InfrastructureCache()
private var poolModelsByName: [Infrastructure.Name: [PoolModel]]
private init() {
poolModelsByName = [:]
}
public func poolModels(for provider: ProviderConnectionProfile) -> [PoolModel] {
if let models = poolModelsByName[provider.name] {
return models
}
let freeModel = PoolModel(isFree: true)
let paidModel = PoolModel(isFree: false)
for p in provider.infrastructure.pools {
if p.isFree ?? false {
freeModel.addPool(p)
} else {
paidModel.addPool(p)
}
// if p.id == currentPoolId {
// currentPool = p
// }
}
freeModel.sort()
paidModel.sort()
var models: [PoolModel] = []
if !freeModel.isEmpty {
models.append(freeModel)
}
if !paidModel.isEmpty {
models.append(paidModel)
}
poolModelsByName[provider.name] = models
return models
}
public func removePoolModels(for name: Infrastructure.Name? = nil) {
if let name = name {
poolModelsByName.removeValue(forKey: name)
return
}
poolModelsByName.removeAll()
}
}

View File

@ -0,0 +1,58 @@
//
// PoolModel.swift
// Passepartout
//
// Created by Davide De Rosa on 4/11/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public class PoolModel {
public let isFree: Bool
public var poolsByGroup: [PoolGroup: [Pool]]
public private(set) var sortedGroups: [PoolGroup]
public init(isFree: Bool) {
self.isFree = isFree
poolsByGroup = [:]
sortedGroups = []
}
public var isEmpty: Bool {
return sortedGroups.isEmpty
}
public func addPool(_ p: Pool) {
let group = p.group()
if var existingPools = poolsByGroup[group] {
existingPools.append(p)
poolsByGroup[group] = existingPools
} else {
poolsByGroup[group] = [p]
}
}
public func sort() {
sortedGroups = poolsByGroup.keys.sorted()
}
}

View File

@ -72,10 +72,6 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
presetId = infrastructure.defaults.preset presetId = infrastructure.defaults.preset
} }
public func pools() -> [Pool] {
return infrastructure.pools
}
private func validateEndpoint() { private func validateEndpoint() {
guard let pool = pool, let preset = preset else { guard let pool = pool, let preset = preset else {
manualAddress = nil manualAddress = nil