Upgrade API to v2 (entities)
This commit is contained in:
parent
2d40213625
commit
d9a0ebd923
|
@ -169,7 +169,7 @@ extension Infrastructure.Name {
|
|||
}
|
||||
}
|
||||
|
||||
extension Pool {
|
||||
extension PoolGroup {
|
||||
var logo: UIImage? {
|
||||
return ImageAsset(name: country.lowercased()).image
|
||||
}
|
||||
|
|
|
@ -33,19 +33,25 @@ protocol ProviderPoolViewControllerDelegate: class {
|
|||
class ProviderPoolViewController: UIViewController {
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
private var models: [PoolModel] = []
|
||||
private var categories: [PoolCategory] = []
|
||||
|
||||
private var sortedGroupsByCategory: [String: [PoolGroup]] = [:]
|
||||
|
||||
private var currentPool: Pool?
|
||||
|
||||
weak var delegate: ProviderPoolViewControllerDelegate?
|
||||
|
||||
func setModels(_ models: [PoolModel], currentPoolId: String?) {
|
||||
self.models = models
|
||||
func setInfrastructure(_ infrastructure: Infrastructure, currentPoolId: String?) {
|
||||
categories = infrastructure.categories.sorted { $0.name < $1.name }
|
||||
|
||||
for c in categories {
|
||||
sortedGroupsByCategory[c.name] = c.groups.values.sorted()
|
||||
}
|
||||
|
||||
// XXX: uglyyy
|
||||
for m in models {
|
||||
for pools in m.poolsByGroup.values {
|
||||
for p in pools {
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
for p in group.pools {
|
||||
if p.id == currentPoolId {
|
||||
currentPool = p
|
||||
return
|
||||
|
@ -78,12 +84,16 @@ class ProviderPoolViewController: UIViewController {
|
|||
|
||||
extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
private var selectedIndexPath: IndexPath? {
|
||||
for (i, model) in models.enumerated() {
|
||||
for entries in model.poolsByGroup.enumerated() {
|
||||
guard let _ = entries.element.value.firstIndex(where: { $0.id == currentPool?.id }) else {
|
||||
for (i, cat) in categories.enumerated() {
|
||||
guard let sortedGroups = sortedGroupsByCategory[cat.name] else {
|
||||
continue
|
||||
}
|
||||
for entries in cat.groups.enumerated() {
|
||||
let group = entries.element.value
|
||||
guard let _ = group.pools.firstIndex(where: { $0.id == currentPool?.id }) else {
|
||||
continue
|
||||
}
|
||||
guard let row = model.sortedGroups.firstIndex(of: entries.element.key) else {
|
||||
guard let row = sortedGroups.firstIndex(where: { $0.country == group.country && $0.area == group.area }) else {
|
||||
continue
|
||||
}
|
||||
return IndexPath(row: row, section: i)
|
||||
|
@ -93,34 +103,32 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return models.count
|
||||
return categories.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
guard models.count > 1 else {
|
||||
guard categories.count > 1 else {
|
||||
return nil
|
||||
}
|
||||
let model = models[section]
|
||||
return model.isFree ? L10n.Provider.Pool.Sections.Free.header : L10n.Provider.Pool.Sections.Paid.header
|
||||
let model = categories[section]
|
||||
return model.name
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
let model = models[section]
|
||||
return model.sortedGroups.count
|
||||
let model = categories[section]
|
||||
return model.groups.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.first else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard let pool = group.pools.first else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
|
||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||
cell.imageView?.image = pool.logo
|
||||
cell.imageView?.image = group.logo
|
||||
cell.leftText = pool.localizedCountry
|
||||
if groupPools.count > 1 {
|
||||
if group.pools.count > 1 {
|
||||
cell.rightText = pool.area?.uppercased()
|
||||
cell.accessoryType = .detailDisclosureButton // no checkmark!
|
||||
} else {
|
||||
|
@ -131,10 +139,8 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.randomElement() else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard let pool = group.pools.randomElement() else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
currentPool = pool
|
||||
|
@ -142,25 +148,20 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.first else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
guard groupPools.count > 1 else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard group.pools.count > 1 else {
|
||||
return
|
||||
}
|
||||
let vc = OptionViewController<Pool>()
|
||||
vc.title = pool.localizedCountry
|
||||
vc.options = groupPools.sorted {
|
||||
guard let lnum = $0.num, let ln = Int(lnum) else {
|
||||
vc.title = group.localizedCountry
|
||||
vc.options = group.pools.sorted {
|
||||
guard let lnum = $0.num else {
|
||||
return true
|
||||
}
|
||||
guard let rnum = $1.num, let rn = Int(rnum) else {
|
||||
guard let rnum = $1.num else {
|
||||
return false
|
||||
}
|
||||
return ln < rn
|
||||
return lnum < rnum
|
||||
}
|
||||
vc.selectedOption = currentPool
|
||||
vc.descriptionBlock = { $0.areaId ?? "" } // XXX: fail gracefully
|
||||
|
@ -170,4 +171,12 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
private func poolGroup(at indexPath: IndexPath) -> PoolGroup {
|
||||
let model = categories[indexPath.section]
|
||||
guard let sortedGroups = sortedGroupsByCategory[model.name] else {
|
||||
fatalError("Missing sorted groups for category '\(model.name)'")
|
||||
}
|
||||
return sortedGroups[indexPath.row]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||
|
||||
case .providerPoolSegueIdentifier:
|
||||
let vc = destination as? ProviderPoolViewController
|
||||
vc?.setModels(InfrastructureCache.shared.poolModels(for: uncheckedProviderProfile), currentPoolId: uncheckedProviderProfile.poolId)
|
||||
vc?.setInfrastructure(uncheckedProviderProfile.infrastructure, currentPoolId: uncheckedProviderProfile.poolId)
|
||||
vc?.delegate = self
|
||||
|
||||
case .endpointSegueIdentifier:
|
||||
|
@ -299,9 +299,6 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||
}
|
||||
self.lastInfrastructureUpdate = response.1
|
||||
self.tableView.reloadData()
|
||||
|
||||
// invalidate current pool cache
|
||||
InfrastructureCache.shared.removePoolModels(for: name)
|
||||
}
|
||||
if !isUpdating {
|
||||
hud.hide()
|
||||
|
|
|
@ -89,7 +89,7 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
|
|||
guard let provider = selectedProfile as? ProviderConnectionProfile else {
|
||||
return
|
||||
}
|
||||
vc.setModels(InfrastructureCache.shared.poolModels(for: provider), currentPoolId: nil)
|
||||
vc.setInfrastructure(provider.infrastructure, currentPoolId: nil)
|
||||
vc.delegate = self
|
||||
}
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@
|
|||
0E57F64620C83FC7008323CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64420C83FC7008323CF /* LaunchScreen.storyboard */; };
|
||||
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, ); }; };
|
||||
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolModel.swift */; };
|
||||
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */; };
|
||||
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolCategory.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 */; };
|
||||
|
@ -223,8 +222,7 @@
|
|||
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>"; };
|
||||
0E66A26F225FE25800F9C779 /* PoolCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PoolCategory.swift; path = ../Model/Profiles/PoolCategory.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>"; };
|
||||
|
@ -484,9 +482,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */,
|
||||
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */,
|
||||
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */,
|
||||
0E66A26F225FE25800F9C779 /* PoolModel.swift */,
|
||||
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */,
|
||||
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */,
|
||||
);
|
||||
|
@ -529,6 +525,7 @@
|
|||
0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */,
|
||||
0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */,
|
||||
0ED31C0F20CF09A30027975F /* Pool.swift */,
|
||||
0E66A26F225FE25800F9C779 /* PoolCategory.swift */,
|
||||
0E533B152258E03B00EF94FC /* PoolGroup.swift */,
|
||||
0E39BCEF214B9EF10035E9DE /* WebServices.swift */,
|
||||
);
|
||||
|
@ -1016,7 +1013,7 @@
|
|||
files = (
|
||||
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
|
||||
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */,
|
||||
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */,
|
||||
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */,
|
||||
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
|
||||
0E533B162258E03B00EF94FC /* PoolGroup.swift in Sources */,
|
||||
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
|
||||
|
@ -1047,7 +1044,6 @@
|
|||
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 */,
|
||||
|
|
|
@ -153,9 +153,6 @@
|
|||
"account.cells.open_guide.caption" = "See your credentials";
|
||||
"account.cells.signup.caption" = "Register with %@";
|
||||
|
||||
"provider.pool.sections.free.header" = "Free";
|
||||
"provider.pool.sections.paid.header" = "Paid";
|
||||
|
||||
"endpoint.sections.location_addresses.header" = "Addresses";
|
||||
"endpoint.sections.location_protocols.header" = "Protocols";
|
||||
"endpoint.cells.any_address.caption" = "Automatic";
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// PoolModel.swift
|
||||
// PoolCategory.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 4/11/19.
|
||||
|
@ -25,34 +25,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class PoolModel {
|
||||
public let isFree: Bool
|
||||
public struct PoolCategory: Codable {
|
||||
public let name: String
|
||||
|
||||
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()
|
||||
}
|
||||
public let groups: [String: PoolGroup]
|
||||
}
|
|
@ -53,7 +53,7 @@ public struct Infrastructure: Codable {
|
|||
|
||||
public let name: Name
|
||||
|
||||
public let pools: [Pool]
|
||||
public let categories: [PoolCategory]
|
||||
|
||||
public let presets: [InfrastructurePreset]
|
||||
|
||||
|
@ -69,11 +69,27 @@ public struct Infrastructure: Codable {
|
|||
}
|
||||
|
||||
public func pool(for identifier: String) -> Pool? {
|
||||
return pools.first { $0.id == identifier }
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
guard let found = group.pools.first(where: { $0.id == identifier }) else {
|
||||
continue
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func pool(withPrefix prefix: String) -> Pool? {
|
||||
return pools.first { $0.id.hasPrefix(prefix) }
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
guard let found = group.pools.first(where: { $0.id.hasPrefix(prefix) }) else {
|
||||
continue
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func preset(for identifier: String) -> InfrastructurePreset? {
|
||||
|
|
|
@ -26,17 +26,17 @@
|
|||
import Foundation
|
||||
import TunnelKit
|
||||
|
||||
public struct Pool: Codable, Hashable, CustomStringConvertible {
|
||||
public struct Pool: Codable, Hashable {
|
||||
public enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
|
||||
case country
|
||||
|
||||
|
||||
case area
|
||||
|
||||
|
||||
case num
|
||||
|
||||
case isFree = "free"
|
||||
case tags
|
||||
|
||||
// case location
|
||||
|
||||
|
@ -48,26 +48,12 @@ public struct Pool: Codable, Hashable, CustomStringConvertible {
|
|||
public let id: String
|
||||
|
||||
public let country: String
|
||||
|
||||
|
||||
public let area: String?
|
||||
|
||||
public let num: Int?
|
||||
|
||||
public let num: String?
|
||||
|
||||
public var areaId: String? {
|
||||
let id: String
|
||||
if let area = area, let num = num {
|
||||
id = "\(area) #\(num)"
|
||||
} else if let area = area {
|
||||
id = area
|
||||
} else if let num = num {
|
||||
id = "#\(num)"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return id.uppercased()
|
||||
}
|
||||
|
||||
public let isFree: Bool?
|
||||
public let tags: [String]?
|
||||
|
||||
// public let location: (Double, Double)
|
||||
|
||||
|
@ -94,21 +80,11 @@ public struct Pool: Codable, Hashable, CustomStringConvertible {
|
|||
return addrs
|
||||
}
|
||||
|
||||
public func group() -> PoolGroup {
|
||||
return PoolGroup(country: country, area: area)
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
id.hash(into: &hasher)
|
||||
}
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
public var description: String {
|
||||
return "{[\(id)] \"\(localizedCountry)\"}"
|
||||
}
|
||||
}
|
||||
|
||||
extension Pool {
|
||||
|
@ -132,4 +108,18 @@ extension Pool {
|
|||
}
|
||||
return String.init(format: Pool.localizedFormat, countryString, zone.uppercased())
|
||||
}
|
||||
|
||||
public var areaId: String? {
|
||||
let id: String
|
||||
if let area = area, let num = num {
|
||||
id = "\(area) #\(num)"
|
||||
} else if let area = area {
|
||||
id = area
|
||||
} else if let num = num {
|
||||
id = "#\(num)"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return id.uppercased()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,31 +25,27 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct PoolGroup: Hashable, Comparable, CustomStringConvertible {
|
||||
public struct PoolGroup: Codable, Hashable, Comparable, CustomStringConvertible {
|
||||
public let country: String
|
||||
|
||||
public let area: String?
|
||||
|
||||
private let id: String
|
||||
public let pools: [Pool]
|
||||
|
||||
private let localizedId: String
|
||||
|
||||
public init(country: String, area: String?) {
|
||||
self.country = country
|
||||
self.area = area
|
||||
|
||||
private var id: String {
|
||||
var id = country
|
||||
var localizedId = Utils.localizedCountry(country)
|
||||
if let area = area {
|
||||
id += area
|
||||
localizedId += area
|
||||
}
|
||||
self.id = id
|
||||
self.localizedId = localizedId
|
||||
return id
|
||||
}
|
||||
|
||||
public func contains(_ pool: Pool) -> Bool {
|
||||
return (pool.country == country) && (pool.area == area)
|
||||
private var localizedId: String {
|
||||
var localizedId = Utils.localizedCountry(country)
|
||||
if let area = area {
|
||||
localizedId += area
|
||||
}
|
||||
return localizedId
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
@ -70,3 +66,9 @@ public struct PoolGroup: Hashable, Comparable, CustomStringConvertible {
|
|||
return "{\(country), \(area ?? "--")}"
|
||||
}
|
||||
}
|
||||
|
||||
extension PoolGroup {
|
||||
public var localizedCountry: String {
|
||||
return Utils.localizedCountry(country)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue