aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoopesh Chander <roop@roopc.net>2019-02-10 02:38:23 +0530
committerRoopesh Chander <roop@roopc.net>2019-02-10 03:35:24 +0530
commite53c2d4d179257ea2f575fb3cf7bdd492d321ab6 (patch)
treea2ec16f2f2d74ad57020e7c79534282b78a79120
parentImporting: Ignore case in matching file extensions inside zip files (diff)
downloadwireguard-apple-e53c2d4d179257ea2f575fb3cf7bdd492d321ab6.tar.xz
wireguard-apple-e53c2d4d179257ea2f575fb3cf7bdd492d321ab6.zip
iOS: Rewrite applying runtime configuration
To make scrolling smoother while the fields are modified Signed-off-by: Roopesh Chander <roop@roopc.net>
-rw-r--r--WireGuard/WireGuard/UI/TunnelViewModel.swift59
-rw-r--r--WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift83
2 files changed, 76 insertions, 66 deletions
diff --git a/WireGuard/WireGuard/UI/TunnelViewModel.swift b/WireGuard/WireGuard/UI/TunnelViewModel.swift
index efca5c0f..0657a16d 100644
--- a/WireGuard/WireGuard/UI/TunnelViewModel.swift
+++ b/WireGuard/WireGuard/UI/TunnelViewModel.swift
@@ -67,17 +67,17 @@ class TunnelViewModel {
static let keyLengthInBase64 = 44
- struct ChangeHandlers {
- enum FieldChange {
+ struct Changes {
+ enum FieldChange: Equatable {
case added
case removed
- case modified
+ case modified(newValue: String)
}
- var interfaceChanged: ([InterfaceField: FieldChange]) -> Void
- var peerChangedAt: (Int, [PeerField: FieldChange]) -> Void
- var peersRemovedAt: ([Int]) -> Void
- var peersInsertedAt: ([Int]) -> Void
+ var interfaceChanges: [InterfaceField: FieldChange]
+ var peerChanges: [(peerIndex: Int, changes: [PeerField: FieldChange])]
+ var peersRemovedIndices: [Int]
+ var peersInsertedIndices: [Int]
}
class InterfaceData {
@@ -217,12 +217,12 @@ class TunnelViewModel {
}
}
- func applyConfiguration(other: InterfaceConfiguration, otherName: String, changeHandler: ([InterfaceField: ChangeHandlers.FieldChange]) -> Void) {
+ func applyConfiguration(other: InterfaceConfiguration, otherName: String) -> [InterfaceField: Changes.FieldChange] {
if scratchpad.isEmpty {
populateScratchpad()
}
let otherScratchPad = InterfaceData.createScratchPad(from: other, name: otherName)
- var changes = [InterfaceField: ChangeHandlers.FieldChange]()
+ var changes = [InterfaceField: Changes.FieldChange]()
for field in InterfaceField.allCases {
switch (scratchpad[field] ?? "", otherScratchPad[field] ?? "") {
case ("", ""):
@@ -233,14 +233,12 @@ class TunnelViewModel {
changes[field] = .removed
case (let this, let other):
if this != other {
- changes[field] = .modified
+ changes[field] = .modified(newValue: other)
}
}
}
scratchpad = otherScratchPad
- if !changes.isEmpty {
- changeHandler(changes)
- }
+ return changes
}
}
@@ -441,12 +439,12 @@ class TunnelViewModel {
excludePrivateIPsValue = isOn
}
- func applyConfiguration(other: PeerConfiguration, peerIndex: Int, changeHandler: (Int, [PeerField: ChangeHandlers.FieldChange]) -> Void) {
+ func applyConfiguration(other: PeerConfiguration) -> [PeerField: Changes.FieldChange] {
if scratchpad.isEmpty {
populateScratchpad()
}
let otherScratchPad = PeerData.createScratchPad(from: other)
- var changes = [PeerField: ChangeHandlers.FieldChange]()
+ var changes = [PeerField: Changes.FieldChange]()
for field in PeerField.allCases {
switch (scratchpad[field] ?? "", otherScratchPad[field] ?? "") {
case ("", ""):
@@ -457,14 +455,12 @@ class TunnelViewModel {
changes[field] = .removed
case (let this, let other):
if this != other {
- changes[field] = .modified
+ changes[field] = .modified(newValue: other)
}
}
}
scratchpad = otherScratchPad
- if !changes.isEmpty {
- changeHandler(peerIndex, changes)
- }
+ return changes
}
}
@@ -548,21 +544,20 @@ class TunnelViewModel {
}
}
- func applyConfiguration(other: TunnelConfiguration, changeHandlers: ChangeHandlers) {
+ @discardableResult
+ func applyConfiguration(other: TunnelConfiguration) -> Changes {
// Replaces current data with data from other TunnelConfiguration, ignoring any changes in peer ordering.
- // Change handler callbacks are processed in the following order, which is designed to work with both the
- // UITableView way (modify - delete - insert) and the NSTableView way (indices are based on past operations):
- // - interfaceChanged
- // - peerChangedAt
- // - peersRemovedAt
- // - peersInsertedAt
- interfaceData.applyConfiguration(other: other.interface, otherName: other.name ?? "", changeHandler: changeHandlers.interfaceChanged)
+ let interfaceChanges = interfaceData.applyConfiguration(other: other.interface, otherName: other.name ?? "")
+ var peerChanges = [(peerIndex: Int, changes: [PeerField: Changes.FieldChange])]()
for otherPeer in other.peers {
if let peersDataIndex = peersData.firstIndex(where: { $0.publicKey == otherPeer.publicKey }) {
let peerData = peersData[peersDataIndex]
- peerData.applyConfiguration(other: otherPeer, peerIndex: peersDataIndex, changeHandler: changeHandlers.peerChangedAt)
+ let changes = peerData.applyConfiguration(other: otherPeer)
+ if !changes.isEmpty {
+ peerChanges.append((peerIndex: peersDataIndex, changes: changes))
+ }
}
}
@@ -573,9 +568,6 @@ class TunnelViewModel {
peersData.remove(at: index)
}
}
- if !removedPeerIndices.isEmpty {
- changeHandlers.peersRemovedAt(removedPeerIndices)
- }
var addedPeerIndices = [Int]()
for otherPeer in other.peers {
@@ -586,15 +578,14 @@ class TunnelViewModel {
peersData.append(peerData)
}
}
- if !addedPeerIndices.isEmpty {
- changeHandlers.peersInsertedAt(addedPeerIndices)
- }
for (index, peer) in peersData.enumerated() {
peer.index = index
peer.numberOfPeers = peersData.count
peer.updateExcludePrivateIPsFieldState()
}
+
+ return Changes(interfaceChanges: interfaceChanges, peerChanges: peerChanges, peersRemovedIndices: removedPeerIndices, peersInsertedIndices: addedPeerIndices)
}
}
diff --git a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift
index 1eb64619..d45f8469 100644
--- a/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift
+++ b/WireGuard/WireGuard/UI/iOS/ViewController/TunnelDetailTableViewController.swift
@@ -158,16 +158,21 @@ class TunnelDetailTableViewController: UITableViewController {
var interfaceFieldIsVisible = self.interfaceFieldIsVisible
var peerFieldIsVisible = self.peerFieldIsVisible
- func sectionChanged<T>(fields: [T], fieldIsVisible fieldIsVisibleInput: [Bool], tableView: UITableView, section: Int, changes: [T: TunnelViewModel.ChangeHandlers.FieldChange]) {
- var fieldIsVisible = fieldIsVisibleInput
- var modifiedIndexPaths = [IndexPath]()
- for (index, field) in fields.enumerated() where changes[field] == .modified {
- let row = fieldIsVisible[0 ..< index].filter { $0 }.count
- modifiedIndexPaths.append(IndexPath(row: row, section: section))
- }
- if !modifiedIndexPaths.isEmpty {
- tableView.reloadRows(at: modifiedIndexPaths, with: .none)
+ func handleSectionFieldsModified<T>(fields: [T], fieldIsVisible: [Bool], section: Int, changes: [T: TunnelViewModel.Changes.FieldChange]) {
+ for (index, field) in fields.enumerated() {
+ guard let change = changes[field] else { continue }
+ if case .modified(let newValue) = change {
+ let row = fieldIsVisible[0 ..< index].filter { $0 }.count
+ let indexPath = IndexPath(row: row, section: section)
+ if let cell = tableView.cellForRow(at: indexPath) as? KeyValueCell {
+ cell.value = newValue
+ }
+ }
}
+ }
+
+ func handleSectionRowsInsertedOrRemoved<T>(fields: [T], fieldIsVisible fieldIsVisibleInput: [Bool], section: Int, changes: [T: TunnelViewModel.Changes.FieldChange]) {
+ var fieldIsVisible = fieldIsVisibleInput
var removedIndexPaths = [IndexPath]()
for (index, field) in fields.enumerated().reversed() where changes[field] == .removed {
@@ -190,30 +195,44 @@ class TunnelDetailTableViewController: UITableViewController {
}
}
- let changeHandlers = TunnelViewModel.ChangeHandlers(
- interfaceChanged: { changes in
- sectionChanged(fields: TunnelDetailTableViewController.interfaceFields, fieldIsVisible: interfaceFieldIsVisible,
- tableView: tableView, section: interfaceSectionIndex, changes: changes)
- },
- peerChangedAt: { peerIndex, changes in
- sectionChanged(fields: TunnelDetailTableViewController.peerFields, fieldIsVisible: peerFieldIsVisible[peerIndex],
- tableView: tableView, section: firstPeerSectionIndex + peerIndex, changes: changes)
- },
- peersRemovedAt: { peerIndices in
- let sectionIndices = peerIndices.map { firstPeerSectionIndex + $0 }
- tableView.deleteSections(IndexSet(sectionIndices), with: .automatic)
- },
- peersInsertedAt: { peerIndices in
- let sectionIndices = peerIndices.map { firstPeerSectionIndex + $0 }
- tableView.insertSections(IndexSet(sectionIndices), with: .automatic)
- }
- )
+ let changes = self.tunnelViewModel.applyConfiguration(other: tunnelConfiguration)
+
+ if !changes.interfaceChanges.isEmpty {
+ handleSectionFieldsModified(fields: TunnelDetailTableViewController.interfaceFields, fieldIsVisible: interfaceFieldIsVisible,
+ section: interfaceSectionIndex, changes: changes.interfaceChanges)
+ }
+ for (peerIndex, peerChanges) in changes.peerChanges {
+ handleSectionFieldsModified(fields: TunnelDetailTableViewController.peerFields, fieldIsVisible: peerFieldIsVisible[peerIndex], section: firstPeerSectionIndex + peerIndex, changes: peerChanges)
+ }
+
+ let isAnyInterfaceFieldAddedOrRemoved = changes.interfaceChanges.contains { $0.value == .added || $0.value == .removed }
+ let isAnyPeerFieldAddedOrRemoved = changes.peerChanges.contains { $0.changes.contains { $0.value == .added || $0.value == .removed } }
+ let peersRemovedSectionIndices = changes.peersRemovedIndices.map { firstPeerSectionIndex + $0 }
+ let peersInsertedSectionIndices = changes.peersInsertedIndices.map { firstPeerSectionIndex + $0 }
- tableView.beginUpdates()
- self.tunnelViewModel.applyConfiguration(other: tunnelConfiguration, changeHandlers: changeHandlers)
- self.loadSections()
- self.loadVisibleFields()
- tableView.endUpdates()
+ if isAnyInterfaceFieldAddedOrRemoved || isAnyPeerFieldAddedOrRemoved || !peersRemovedSectionIndices.isEmpty || !peersInsertedSectionIndices.isEmpty {
+ tableView.beginUpdates()
+ if isAnyInterfaceFieldAddedOrRemoved {
+ handleSectionRowsInsertedOrRemoved(fields: TunnelDetailTableViewController.interfaceFields, fieldIsVisible: interfaceFieldIsVisible, section: interfaceSectionIndex, changes: changes.interfaceChanges)
+ }
+ if isAnyPeerFieldAddedOrRemoved {
+ for (peerIndex, peerChanges) in changes.peerChanges {
+ handleSectionRowsInsertedOrRemoved(fields: TunnelDetailTableViewController.peerFields, fieldIsVisible: peerFieldIsVisible[peerIndex], section: firstPeerSectionIndex + peerIndex, changes: peerChanges)
+ }
+ }
+ if !peersRemovedSectionIndices.isEmpty {
+ tableView.deleteSections(IndexSet(peersRemovedSectionIndices), with: .automatic)
+ }
+ if !peersInsertedSectionIndices.isEmpty {
+ tableView.insertSections(IndexSet(peersInsertedSectionIndices), with: .automatic)
+ }
+ self.loadSections()
+ self.loadVisibleFields()
+ tableView.endUpdates()
+ } else {
+ self.loadSections()
+ self.loadVisibleFields()
+ }
}
private func reloadRuntimeConfiguration() {