From 6ba68b7f9a995e912989d456a9e489256aed7f5d Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sat, 30 Mar 2019 18:54:21 +0100 Subject: [PATCH 1/5] Upgrade TunnelKit for data count updates --- Podfile | 4 ++-- Podfile.lock | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Podfile b/Podfile index dd1dc6c1..adbe781b 100644 --- a/Podfile +++ b/Podfile @@ -5,8 +5,8 @@ use_frameworks! def shared_pods #pod 'TunnelKit', '~> 1.5.0' #pod 'TunnelKit/LZO', '~> 1.5.0' - pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'd03f1bd' - pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'd03f1bd' + pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => '44fb5a5' + pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => '44fb5a5' #pod 'TunnelKit', :path => '../../personal/tunnelkit' #pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit' end diff --git a/Podfile.lock b/Podfile.lock index c95590cc..4cc618bf 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,8 +15,8 @@ PODS: DEPENDENCIES: - MBProgressHUD - - TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `d03f1bd`) - - TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `d03f1bd`) + - TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `44fb5a5`) + - TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `44fb5a5`) SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -26,12 +26,12 @@ SPEC REPOS: EXTERNAL SOURCES: TunnelKit: - :commit: d03f1bd + :commit: 44fb5a5 :git: https://github.com/keeshux/tunnelkit CHECKOUT OPTIONS: TunnelKit: - :commit: d03f1bd + :commit: 44fb5a5 :git: https://github.com/keeshux/tunnelkit SPEC CHECKSUMS: @@ -40,6 +40,6 @@ SPEC CHECKSUMS: SwiftyBeaver: 8e67ab3cd94389cbbb7a9c7cc02748d98bfee68e TunnelKit: 75323e2f45e698647ccaebd5a6bcae8c002afea4 -PODFILE CHECKSUM: a2c0a287a69e21e328e35816f2c813616b80f54c +PODFILE CHECKSUM: 871b647058e72fb7ae3c666abb35d2683806455d COCOAPODS: 1.6.1 From bc0a0d40dc8248ac5badb7e24e58dcf8bfcf0eed Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 29 Mar 2019 11:17:50 +0100 Subject: [PATCH 2/5] Observe tunnel data count periodically (5s) Use Timer as KVO is not possible on App Group defaults. Be tolerant about missing sections, return type is optional. Also reword data count cell caption. --- .../PacketTunnelProvider.swift | 1 + .../Scenes/ServiceViewController.swift | 92 +++++++++++++------ Passepartout-iOS/Tables/TableModel.swift | 2 +- .../Resources/en.lproj/Localizable.strings | 2 +- Passepartout/Sources/GroupConstants.swift | 2 + .../Sources/Model/ConnectionService.swift | 44 +++++++-- .../Sources/Model/TransientStore.swift | 1 + Passepartout/Sources/SwiftGen+Strings.swift | 2 +- 8 files changed, 106 insertions(+), 40 deletions(-) diff --git a/Passepartout-iOS-Tunnel/PacketTunnelProvider.swift b/Passepartout-iOS-Tunnel/PacketTunnelProvider.swift index 223b1949..f3c423cb 100644 --- a/Passepartout-iOS-Tunnel/PacketTunnelProvider.swift +++ b/Passepartout-iOS-Tunnel/PacketTunnelProvider.swift @@ -30,6 +30,7 @@ class PacketTunnelProvider: TunnelKitProvider { appVersion = "\(GroupConstants.App.name) \(GroupConstants.App.versionString)" dnsTimeout = GroupConstants.VPN.dnsTimeout logSeparator = GroupConstants.VPN.sessionMarker + dataCountInterval = GroupConstants.VPN.dataCountInterval super.startTunnel(options: options, completionHandler: completionHandler) } } diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index 50be7a11..5cd28b7e 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -50,6 +50,8 @@ class ServiceViewController: UIViewController, TableModelHost { private var shouldDeleteLogOnDisconnection = false + private var currentDataCount: (Int, Int)? + // MARK: Table var model: TableModel = TableModel() @@ -103,6 +105,7 @@ class ServiceViewController: UIViewController, TableModelHost { nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidChangeStatus, object: nil) nc.addObserver(self, selector: #selector(vpnDidUpdate), name: .VPNDidReinstall, object: nil) nc.addObserver(self, selector: #selector(intentDidUpdateService), name: .IntentDidUpdateService, object: nil) + nc.addObserver(self, selector: #selector(serviceDidUpdateDataCount(_:)), name: .ConnectionServiceDidUpdateDataCount, object: nil) // run this no matter what // XXX: convenient here vs AppDelegate for updating table @@ -373,32 +376,32 @@ class ServiceViewController: UIViewController, TableModelHost { } } - private func displayDataCount() { - guard vpn.isEnabled else { - let alert = Macros.alert( - L10n.Service.Cells.DataCount.caption, - L10n.Service.Alerts.DataCount.Messages.notAvailable - ) - alert.addCancelAction(L10n.Global.ok) - present(alert, animated: true, completion: nil) - return - } - - vpn.requestBytesCount { - let message: String - if let count = $0 { - message = L10n.Service.Alerts.DataCount.Messages.current(Int(count.0), Int(count.1)) - } else { - message = L10n.Service.Alerts.DataCount.Messages.notAvailable - } - let alert = Macros.alert( - L10n.Service.Cells.DataCount.caption, - message - ) - alert.addCancelAction(L10n.Global.ok) - self.present(alert, animated: true, completion: nil) - } - } +// private func displayDataCount() { +// guard vpn.isEnabled else { +// let alert = Macros.alert( +// L10n.Service.Cells.DataCount.caption, +// L10n.Service.Alerts.DataCount.Messages.notAvailable +// ) +// alert.addCancelAction(L10n.Global.ok) +// present(alert, animated: true, completion: nil) +// return +// } +// +// vpn.requestBytesCount { +// let message: String +// if let count = $0 { +// message = L10n.Service.Alerts.DataCount.Messages.current(Int(count.0), Int(count.1)) +// } else { +// message = L10n.Service.Alerts.DataCount.Messages.notAvailable +// } +// let alert = Macros.alert( +// L10n.Service.Cells.DataCount.caption, +// message +// ) +// alert.addCancelAction(L10n.Global.ok) +// self.present(alert, animated: true, completion: nil) +// } +// } private func togglePrivateDataMasking(cell: ToggleTableViewCell) { let handler = { @@ -468,6 +471,13 @@ class ServiceViewController: UIViewController, TableModelHost { reloadModel() updateViewsIfNeeded() } + + @objc private func serviceDidUpdateDataCount(_ notification: Notification) { + guard let dataCount = notification.userInfo?[ConnectionService.NotificationKeys.dataCount] as? (Int, Int) else { + return + } + refreshDataCount(dataCount) + } } // MARK: - @@ -551,6 +561,10 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog return model.indexPath(row: .connectionStatus, section: .vpn) } + private var dataCountIndexPath: IndexPath? { + return model.indexPath(row: .dataCount, section: .diagnostics) + } + private var endpointIndexPath: IndexPath { guard let ip = model.indexPath(row: .endpoint, section: .configuration) else { fatalError("Could not locate endpointIndexPath") @@ -743,6 +757,13 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog case .dataCount: let cell = Cells.setting.dequeue(from: tableView, for: indexPath) cell.leftText = L10n.Service.Cells.DataCount.caption + if let count = currentDataCount, vpn.status == .connected { + cell.rightText = "\(count.0)/\(count.1)" + } else { + cell.rightText = nil + } + cell.accessoryType = .none + cell.isTappable = false return cell case .debugLog: @@ -859,8 +880,8 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog case .testConnectivity: testInternetConnectivity() - case .dataCount: - displayDataCount() +// case .dataCount: +// displayDataCount() case .debugLog: perform(segue: StoryboardSegue.Main.debugLogSegueIdentifier, sender: cell) @@ -1018,10 +1039,23 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog guard service.isActiveProfile(profile) else { return } + var ips: [IndexPath] = [] guard let statusIndexPath = statusIndexPath else { return } - tableView.reloadRows(at: [statusIndexPath], with: .none) + ips.append(statusIndexPath) + if let dataCountIndexPath = dataCountIndexPath { + ips.append(dataCountIndexPath) + } + tableView.reloadRows(at: ips, with: .none) + } + + private func refreshDataCount(_ dataCount: (Int, Int)?) { + currentDataCount = dataCount + guard let dataCountIndexPath = dataCountIndexPath else { + return + } + tableView.reloadRows(at: [dataCountIndexPath], with: .none) } func reloadSelectedRow(andRowAt indexPath: IndexPath? = nil) { diff --git a/Passepartout-iOS/Tables/TableModel.swift b/Passepartout-iOS/Tables/TableModel.swift index d86d6112..aa4c3264 100644 --- a/Passepartout-iOS/Tables/TableModel.swift +++ b/Passepartout-iOS/Tables/TableModel.swift @@ -93,7 +93,7 @@ class TableModel { func indexPath(row rowObject: R, section sectionObject: S) -> IndexPath? { guard let sectionIndex = sections.index(of: sectionObject) else { - fatalError("Missing section: \(sectionObject)") + return nil } guard let row = rowsBySection[sectionObject]?.index(of: rowObject) else { return nil diff --git a/Passepartout/Resources/en.lproj/Localizable.strings b/Passepartout/Resources/en.lproj/Localizable.strings index 4fba270f..70f98ec0 100644 --- a/Passepartout/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Resources/en.lproj/Localizable.strings @@ -107,7 +107,7 @@ "service.cells.trusted_add_wifi.caption" = "Add current Wi-Fi"; "service.cells.trusted_policy.caption" = "Trust disables VPN"; "service.cells.test_connectivity.caption" = "Test connectivity"; -"service.cells.data_count.caption" = "Exchanged bytes count"; +"service.cells.data_count.caption" = "Exchanged data count"; "service.cells.debug_log.caption" = "Debug log"; "service.cells.masks_private_data.caption" = "Mask network data"; "service.cells.report_issue.caption" = "Report connectivity issue"; diff --git a/Passepartout/Sources/GroupConstants.swift b/Passepartout/Sources/GroupConstants.swift index b79d793e..54cf1574 100644 --- a/Passepartout/Sources/GroupConstants.swift +++ b/Passepartout/Sources/GroupConstants.swift @@ -79,5 +79,7 @@ public class GroupConstants { public static let dnsTimeout = 5000 public static let sessionMarker = "--- EOF ---" + + public static let dataCountInterval = 5000 } } diff --git a/Passepartout/Sources/Model/ConnectionService.swift b/Passepartout/Sources/Model/ConnectionService.swift index a487375f..e4f93e98 100644 --- a/Passepartout/Sources/Model/ConnectionService.swift +++ b/Passepartout/Sources/Model/ConnectionService.swift @@ -42,6 +42,10 @@ public protocol ConnectionServiceDelegate: class { func connectionService(didActivate profile: ConnectionProfile) } +public extension Notification.Name { + static let ConnectionServiceDidUpdateDataCount = Notification.Name("ConnectionServiceDidUpdateDataCount") +} + public class ConnectionService: Codable { public enum CodingKeys: String, CodingKey { case build @@ -54,6 +58,10 @@ public class ConnectionService: Codable { case preferences } + + public struct NotificationKeys { + public static let dataCount = "DataCount" + } public var directory: String? = nil @@ -85,6 +93,8 @@ public class ConnectionService: Codable { private var cache: [ProfileKey: ConnectionProfile] + private var dataCountObserver: Timer? + public private(set) var activeProfileKey: ProfileKey? { willSet { if let oldProfile = activeProfile { @@ -130,6 +140,10 @@ public class ConnectionService: Codable { cache = [:] } + deinit { + dataCountObserver?.invalidate() + } + // MARK: Codable public required init(from decoder: Decoder) throws { @@ -502,14 +516,6 @@ public class ConnectionService: Codable { return baseConfiguration.existingLog(in: appGroup) ?? "" } - public var vpnLastError: TunnelKitProvider.ProviderError? { - return baseConfiguration.lastError(in: appGroup) - } - - public func clearVpnLastError() { - baseConfiguration.clearLastError(in: appGroup) - } - public func eraseVpnLog() { log.info("Erasing VPN log...") guard let url = baseConfiguration.urlForLog(in: appGroup) else { @@ -517,4 +523,26 @@ public class ConnectionService: Codable { } try? FileManager.default.removeItem(at: url) } + + public var vpnLastError: TunnelKitProvider.ProviderError? { + return baseConfiguration.lastError(in: appGroup) + } + + public func clearVpnLastError() { + baseConfiguration.clearLastError(in: appGroup) + } + + public func observeVPNDataCount(interval: TimeInterval) { + dataCountObserver?.invalidate() + dataCountObserver = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { [weak self] (_) in + guard let dataCount = self?.vpnDataCount else { + return + } + NotificationCenter.default.post(name: .ConnectionServiceDidUpdateDataCount, object: nil, userInfo: [NotificationKeys.dataCount: dataCount]) + }) + } + + public var vpnDataCount: (Int, Int)? { + return baseConfiguration.dataCount(in: appGroup) + } } diff --git a/Passepartout/Sources/Model/TransientStore.swift b/Passepartout/Sources/Model/TransientStore.swift index df93dad2..bf243697 100644 --- a/Passepartout/Sources/Model/TransientStore.swift +++ b/Passepartout/Sources/Model/TransientStore.swift @@ -107,6 +107,7 @@ public class TransientStore { // _ = service.addProfile(HostConnectionProfile(title: "vps"), credentials: Credentials(username: "foo", password: "bar")) // service.activateProfile(service.profiles.first!) } + service.observeVPNDataCount(interval: TimeInterval(GroupConstants.VPN.dataCountInterval) / 1000.0) } public func serialize(withProfiles: Bool) { diff --git a/Passepartout/Sources/SwiftGen+Strings.swift b/Passepartout/Sources/SwiftGen+Strings.swift index 8e252def..f28eaabd 100644 --- a/Passepartout/Sources/SwiftGen+Strings.swift +++ b/Passepartout/Sources/SwiftGen+Strings.swift @@ -541,7 +541,7 @@ public enum L10n { public static let caption = L10n.tr("Localizable", "service.cells.connection_status.caption") } public enum DataCount { - /// Exchanged bytes count + /// Exchanged data count public static let caption = L10n.tr("Localizable", "service.cells.data_count.caption") } public enum DebugLog { From 2e142680c3423fc2c7ee61428e943830b8b10d9a Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sat, 30 Mar 2019 20:03:50 +0100 Subject: [PATCH 3/5] Fetch current data count on VPN status update --- Passepartout-iOS/Scenes/ServiceViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index 5cd28b7e..59354ab0 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -1045,6 +1045,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog } ips.append(statusIndexPath) if let dataCountIndexPath = dataCountIndexPath { + currentDataCount = service.vpnDataCount ips.append(dataCountIndexPath) } tableView.reloadRows(at: ips, with: .none) From 921e57557d6728c6c1a33153c7f082830358af30 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sat, 30 Mar 2019 19:37:24 +0100 Subject: [PATCH 4/5] Render data count with closest unit Set a 10% boundary (e.g. 100MB = 0.1GB). --- Passepartout-CoreTests/UtilsTests.swift | 51 ++++++++++ .../Scenes/ServiceViewController.swift | 2 +- Passepartout.xcodeproj/project.pbxproj | 8 ++ .../Resources/en.lproj/Localizable.strings | 1 + Passepartout/Sources/Model/DataUnit.swift | 93 +++++++++++++++++++ Passepartout/Sources/SwiftGen+Strings.swift | 4 + 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 Passepartout-CoreTests/UtilsTests.swift create mode 100644 Passepartout/Sources/Model/DataUnit.swift diff --git a/Passepartout-CoreTests/UtilsTests.swift b/Passepartout-CoreTests/UtilsTests.swift new file mode 100644 index 00000000..516517a7 --- /dev/null +++ b/Passepartout-CoreTests/UtilsTests.swift @@ -0,0 +1,51 @@ +// +// UtilsTests.swift +// Passepartout-CoreTests +// +// Created by Davide De Rosa on 3/30/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 . +// + +import XCTest +@testable import Passepartout_Core + +class UtilsTests: XCTestCase { + override func setUp() { + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testDataUnitDescription() { + XCTAssertEqual(0.dataUnitDescription, "0B") + XCTAssertEqual(1.dataUnitDescription, "1B") + XCTAssertEqual(1024.dataUnitDescription, "1kB") + XCTAssertEqual(1025.dataUnitDescription, "1kB") + XCTAssertEqual(548575.dataUnitDescription, "0.52MB") + XCTAssertEqual(1048575.dataUnitDescription, "1.00MB") + XCTAssertEqual(1048576.dataUnitDescription, "1.00MB") + XCTAssertEqual(1048577.dataUnitDescription, "1.00MB") + XCTAssertEqual(600000000.dataUnitDescription, "0.56GB") + XCTAssertEqual(1073741823.dataUnitDescription, "1.00GB") + XCTAssertEqual(1073741824.dataUnitDescription, "1.00GB") + XCTAssertEqual(1073741825.dataUnitDescription, "1.00GB") + } +} diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index 59354ab0..083bdfd6 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -758,7 +758,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog let cell = Cells.setting.dequeue(from: tableView, for: indexPath) cell.leftText = L10n.Service.Cells.DataCount.caption if let count = currentDataCount, vpn.status == .connected { - cell.rightText = "\(count.0)/\(count.1)" + cell.rightText = L10n.Service.Cells.DataCount.value(count.0.dataUnitDescription, count.1.dataUnitDescription) } else { cell.rightText = nil } diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index b3c7a7df..527aee9f 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -82,6 +82,8 @@ 0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; }; 0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; }; 0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; }; + 0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB109224FECEA00E9E551 /* DataUnit.swift */; }; + 0ECEB10C224FEF9B00E9E551 /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */; }; 0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44D20E1122200A6BB43 /* TableModel.swift */; }; 0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; }; 0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2820CF2A340027975F /* AccountViewController.swift */; }; @@ -226,6 +228,8 @@ 0ECEB106224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Organizer.storyboard; sourceTree = ""; }; 0ECEB107224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Shortcuts.storyboard; sourceTree = ""; }; 0ECEB108224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 0ECEB109224FECEA00E9E551 /* DataUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataUnit.swift; sourceTree = ""; }; + 0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UtilsTests.swift; sourceTree = ""; }; 0ECEE44D20E1122200A6BB43 /* TableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableModel.swift; sourceTree = ""; }; 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Cells.swift"; sourceTree = ""; }; 0ED31C0F20CF09A30027975F /* Pool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = ""; }; @@ -338,6 +342,7 @@ 0EBBE8F121822B4D00106008 /* ConnectionService.json */, 0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */, 0ED31C2620CF257C0027975F /* InfrastructureTests.swift */, + 0ECEB10B224FEF9B00E9E551 /* UtilsTests.swift */, 0E3152AC223F9EF500F61841 /* Info.plist */, ); path = "Passepartout-CoreTests"; @@ -456,6 +461,7 @@ 0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */, 0EBBE8F42182361700106008 /* ConnectionService+Migration.swift */, 0EDE8DE620C93945004C739C /* Credentials.swift */, + 0ECEB109224FECEA00E9E551 /* DataUnit.swift */, 0EC7F20420E24308004EA58E /* DebugLog.swift */, 0ED38AE621404F100004D387 /* EndpointDataSource.swift */, 0E89DFC4213DF7AE00741BA1 /* Preferences.swift */, @@ -942,6 +948,7 @@ buildActionMask = 2147483647; files = ( 0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */, + 0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */, 0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */, 0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */, 0E3152C4223FA04800F61841 /* VPN.swift in Sources */, @@ -986,6 +993,7 @@ files = ( 0E3152BA223F9F3D00F61841 /* InfrastructureTests.swift in Sources */, 0E3152B9223F9F3B00F61841 /* ConnectionServiceTests.swift in Sources */, + 0ECEB10C224FEF9B00E9E551 /* UtilsTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Passepartout/Resources/en.lproj/Localizable.strings b/Passepartout/Resources/en.lproj/Localizable.strings index 70f98ec0..a1100153 100644 --- a/Passepartout/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Resources/en.lproj/Localizable.strings @@ -108,6 +108,7 @@ "service.cells.trusted_policy.caption" = "Trust disables VPN"; "service.cells.test_connectivity.caption" = "Test connectivity"; "service.cells.data_count.caption" = "Exchanged data count"; +"service.cells.data_count.value" = "%@ / %@"; "service.cells.debug_log.caption" = "Debug log"; "service.cells.masks_private_data.caption" = "Mask network data"; "service.cells.report_issue.caption" = "Report connectivity issue"; diff --git a/Passepartout/Sources/Model/DataUnit.swift b/Passepartout/Sources/Model/DataUnit.swift new file mode 100644 index 00000000..e93e2215 --- /dev/null +++ b/Passepartout/Sources/Model/DataUnit.swift @@ -0,0 +1,93 @@ +// +// DataUnit.swift +// Passepartout +// +// Created by Davide De Rosa on 3/30/18. +// 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 . +// + +import Foundation + +public enum DataUnit: Int, CustomStringConvertible { + case byte = 1 + + case kilobyte = 1024 + + case megabyte = 1048576 + + case gigabyte = 1073741824 + + fileprivate var showsDecimals: Bool { + switch self { + case .byte, .kilobyte: + return false + + case .megabyte, .gigabyte: + return true + } + } + + fileprivate var boundary: Int { + return Int(0.1 * Double(rawValue)) + } + + // MARK: CustomStringConvertible + + public var description: String { + switch self { + case .byte: + return "B" + + case .kilobyte: + return "kB" + + case .megabyte: + return "MB" + + case .gigabyte: + return "GB" + } + } +} + +public extension Int { + private static let allUnits: [DataUnit] = [ + .gigabyte, + .megabyte, + .kilobyte, + .byte + ] + + var dataUnitDescription: String { + if self == 0 { + return "0B" + } + for u in Int.allUnits { + if self >= u.boundary { + if !u.showsDecimals { + return "\(self / u.rawValue)\(u)" + } + let count = Double(self) / Double(u.rawValue) + return String(format: "%.2f%@", count, u.description) + } + } + fatalError("Number is negative") + } +} diff --git a/Passepartout/Sources/SwiftGen+Strings.swift b/Passepartout/Sources/SwiftGen+Strings.swift index f28eaabd..01171a2a 100644 --- a/Passepartout/Sources/SwiftGen+Strings.swift +++ b/Passepartout/Sources/SwiftGen+Strings.swift @@ -543,6 +543,10 @@ public enum L10n { public enum DataCount { /// Exchanged data count public static let caption = L10n.tr("Localizable", "service.cells.data_count.caption") + /// %@ / %@ + public static func value(_ p1: String, _ p2: String) -> String { + return L10n.tr("Localizable", "service.cells.data_count.value", p1, p2) + } } public enum DebugLog { /// Debug log From a47444848e26c97a855b2ee948afd879a3e76ca7 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sat, 30 Mar 2019 20:11:45 +0100 Subject: [PATCH 5/5] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90da950a..a8d4543b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Siri Shortcuts in-app manager. [#46](https://github.com/passepartoutvpn/passepartout-ios/pull/46) +- Background data count updates in diagnostics. [#51](https://github.com/passepartoutvpn/passepartout-ios/pull/51) ### Fixed