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.
This commit is contained in:
parent
6ba68b7f9a
commit
bc0a0d40dc
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||
|
||||
private var shouldDeleteLogOnDisconnection = false
|
||||
|
||||
private var currentDataCount: (Int, Int)?
|
||||
|
||||
// MARK: Table
|
||||
|
||||
var model: TableModel<SectionType, RowType> = 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) {
|
||||
|
|
|
@ -93,7 +93,7 @@ class TableModel<S: Hashable, R: Equatable> {
|
|||
|
||||
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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -79,5 +79,7 @@ public class GroupConstants {
|
|||
public static let dnsTimeout = 5000
|
||||
|
||||
public static let sessionMarker = "--- EOF ---"
|
||||
|
||||
public static let dataCountInterval = 5000
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -55,6 +59,10 @@ public class ConnectionService: Codable {
|
|||
case preferences
|
||||
}
|
||||
|
||||
public struct NotificationKeys {
|
||||
public static let dataCount = "DataCount"
|
||||
}
|
||||
|
||||
public var directory: String? = nil
|
||||
|
||||
public var rootURL: URL {
|
||||
|
@ -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,6 +516,14 @@ public class ConnectionService: Codable {
|
|||
return baseConfiguration.existingLog(in: appGroup) ?? ""
|
||||
}
|
||||
|
||||
public func eraseVpnLog() {
|
||||
log.info("Erasing VPN log...")
|
||||
guard let url = baseConfiguration.urlForLog(in: appGroup) else {
|
||||
return
|
||||
}
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
}
|
||||
|
||||
public var vpnLastError: TunnelKitProvider.ProviderError? {
|
||||
return baseConfiguration.lastError(in: appGroup)
|
||||
}
|
||||
|
@ -510,11 +532,17 @@ public class ConnectionService: Codable {
|
|||
baseConfiguration.clearLastError(in: appGroup)
|
||||
}
|
||||
|
||||
public func eraseVpnLog() {
|
||||
log.info("Erasing VPN log...")
|
||||
guard let url = baseConfiguration.urlForLog(in: appGroup) else {
|
||||
return
|
||||
}
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue