Merge branch 'optimize-pool-caching'

This commit is contained in:
Davide De Rosa 2019-04-11 23:38:45 +02:00
commit 257f62e136
8 changed files with 166 additions and 60 deletions

View File

@ -26,36 +26,6 @@
import UIKit
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 {
func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool)
}
@ -69,28 +39,19 @@ class ProviderPoolViewController: UIViewController {
weak var delegate: ProviderPoolViewControllerDelegate?
func setPools(_ pools: [Pool], currentPoolId: String?) {
let freeModel = PoolModel(title: L10n.Provider.Pool.Sections.Free.header)
let paidModel = PoolModel(title: L10n.Provider.Pool.Sections.Paid.header)
for p in pools {
if p.isFree ?? false {
freeModel.addPool(p)
} else {
paidModel.addPool(p)
func setModels(_ models: [PoolModel], currentPoolId: String?) {
self.models = models
// XXX: uglyyy
for m in models {
for pools in m.poolsByGroup.values {
for p in pools {
if p.id == currentPoolId {
currentPool = p
return
}
}
}
if p.id == currentPoolId {
currentPool = p
}
}
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
}
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 {
@ -173,7 +134,7 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
let model = models[indexPath.section]
let group = model.sortedGroups[indexPath.row]
let groupPools = model.poolsByGroup[group]!
guard let pool = groupPools.first else {
guard let pool = groupPools.randomElement() else {
fatalError("Empty pools in group \(group)")
}
currentPool = pool

View File

@ -152,7 +152,7 @@ class ServiceViewController: UIViewController, TableModelHost {
case .providerPoolSegueIdentifier:
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
case .endpointSegueIdentifier:
@ -289,14 +289,19 @@ class ServiceViewController: UIViewController, TableModelHost {
}
private func refreshProviderInfrastructure() {
let name = uncheckedProviderProfile.name
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()
guard let response = response else {
return
}
self.lastInfrastructureUpdate = response.1
self.tableView.reloadData()
// invalidate current pool cache
InfrastructureCache.shared.removePoolModels(for: name)
}
if !isUpdating {
hud.hide()

View File

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

View File

@ -79,6 +79,8 @@
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, ); }; };
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 */; };
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.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>"; };
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>"; };
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>"; };
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>"; };
@ -476,7 +480,9 @@
isa = PBXGroup;
children = (
0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */,
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */,
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */,
0E66A26F225FE25800F9C779 /* PoolModel.swift */,
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */,
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */,
);
@ -1005,6 +1011,7 @@
files = (
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */,
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */,
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
0E533B162258E03B00EF94FC /* PoolGroup.swift in Sources */,
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
@ -1035,6 +1042,7 @@
0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */,
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */,
0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */,
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */,
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */,
0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */,
0E3152D4223FA05400F61841 /* Preferences.swift in Sources */,

View File

@ -0,0 +1,76 @@
//
// 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
// TODO: retain max N pool models at a time (LRU)
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
}
public func pools() -> [Pool] {
return infrastructure.pools
}
private func validateEndpoint() {
guard let pool = pool, let preset = preset else {
manualAddress = nil

View File

@ -28,6 +28,8 @@ import SwiftyBeaver
private let log = SwiftyBeaver.self
// TODO: retain max N infrastructures at a time (LRU)
public class InfrastructureFactory {
private static func embedded(withName name: Infrastructure.Name) -> Infrastructure {
guard let url = name.bundleURL else {