init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/WireGuard/tree/src/tools/config.c
- guard (!string.isEmpty) else { return nil }
+ guard !string.isEmpty else { return nil }
let startOfPort: String.Index
let hostString: String
- if (string.first! == "[") {
+ if string.first! == "[" {
// Look for IPv6-style endpoint, like [::1]:80
let startOfHost = string.index(after: string.startIndex)
guard let endOfHost = string.dropFirst().firstIndex(of: "]") else { return nil }
let afterEndOfHost = string.index(after: endOfHost)
- guard (string[afterEndOfHost] == ":") else { return nil }
+ guard string[afterEndOfHost] == ":" else { return nil }
startOfPort = string.index(after: afterEndOfHost)
hostString = String(string[startOfHost ..< endOfHost])
} else {
let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in
return !CharacterSet.urlHostAllowed.contains(char)
}
- guard (invalidCharacterIndex == nil) else { return nil }
+ guard invalidCharacterIndex == nil else { return nil }
host = NWEndpoint.Host(hostString)
port = endpointPort
}
func stringRepresentation() -> String {
- switch (host) {
+ switch host {
case .name(let hostname, _):
return "\(hostname):\(port)"
case .ipv4(let address):
extension Endpoint {
func hasHostAsIPAddress() -> Bool {
- switch (host) {
+ switch host {
case .name:
return false
case .ipv4:
}
func hostname() -> String? {
- switch (host) {
+ switch host {
case .name(let hostname, _):
return hostname
case .ipv4:
attributes[key] = value
}
} else {
- if (lowercasedLine != "[interface]" && lowercasedLine != "[peer]") {
+ if lowercasedLine != "[interface]" && lowercasedLine != "[peer]" {
throw ParseError.invalidLine(line)
}
}
let isLastLine: Bool = (lineIndex == lines.count - 1)
- if (isLastLine || lowercasedLine == "[interface]" || lowercasedLine == "[peer]") {
+ if isLastLine || lowercasedLine == "[interface]" || lowercasedLine == "[peer]" {
// Previous section has ended; process the attributes collected so far
- if (parserState == .inInterfaceSection) {
+ if parserState == .inInterfaceSection {
guard let interface = collate(interfaceAttributes: attributes) else { throw ParseError.invalidInterface }
- guard (interfaceConfiguration == nil) else { throw ParseError.multipleInterfaces }
+ guard interfaceConfiguration == nil else { throw ParseError.multipleInterfaces }
interfaceConfiguration = interface
- } else if (parserState == .inPeerSection) {
+ } else if parserState == .inPeerSection {
guard let peer = collate(peerAttributes: attributes) else { throw ParseError.invalidPeer }
peerConfigurations.append(peer)
}
}
- if (lowercasedLine == "[interface]") {
+ if lowercasedLine == "[interface]" {
parserState = .inInterfaceSection
attributes.removeAll()
- } else if (lowercasedLine == "[peer]") {
+ } else if lowercasedLine == "[peer]" {
parserState = .inPeerSection
attributes.removeAll()
}
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
- if (peerPublicKeysArray.count != peerPublicKeysSet.count) {
+ if peerPublicKeysArray.count != peerPublicKeysSet.count {
throw ParseError.multiplePeersWithSamePublicKey
}
import UIKit
class WgQuickConfigFileWriter {
- static func writeConfigFile(from tc: TunnelConfiguration) -> Data? {
- let interface = tc.interface
+ static func writeConfigFile(from configuration: TunnelConfiguration) -> Data? {
+ let interface = configuration.interface
var output = "[Interface]\n"
output.append("PrivateKey = \(interface.privateKey.base64EncodedString())\n")
if let listenPort = interface.listenPort {
output.append("ListenPort = \(listenPort)\n")
}
- if (!interface.addresses.isEmpty) {
+ if !interface.addresses.isEmpty {
let addressString = interface.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("Address = \(addressString)\n")
}
- if (!interface.dns.isEmpty) {
+ if !interface.dns.isEmpty {
let dnsString = interface.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("DNS = \(dnsString)\n")
}
output.append("MTU = \(mtu)\n")
}
- for peer in tc.peers {
+ for peer in configuration.peers {
output.append("\n[Peer]\n")
output.append("PublicKey = \(peer.publicKey.base64EncodedString())\n")
if let preSharedKey = peer.preSharedKey {
output.append("PresharedKey = \(preSharedKey.base64EncodedString())\n")
}
- if (!peer.allowedIPs.isEmpty) {
+ if !peer.allowedIPs.isEmpty {
let allowedIPsString = peer.allowedIPs.map { $0.stringRepresentation() }.joined(separator: ", ")
output.append("AllowedIPs = \(allowedIPsString)\n")
}
subscript(field: InterfaceField) -> String {
get {
- if (scratchpad.isEmpty) {
+ if scratchpad.isEmpty {
// When starting to read a config, setup the scratchpad.
// The scratchpad shall serve as a cache of what we want to show in the UI.
populateScratchpad()
return scratchpad[field] ?? ""
}
set(stringValue) {
- if (scratchpad.isEmpty) {
+ if scratchpad.isEmpty {
// When starting to edit a config, setup the scratchpad and remove the configuration.
// The scratchpad shall be the sole source of the being-edited configuration.
populateScratchpad()
}
validatedConfiguration = nil
- if (stringValue.isEmpty) {
+ if stringValue.isEmpty {
scratchpad.removeValue(forKey: field)
} else {
scratchpad[field] = stringValue
}
- if (field == .privateKey) {
+ if field == .privateKey {
if (stringValue.count == TunnelViewModel.keyLengthInBase64),
let privateKey = Data(base64Encoded: stringValue),
privateKey.count == TunnelConfiguration.keyLength {
scratchpad[.name] = config.name
scratchpad[.privateKey] = config.privateKey.base64EncodedString()
scratchpad[.publicKey] = config.publicKey.base64EncodedString()
- if (!config.addresses.isEmpty) {
+ if !config.addresses.isEmpty {
scratchpad[.addresses] = config.addresses.map { $0.stringRepresentation() }.joined(separator: ", ")
}
if let listenPort = config.listenPort {
if let mtu = config.mtu {
scratchpad[.mtu] = String(mtu)
}
- if (!config.dns.isEmpty) {
+ if !config.dns.isEmpty {
scratchpad[.dns] = config.dns.map { $0.stringRepresentation() }.joined(separator: ", ")
}
}
config.dns = dnsServers
}
- guard (errorMessages.isEmpty) else {
- return .error(errorMessages.first!)
- }
+ guard errorMessages.isEmpty else { return .error(errorMessages.first!) }
+
validatedConfiguration = config
return .saved(config)
}
func filterFieldsWithValueOrControl(interfaceFields: [InterfaceField]) -> [InterfaceField] {
return interfaceFields.filter { (field) -> Bool in
- if (TunnelViewModel.interfaceFieldsWithControl.contains(field)) {
+ if TunnelViewModel.interfaceFieldsWithControl.contains(field) {
return true
}
return (!self[field].isEmpty)
subscript(field: PeerField) -> String {
get {
- if (scratchpad.isEmpty) {
+ if scratchpad.isEmpty {
// When starting to read a config, setup the scratchpad.
// The scratchpad shall serve as a cache of what we want to show in the UI.
populateScratchpad()
return scratchpad[field] ?? ""
}
set(stringValue) {
- if (scratchpad.isEmpty) {
+ if scratchpad.isEmpty {
// When starting to edit a config, setup the scratchpad and remove the configuration.
// The scratchpad shall be the sole source of the being-edited configuration.
populateScratchpad()
}
validatedConfiguration = nil
- if (stringValue.isEmpty) {
+ if stringValue.isEmpty {
scratchpad.removeValue(forKey: field)
} else {
scratchpad[field] = stringValue
}
- if (field == .allowedIPs) {
+ if field == .allowedIPs {
updateExcludePrivateIPsFieldState()
}
}
if let preSharedKey = config.preSharedKey {
scratchpad[.preSharedKey] = preSharedKey.base64EncodedString()
}
- if (!config.allowedIPs.isEmpty) {
+ if !config.allowedIPs.isEmpty {
scratchpad[.allowedIPs] = config.allowedIPs.map { $0.stringRepresentation() }.joined(separator: ", ")
}
if let endpoint = config.endpoint {
}
}
- guard (errorMessages.isEmpty) else {
- return .error(errorMessages.first!)
- }
+ guard errorMessages.isEmpty else { return .error(errorMessages.first!) }
+
validatedConfiguration = config
return .saved(config)
}
func filterFieldsWithValueOrControl(peerFields: [PeerField]) -> [PeerField] {
return peerFields.filter { (field) -> Bool in
- if (TunnelViewModel.peerFieldsWithControl.contains(field)) {
+ if TunnelViewModel.peerFieldsWithControl.contains(field) {
return true
}
return (!self[field].isEmpty)
]
func updateExcludePrivateIPsFieldState() {
- guard (numberOfPeers == 1) else {
+ guard numberOfPeers == 1 else {
shouldAllowExcludePrivateIPsControl = false
excludePrivateIPsValue = false
return
}
- if (scratchpad.isEmpty) {
+ if scratchpad.isEmpty {
populateScratchpad()
}
let allowedIPStrings = Set<String>(
.split(separator: ",")
.map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
)
- if (allowedIPStrings.contains(TunnelViewModel.PeerData.ipv4DefaultRouteString)) {
+ if allowedIPStrings.contains(TunnelViewModel.PeerData.ipv4DefaultRouteString) {
shouldAllowExcludePrivateIPsControl = true
excludePrivateIPsValue = false
- } else if (allowedIPStrings.isSuperset(of: TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String)) {
+ } else if allowedIPStrings.isSuperset(of: TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String) {
shouldAllowExcludePrivateIPsControl = true
excludePrivateIPsValue = true
} else {
.map { $0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
let ipv6Addresses = allowedIPStrings.filter { $0.contains(":") }
let modifiedAllowedIPStrings: [String]
- if (isOn) {
+ if isOn {
modifiedAllowedIPStrings = ipv6Addresses +
TunnelViewModel.PeerData.ipv4DefaultRouteModRFC1918String + dnsServerStrings
} else {
let interfaceSaveResult = interfaceData.save()
let peerSaveResults = peersData.map { $0.save() }
// Collate the results
- switch (interfaceSaveResult) {
+ switch interfaceSaveResult {
case .error(let errorMessage):
return .error(errorMessage)
case .saved(let interfaceConfiguration):
var peerConfigurations: [PeerConfiguration] = []
peerConfigurations.reserveCapacity(peerSaveResults.count)
for peerSaveResult in peerSaveResults {
- switch (peerSaveResult) {
+ switch peerSaveResult {
case .error(let errorMessage):
return .error(errorMessage)
case .saved(let peerConfiguration):
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
- if (peerPublicKeysArray.count != peerPublicKeysSet.count) {
+ if peerPublicKeysArray.count != peerPublicKeysSet.count {
return .error("Two or more peers cannot have the same public key")
}
extension TunnelViewModel {
static func activateOnDemandOptionText(for activateOnDemandOption: ActivateOnDemandOption) -> String {
- switch (activateOnDemandOption) {
+ switch activateOnDemandOption {
case .none:
return "Off"
case .useOnDemandOverWiFiOrCellular:
static func activateOnDemandDetailText(for activateOnDemandSetting: ActivateOnDemandSetting?) -> String {
if let activateOnDemandSetting = activateOnDemandSetting {
- if (activateOnDemandSetting.isActivateOnDemandEnabled) {
+ if activateOnDemandSetting.isActivateOnDemandEnabled {
return TunnelViewModel.activateOnDemandOptionText(for: activateOnDemandSetting.activateOnDemandOption)
} else {
return TunnelViewModel.activateOnDemandOptionText(for: .none)
viewControllerWithRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder) -> UIViewController? {
guard let vcIdentifier = identifierComponents.last else { return nil }
- if (vcIdentifier.hasPrefix("TunnelDetailVC:")) {
+ if vcIdentifier.hasPrefix("TunnelDetailVC:") {
let tunnelName = String(vcIdentifier.suffix(vcIdentifier.count - "TunnelDetailVC:".count))
if let tunnelsManager = mainVC?.tunnelsManager {
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC)
tunnelDetailNC.restorationIdentifier = "DetailNC"
if let self = self {
- if (animated) {
+ if animated {
self.showDetailViewController(tunnelDetailNC, sender: self)
} else {
UIView.performWithoutAnimation {
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.allowsSelection = false
- self.tableView.register(TunnelSettingsTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelSettingsTableViewKeyValueCell.id)
- self.tableView.register(TunnelSettingsTableViewButtonCell.self, forCellReuseIdentifier: TunnelSettingsTableViewButtonCell.id)
+ self.tableView.register(TunnelSettingsTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier)
+ self.tableView.register(TunnelSettingsTableViewButtonCell.self, forCellReuseIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier)
let logo = UIImageView(image: UIImage(named: "wireguard.pdf", in: Bundle.main, compatibleWith: nil)!)
logo.contentMode = .scaleAspectFit
DispatchQueue.global(qos: .userInitiated).async {
- if (FileManager.default.fileExists(atPath: destinationURL.path)) {
+ if FileManager.default.fileExists(atPath: destinationURL.path) {
let isDeleted = FileManager.deleteFile(at: destinationURL)
- if (!isDeleted) {
+ if !isDeleted {
ErrorPresenter.showErrorAlert(title: "No log available", message: "The pre-existing log could not be cleared", from: self)
return
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- switch (section) {
+ switch section {
case 0:
return "About"
case 1:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let field = settingsFieldsBySection[indexPath.section][indexPath.row]
- if (field == .iosAppVersion || field == .goBackendVersion) {
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewKeyValueCell.id, for: indexPath) as! TunnelSettingsTableViewKeyValueCell
+ if field == .iosAppVersion || field == .goBackendVersion {
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewKeyValueCell
cell.key = field.rawValue
- if (field == .iosAppVersion) {
+ if field == .iosAppVersion {
var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown version"
if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
appVersion += " (\(appBuild))"
}
cell.value = appVersion
- } else if (field == .goBackendVersion) {
+ } else if field == .goBackendVersion {
cell.value = WIREGUARD_GO_VERSION
}
return cell
- } else if (field == .exportZipArchive) {
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.id, for: indexPath) as! TunnelSettingsTableViewButtonCell
+ } else if field == .exportZipArchive {
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewButtonCell
cell.buttonText = field.rawValue
cell.onTapped = { [weak self] in
self?.exportConfigurationsAsZipFile(sourceView: cell.button)
return cell
} else {
assert(field == .exportLogFile)
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.id, for: indexPath) as! TunnelSettingsTableViewButtonCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelSettingsTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelSettingsTableViewButtonCell
cell.buttonText = field.rawValue
cell.onTapped = { [weak self] in
self?.exportLogForLastActivatedTunnel(sourceView: cell.button)
}
class TunnelSettingsTableViewKeyValueCell: UITableViewCell {
- static let id: String = "TunnelSettingsTableViewKeyValueCell"
+ static let reuseIdentifier = "TunnelSettingsTableViewKeyValueCell"
var key: String {
get { return textLabel?.text ?? "" }
set(value) { textLabel?.text = value }
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
- super.init(style: .value1, reuseIdentifier: TunnelSettingsTableViewKeyValueCell.id)
+ super.init(style: .value1, reuseIdentifier: TunnelSettingsTableViewKeyValueCell.reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
}
class TunnelSettingsTableViewButtonCell: UITableViewCell {
- static let id: String = "TunnelSettingsTableViewButtonCell"
+ static let reuseIdentifier = "TunnelSettingsTableViewButtonCell"
var buttonText: String {
get { return button.title(for: .normal) ?? "" }
set(value) { button.setTitle(value, for: .normal) }
self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.allowsSelection = false
- self.tableView.register(TunnelDetailTableViewStatusCell.self, forCellReuseIdentifier: TunnelDetailTableViewStatusCell.id)
- self.tableView.register(TunnelDetailTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelDetailTableViewKeyValueCell.id)
- self.tableView.register(TunnelDetailTableViewButtonCell.self, forCellReuseIdentifier: TunnelDetailTableViewButtonCell.id)
- self.tableView.register(TunnelDetailTableViewActivateOnDemandCell.self, forCellReuseIdentifier: TunnelDetailTableViewActivateOnDemandCell.id)
+ self.tableView.register(TunnelDetailTableViewStatusCell.self, forCellReuseIdentifier: TunnelDetailTableViewStatusCell.reuseIdentifier)
+ self.tableView.register(TunnelDetailTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier)
+ self.tableView.register(TunnelDetailTableViewButtonCell.self, forCellReuseIdentifier: TunnelDetailTableViewButtonCell.reuseIdentifier)
+ self.tableView.register(TunnelDetailTableViewActivateOnDemandCell.self, forCellReuseIdentifier: TunnelDetailTableViewActivateOnDemandCell.reuseIdentifier)
// State restoration
self.restorationIdentifier = "TunnelDetailVC:\(tunnel.name)"
let interfaceData = tunnelViewModel.interfaceData
let numberOfPeerSections = tunnelViewModel.peersData.count
- if (section == 0) {
+ if section == 0 {
// Status
return 1
- } else if (section == 1) {
+ } else if section == 1 {
// Interface
return interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields).count
- } else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) {
+ } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer
let peerData = tunnelViewModel.peersData[section - 2]
return peerData.filterFieldsWithValueOrControl(peerFields: peerFields).count
- } else if (section < (3 + numberOfPeerSections)) {
+ } else if section < (3 + numberOfPeerSections) {
// Activate on demand
return 1
} else {
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let numberOfPeerSections = tunnelViewModel.peersData.count
- if (section == 0) {
+ if section == 0 {
// Status
return "Status"
- } else if (section == 1) {
+ } else if section == 1 {
// Interface
return "Interface"
- } else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) {
+ } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer
return "Peer"
- } else if (section < (3 + numberOfPeerSections)) {
+ } else if section < (3 + numberOfPeerSections) {
// On-Demand Activation
return "On-Demand Activation"
} else {
let section = indexPath.section
let row = indexPath.row
- if (section == 0) {
+ if section == 0 {
// Status
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewStatusCell.id, for: indexPath) as! TunnelDetailTableViewStatusCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewStatusCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewStatusCell
cell.tunnel = self.tunnel
cell.onSwitchToggled = { [weak self] isOn in
guard let self = self else { return }
- if (isOn) {
+ if isOn {
self.tunnelsManager.startActivation(of: self.tunnel) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: {
}
}
return cell
- } else if (section == 1) {
+ } else if section == 1 {
// Interface
let field = interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFields)[row]
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.id, for: indexPath) as! TunnelDetailTableViewKeyValueCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewKeyValueCell
// Set key and value
cell.key = field.rawValue
cell.value = interfaceData[field]
return cell
- } else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) {
+ } else if (numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections)) {
// Peer
let peerData = tunnelViewModel.peersData[section - 2]
let field = peerData.filterFieldsWithValueOrControl(peerFields: peerFields)[row]
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.id, for: indexPath) as! TunnelDetailTableViewKeyValueCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewKeyValueCell
// Set key and value
cell.key = field.rawValue
cell.value = peerData[field]
return cell
- } else if (section < (3 + numberOfPeerSections)) {
+ } else if section < (3 + numberOfPeerSections) {
// On-Demand Activation
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewActivateOnDemandCell.id, for: indexPath) as! TunnelDetailTableViewActivateOnDemandCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewActivateOnDemandCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewActivateOnDemandCell
cell.tunnel = self.tunnel
return cell
} else {
assert(section == (3 + numberOfPeerSections))
// Delete configuration
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.id, for: indexPath) as! TunnelDetailTableViewButtonCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelDetailTableViewButtonCell
cell.buttonText = "Delete tunnel"
cell.hasDestructiveAction = true
cell.onTapped = { [weak self] in
self.showConfirmationAlert(message: "Delete this tunnel?", buttonTitle: "Delete", from: cell) { [weak self] in
guard let tunnelsManager = self?.tunnelsManager, let tunnel = self?.tunnel else { return }
tunnelsManager.remove(tunnel: tunnel) { (error) in
- if (error != nil) {
+ if error != nil {
print("Error removing tunnel: \(String(describing: error))")
return
}
}
class TunnelDetailTableViewStatusCell: UITableViewCell {
- static let id: String = "TunnelDetailTableViewStatusCell"
+ static let reuseIdentifier = "TunnelDetailTableViewStatusCell"
var tunnel: TunnelContainer? {
didSet(value) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
statusSwitch = UISwitch()
- super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.id)
+ super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.reuseIdentifier)
accessoryView = statusSwitch
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
}
@objc func switchToggled() {
- if (isOnSwitchToggledHandlerEnabled) {
+ if isOnSwitchToggledHandlerEnabled {
onSwitchToggled?(statusSwitch.isOn)
}
}
return
}
let text: String
- switch (status) {
+ switch status {
case .inactive:
text = "Inactive"
case .activating:
}
class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
- static let id: String = "TunnelDetailTableViewKeyValueCell"
+ static let reuseIdentifier = "TunnelDetailTableViewKeyValueCell"
var key: String {
get { return keyLabel.text ?? "" }
set(value) { keyLabel.text = value }
func configureForContentSize() {
var constraints: [NSLayoutConstraint] = []
- if (self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory) {
+ if self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
// Stack vertically
- if (!isStackedVertically) {
+ if !isStackedVertically {
constraints = [
valueLabel.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
}
} else {
// Stack horizontally
- if (!isStackedHorizontally) {
+ if !isStackedHorizontally {
constraints = [
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueLabel.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
isStackedVertically = false
}
}
- if (!constraints.isEmpty) {
+ if !constraints.isEmpty {
NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints)
NSLayoutConstraint.activate(constraints)
self.contentSizeBasedConstraints = constraints
}
class TunnelDetailTableViewButtonCell: UITableViewCell {
- static let id: String = "TunnelDetailTableViewButtonCell"
+ static let reuseIdentifier = "TunnelDetailTableViewButtonCell"
var buttonText: String {
get { return button.title(for: .normal) ?? "" }
set(value) { button.setTitle(value, for: .normal) }
}
class TunnelDetailTableViewActivateOnDemandCell: UITableViewCell {
- static let id: String = "TunnelDetailTableViewActivateOnDemandCell"
+ static let reuseIdentifier = "TunnelDetailTableViewActivateOnDemandCell"
var tunnel: TunnelContainer? {
didSet(value) {
@objc func saveTapped() {
self.tableView.endEditing(false)
let tunnelSaveResult = tunnelViewModel.save()
- switch (tunnelSaveResult) {
+ switch tunnelSaveResult {
case .error(let errorMessage):
let erroringConfiguration = (tunnelViewModel.interfaceData.validatedConfiguration == nil) ? "Interface" : "Peer"
ErrorPresenter.showErrorAlert(title: "Invalid \(erroringConfiguration)", message: errorMessage, from: self)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- if (section < interfaceSectionCount) {
+ if section < interfaceSectionCount {
// Interface
return interfaceFieldsBySection[section].count
- } else if ((peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount))) {
+ } else if (peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount)) {
// Peer
let peerIndex = (section - interfaceSectionCount)
let peerData = tunnelViewModel.peersData[peerIndex]
let peerFieldsToShow = peerData.shouldAllowExcludePrivateIPsControl ? peerFields : peerFields.filter { $0 != .excludePrivateIPs }
return peerFieldsToShow.count
- } else if (section < (interfaceSectionCount + peerSectionCount + 1)) {
+ } else if section < (interfaceSectionCount + peerSectionCount + 1) {
// Add peer
return 1
} else {
// On-Demand Rules
- if (activateOnDemandSetting.isActivateOnDemandEnabled) {
+ if activateOnDemandSetting.isActivateOnDemandEnabled {
return 4
} else {
return 1
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- if (section < interfaceSectionCount) {
+ if section < interfaceSectionCount {
// Interface
return (section == 0) ? "Interface" : nil
- } else if ((peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount))) {
+ } else if (peerSectionCount > 0) && (section < (interfaceSectionCount + peerSectionCount)) {
// Peer
return "Peer"
- } else if (section == (interfaceSectionCount + peerSectionCount)) {
+ } else if section == (interfaceSectionCount + peerSectionCount) {
// Add peer
return nil
} else {
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- if (indexPath.section < interfaceSectionCount) {
+ if indexPath.section < interfaceSectionCount {
return interfaceFieldCell(for: tableView, at: indexPath)
- } else if ((peerSectionCount > 0) && (indexPath.section < (interfaceSectionCount + peerSectionCount))) {
+ } else if (peerSectionCount > 0) && (indexPath.section < (interfaceSectionCount + peerSectionCount)) {
return peerCell(for: tableView, at: indexPath)
- } else if (indexPath.section == (interfaceSectionCount + peerSectionCount)) {
+ } else if indexPath.section == (interfaceSectionCount + peerSectionCount) {
return addPeerCell(for: tableView, at: indexPath)
} else {
return onDemandCell(for: tableView, at: indexPath)
private func interfaceFieldCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let interfaceData = tunnelViewModel.interfaceData
let field = interfaceFieldsBySection[indexPath.section][indexPath.row]
- if (field == .generateKeyPair) {
+ if field == .generateKeyPair {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewButtonCell.reuseIdentifier, for: indexPath) as! TunnelEditTableViewButtonCell
cell.buttonText = field.rawValue
cell.onTapped = { [weak self, weak interfaceData] in
func configureForContentSize() {
var constraints: [NSLayoutConstraint] = []
- if (self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory) {
+ if self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
// Stack vertically
- if (!isStackedVertically) {
+ if !isStackedVertically {
constraints = [
valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueTextField.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
}
} else {
// Stack horizontally
- if (!isStackedHorizontally) {
+ if !isStackedHorizontally {
constraints = [
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: keyLabel.bottomAnchor, multiplier: 0.5),
valueTextField.leftAnchor.constraint(equalToSystemSpacingAfter: keyLabel.rightAnchor, multiplier: 1),
isStackedVertically = false
}
}
- if (!constraints.isEmpty) {
+ if !constraints.isEmpty {
NSLayoutConstraint.deactivate(self.contentSizeBasedConstraints)
NSLayoutConstraint.activate(constraints)
self.contentSizeBasedConstraints = constraints
}
func textFieldDidEndEditing(_ textField: UITextField) {
let isModified = (textField.text ?? "" != textFieldValueOnBeginEditing)
- guard (isModified) else { return }
+ guard isModified else { return }
if let onValueChanged = onValueChanged {
onValueChanged(textField.text ?? "")
}
}
func setTunnelsManager(tunnelsManager: TunnelsManager) {
- if (self.tunnelsManager != nil) {
+ if self.tunnelsManager != nil {
// If a tunnels manager is already set, do nothing
return
}
tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = .none
- tableView.register(TunnelsListTableViewCell.self, forCellReuseIdentifier: TunnelsListTableViewCell.id)
+ tableView.register(TunnelsListTableViewCell.self, forCellReuseIdentifier: TunnelsListTableViewCell.reuseIdentifier)
self.view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
}
@objc func addButtonTapped(sender: AnyObject) {
- if (self.tunnelsManager == nil) { return } // Do nothing until we've loaded the tunnels
+ if self.tunnelsManager == nil { return } // Do nothing until we've loaded the tunnels
let alert = UIAlertController(title: "", message: "Add a new WireGuard tunnel", preferredStyle: .actionSheet)
let importFileAction = UIAlertAction(title: "Create from file or archive", style: .default) { [weak self] (_) in
self?.presentViewControllerForFileImport()
}
@objc func settingsButtonTapped(sender: UIBarButtonItem!) {
- if (self.tunnelsManager == nil) { return } // Do nothing until we've loaded the tunnels
+ if self.tunnelsManager == nil { return } // Do nothing until we've loaded the tunnels
let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager)
let settingsNC = UINavigationController(rootViewController: settingsVC)
settingsNC.modalPresentationStyle = .formSheet
func importFromFile(url: URL, completionHandler: (() -> Void)?) {
guard let tunnelsManager = tunnelsManager else { return }
- if (url.pathExtension == "zip") {
+ if url.pathExtension == "zip" {
ZipImporter.importConfigFiles(from: url) { [weak self] result in
if let error = result.error {
ErrorPresenter.showErrorAlert(error: error, from: self)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = tableView.dequeueReusableCell(withIdentifier: TunnelsListTableViewCell.id, for: indexPath) as! TunnelsListTableViewCell
+ let cell = tableView.dequeueReusableCell(withIdentifier: TunnelsListTableViewCell.reuseIdentifier, for: indexPath) as! TunnelsListTableViewCell
if let tunnelsManager = tunnelsManager {
let tunnel = tunnelsManager.tunnel(at: indexPath.row)
cell.tunnel = tunnel
cell.onSwitchToggled = { [weak self] isOn in
guard let self = self, let tunnelsManager = self.tunnelsManager else { return }
- if (isOn) {
+ if isOn {
tunnelsManager.startActivation(of: tunnel) { [weak self] error in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self, onPresented: {
guard let tunnelsManager = self?.tunnelsManager else { return }
let tunnel = tunnelsManager.tunnel(at: indexPath.row)
tunnelsManager.remove(tunnel: tunnel, completionHandler: { (error) in
- if (error != nil) {
+ if error != nil {
ErrorPresenter.showErrorAlert(error: error!, from: self)
completionHandler(false)
} else {
tableView?.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}
- func tunnelMoved(at oldIndex: Int, to newIndex: Int) {
+ func tunnelMoved(from oldIndex: Int, to newIndex: Int) {
tableView?.moveRow(at: IndexPath(row: oldIndex, section: 0), to: IndexPath(row: newIndex, section: 0))
}
}
class TunnelsListTableViewCell: UITableViewCell {
- static let id: String = "TunnelsListTableViewCell"
+ static let reuseIdentifier = "TunnelsListTableViewCell"
var tunnel: TunnelContainer? {
didSet(value) {
// Bind to the tunnel's name
guard let statusSwitch = statusSwitch, let busyIndicator = busyIndicator else { return }
statusSwitch.isOn = !(status == .deactivating || status == .inactive)
statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
- if (status == .inactive || status == .active) {
+ if status == .inactive || status == .active {
busyIndicator.stopAnimating()
} else {
busyIndicator.startAnimating()
let rules: [NEOnDemandRule]?
let connectRule = NEOnDemandRuleConnect()
let disconnectRule = NEOnDemandRuleDisconnect()
- switch (activateOnDemandOption) {
+ switch activateOnDemandOption {
case .none:
rules = nil
case .useOnDemandOverWiFiOrCellular:
init(from tunnelProviderManager: NETunnelProviderManager) {
let rules = tunnelProviderManager.onDemandRules ?? []
let activateOnDemandOption: ActivateOnDemandOption
- switch (rules.count) {
+ switch rules.count {
case 0:
activateOnDemandOption = .none
case 1:
case 2:
let connectRule = rules.first(where: { $0.action == .connect })!
let disconnectRule = rules.first(where: { $0.action == .disconnect })!
- if (connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == .cellular) {
+ if connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == .cellular {
activateOnDemandOption = .useOnDemandOverWiFiOnly
- } else if (connectRule.interfaceTypeMatch == .cellular && disconnectRule.interfaceTypeMatch == .wiFi) {
+ } else if connectRule.interfaceTypeMatch == .cellular && disconnectRule.interfaceTypeMatch == .wiFi {
activateOnDemandOption = .useOnDemandOverCellularOnly
} else {
fatalError("Unexpected onDemandRules set on tunnel provider manager")
fatalError("Unexpected number of onDemandRules set on tunnel provider manager")
}
self.activateOnDemandOption = activateOnDemandOption
- if (activateOnDemandOption == .none) {
+ if activateOnDemandOption == .none {
self.isActivateOnDemandEnabled = false
} else {
self.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
extension InternetReachability.Status {
init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
var status: InternetReachability.Status = .notReachable
- if (flags.contains(.reachable)) {
- if (flags.contains(.isWWAN)) {
+ if flags.contains(.reachable) {
+ if flags.contains(.isWWAN) {
status = .reachableOverCellular
} else {
status = .reachableOverWiFi
import os.log
protocol TunnelsManagerListDelegate: class {
- func tunnelAdded(at: Int)
- func tunnelModified(at: Int)
- func tunnelMoved(at oldIndex: Int, to newIndex: Int)
- func tunnelRemoved(at: Int)
+ func tunnelAdded(at index: Int)
+ func tunnelModified(at index: Int)
+ func tunnelMoved(from oldIndex: Int, to newIndex: Int)
+ func tunnelRemoved(at index: Int)
}
protocol TunnelsManagerActivationDelegate: class {
case tunnelActivationFailedNoInternetConnection // startTunnel() succeeded, but activation failed since no internet
func alertText() -> (String, String)? {
- switch (self) {
+ switch self {
case .tunnelNameEmpty:
return ("No name provided", "Can't create tunnel with an empty name")
case .tunnelAlreadyExistsWithThatName:
activateOnDemandSetting.apply(on: tunnelProviderManager)
tunnelProviderManager.saveToPreferences { [weak self] (error) in
- guard (error == nil) else {
+ guard error == nil else {
os_log("Add: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(.failure(TunnelsManagerError.vpnSystemErrorOnAddTunnel))
return
let tunnelProviderManager = tunnel.tunnelProvider
let isNameChanged = (tunnelName != tunnelProviderManager.localizedDescription)
- if (isNameChanged) {
+ if isNameChanged {
if self.tunnels.contains(where: { $0.name == tunnelName }) {
completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
return
activateOnDemandSetting.apply(on: tunnelProviderManager)
tunnelProviderManager.saveToPreferences { [weak self] (error) in
- guard (error == nil) else {
+ guard error == nil else {
os_log("Modify: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel)
return
}
if let self = self {
- if (isNameChanged) {
+ if isNameChanged {
let oldIndex = self.tunnels.firstIndex(of: tunnel)!
self.tunnels.sort { $0.name < $1.name }
let newIndex = self.tunnels.firstIndex(of: tunnel)!
- self.tunnelsListDelegate?.tunnelMoved(at: oldIndex, to: newIndex)
+ self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
}
self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
- if (tunnel.status == .active || tunnel.status == .activating || tunnel.status == .reasserting) {
+ if tunnel.status == .active || tunnel.status == .activating || tunnel.status == .reasserting {
// Turn off the tunnel, and then turn it back on, so the changes are made effective
let session = (tunnel.tunnelProvider.connection as! NETunnelProviderSession)
tunnel.status = .restarting
session.stopTunnel()
}
- if (isActivatingOnDemand) {
+ if isActivatingOnDemand {
// Reload tunnel after saving.
// Without this, the tunnel stopes getting updates on the tunnel status from iOS.
tunnelProviderManager.loadFromPreferences { (error) in
tunnel.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
- guard (error == nil) else {
+ guard error == nil else {
os_log("Modify: Re-loading after saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnModifyTunnel)
return
let tunnelProviderManager = tunnel.tunnelProvider
tunnelProviderManager.removeFromPreferences { [weak self] (error) in
- guard (error == nil) else {
+ guard error == nil else {
os_log("Remove: Saving configuration failed: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.vpnSystemErrorOnRemoveTunnel)
return
func startActivation(of tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted
- guard (tunnel.status == .inactive) else {
+ guard tunnel.status == .inactive else {
completionHandler(TunnelsManagerError.attemptingActivationWhenTunnelIsNotInactive)
return
}
}
func startDeactivation(of tunnel: TunnelContainer) {
- if (tunnel.status == .inactive || tunnel.status == .deactivating) {
+ if tunnel.status == .inactive || tunnel.status == .deactivating {
return
}
tunnel.startDeactivation()
}
private func startObservingTunnelStatuses() {
- if (statusObservationToken != nil) { return }
+ if statusObservationToken != nil { return }
statusObservationToken = NotificationCenter.default.addObserver(
forName: .NEVPNStatusDidChange,
object: nil,
log: OSLog.default, type: .debug, tunnel.name, "\(tunnel.tunnelProvider.connection.status)")
// In case our attempt to start the tunnel, didn't succeed
- if (tunnel == self.tunnelBeingActivated) {
- if (session.status == .disconnected) {
- if (InternetReachability.currentStatus() == .notReachable) {
+ if tunnel == self.tunnelBeingActivated {
+ if session.status == .disconnected {
+ if InternetReachability.currentStatus() == .notReachable {
let error = TunnelsManagerError.tunnelActivationFailedNoInternetConnection
self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: error)
}
self.tunnelBeingActivated = nil
- } else if (session.status == .connected) {
+ } else if session.status == .connected {
self.tunnelBeingActivated = nil
}
}
// In case we're restarting the tunnel
- if ((tunnel.status == .restarting) && (session.status == .disconnected || session.status == .disconnecting)) {
+ if (tunnel.status == .restarting) && (session.status == .disconnected || session.status == .disconnecting) {
// Don't change tunnel.status when disconnecting for a restart
- if (session.status == .disconnected) {
+ if session.status == .disconnected {
self.tunnelBeingActivated = tunnel
tunnel.startActivation(completionHandler: { _ in })
}
lastError: Error? = nil,
tunnelConfiguration: TunnelConfiguration,
completionHandler: @escaping (TunnelsManagerError?) -> Void) {
- if (recursionCount >= 8) {
+ if recursionCount >= 8 {
os_log("startActivation: Failed after 8 attempts. Giving up with %{public}@", log: OSLog.default, type: .error, "\(lastError!)")
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return
os_log("startActivation: Entering (tunnel: %{public}@)", log: OSLog.default, type: .debug, self.name)
- guard (tunnelProvider.isEnabled) else {
+ guard tunnelProvider.isEnabled else {
// In case the tunnel had gotten disabled, re-enable and save it,
// then call this function again.
os_log("startActivation: Tunnel is disabled. Re-enabling and saving", log: OSLog.default, type: .info)
tunnelProvider.isEnabled = true
tunnelProvider.saveToPreferences { [weak self] (error) in
- if (error != nil) {
+ if error != nil {
os_log("Error saving tunnel after re-enabling: %{public}@", log: OSLog.default, type: .error, "\(error!)")
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return
try session.startTunnel()
os_log("startActivation: Success", log: OSLog.default, type: .debug)
completionHandler(nil)
- } catch (let error) {
+ } catch let error {
guard let vpnError = error as? NEVPNError else {
os_log("Failed to activate tunnel: Error: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return
}
- guard (vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale) else {
+ guard vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale else {
os_log("Failed to activate tunnel: VPN Error: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
return
}
- assert(vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale)
os_log("startActivation: Will reload tunnel and then try to start it. ", log: OSLog.default, type: .info)
tunnelProvider.loadFromPreferences { [weak self] (error) in
- if (error != nil) {
+ if error != nil {
os_log("startActivation: Error reloading tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error!)")
self?.status = .inactive
completionHandler(TunnelsManagerError.tunnelActivationAttemptFailed)
case restarting // Restarting tunnel (done after saving modifications to an active tunnel)
init(from vpnStatus: NEVPNStatus) {
- switch (vpnStatus) {
+ switch vpnStatus {
case .connected:
self = .active
case .connecting:
extension TunnelStatus: CustomDebugStringConvertible {
public var debugDescription: String {
- switch (self) {
+ switch self {
case .inactive: return "inactive"
case .activating: return "activating"
case .active: return "active"
extension NEVPNStatus: CustomDebugStringConvertible {
public var debugDescription: String {
- switch (self) {
+ switch self {
case .connected: return "connected"
case .connecting: return "connecting"
case .disconnected: return "disconnected"
case failure(_ error: WireGuardAppError)
var value: T? {
- switch (self) {
+ switch self {
case .success(let value): return value
case .failure: return nil
}
}
var error: WireGuardAppError? {
- switch (self) {
+ switch self {
case .success: return nil
case .failure(let error): return error
}
}
var isSuccess: Bool {
- switch (self) {
+ switch self {
case .success: return true
case .failure: return false
}
case badArchive
func alertText() -> (String, String)? {
- switch (self) {
+ switch self {
case .cantOpenInputZipFile:
return ("Unable to read zip archive", "The zip archive could not be read.")
case .cantOpenOutputZipFileForWriting:
defer {
unzClose(zipFile)
}
- guard (unzGoToFirstFile(zipFile) == UNZ_OK) else {
- throw ZipArchiveError.badArchive
- }
+ guard unzGoToFirstFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
var resultOfGoToNextFile: Int32
repeat {
- guard (unzOpenCurrentFile(zipFile) == UNZ_OK) else {
- throw ZipArchiveError.badArchive
- }
+ guard unzOpenCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
let bufferSize = 16384 // 16 KiB
var fileNameBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: bufferSize)
dataBuffer.deallocate()
}
- guard (unzGetCurrentFileInfo64(zipFile, nil, fileNameBuffer, UInt(bufferSize), nil, 0, nil, 0) == UNZ_OK) else {
- throw ZipArchiveError.badArchive
- }
+ guard unzGetCurrentFileInfo64(zipFile, nil, fileNameBuffer, UInt(bufferSize), nil, 0, nil, 0) == UNZ_OK else { throw ZipArchiveError.badArchive }
let lastChar = String(cString: fileNameBuffer).suffix(1)
let isDirectory = (lastChar == "/" || lastChar == "\\")
let fileURL = URL(fileURLWithFileSystemRepresentation: fileNameBuffer, isDirectory: isDirectory, relativeTo: nil)
- if (!isDirectory && requiredFileExtensions.contains(fileURL.pathExtension)) {
+ if !isDirectory && requiredFileExtensions.contains(fileURL.pathExtension) {
var unzippedData = Data()
var bytesRead: Int32 = 0
repeat {
bytesRead = unzReadCurrentFile(zipFile, dataBuffer, UInt32(bufferSize))
- if (bytesRead > 0) {
+ if bytesRead > 0 {
let dataRead = dataBuffer.withMemoryRebound(to: UInt8.self, capacity: bufferSize) {
- (buf: UnsafeMutablePointer<UInt8>) -> Data in
- return Data(bytes: buf, count: Int(bytesRead))
+ return Data(bytes: $0, count: Int(bytesRead))
}
unzippedData.append(dataRead)
}
- } while (bytesRead > 0)
+ } while bytesRead > 0
results.append((fileBaseName: fileURL.deletingPathExtension().lastPathComponent, contents: unzippedData))
}
- guard (unzCloseCurrentFile(zipFile) == UNZ_OK) else {
- throw ZipArchiveError.badArchive
- }
+ guard unzCloseCurrentFile(zipFile) == UNZ_OK else { throw ZipArchiveError.badArchive }
resultOfGoToNextFile = unzGoToNextFile(zipFile)
- } while (resultOfGoToNextFile == UNZ_OK)
+ } while resultOfGoToNextFile == UNZ_OK
- if (resultOfGoToNextFile == UNZ_END_OF_LIST_OF_FILE) {
+ if resultOfGoToNextFile == UNZ_END_OF_LIST_OF_FILE {
return results
} else {
throw ZipArchiveError.badArchive
case noTunnelsToExport
func alertText() -> (String, String)? {
- switch (self) {
+ switch self {
case .noTunnelsToExport:
return ("Nothing to export", "There are no tunnels to export")
}
static func exportConfigFiles(tunnelConfigurations: [TunnelConfiguration], to url: URL,
completion: @escaping (WireGuardAppError?) -> Void) {
- guard (!tunnelConfigurations.isEmpty) else {
+ guard !tunnelConfigurations.isEmpty else {
completion(ZipExporterError.noTunnelsToExport)
return
}
for tunnelConfiguration in tunnelConfigurations {
if let contents = WgQuickConfigFileWriter.writeConfigFile(from: tunnelConfiguration) {
let name = tunnelConfiguration.interface.name
- if (name.isEmpty || name == lastTunnelName) { continue }
+ if name.isEmpty || name == lastTunnelName { continue }
inputsToArchiver.append((fileName: "\(name).conf", contents: contents))
lastTunnelName = name
}
}
do {
try ZipArchive.archive(inputs: inputsToArchiver, to: url)
- } catch (let error as WireGuardAppError) {
+ } catch let error as WireGuardAppError {
DispatchQueue.main.async { completion(error) }
return
} catch {
case noTunnelsInZipArchive
func alertText() -> (String, String)? {
- switch (self) {
+ switch self {
case .noTunnelsInZipArchive:
return ("No tunnels in zip archive", "No .conf tunnel files were found inside the zip archive.")
}
for (index, unarchivedFile) in unarchivedFiles.enumerated().reversed() {
let fileBaseName = unarchivedFile.fileBaseName
let trimmedName = fileBaseName.trimmingCharacters(in: .whitespacesAndNewlines)
- if (!trimmedName.isEmpty) {
+ if !trimmedName.isEmpty {
unarchivedFiles[index].fileBaseName = trimmedName
} else {
unarchivedFiles.remove(at: index)
}
}
- if (unarchivedFiles.isEmpty) {
+ if unarchivedFiles.isEmpty {
throw ZipImporterError.noTunnelsInZipArchive
}
- } catch (let error as WireGuardAppError) {
+ } catch let error as WireGuardAppError {
DispatchQueue.main.async { completion(.failure(error)) }
return
} catch {
}
unarchivedFiles.sort { $0.fileBaseName < $1.fileBaseName }
- var configs = Array<TunnelConfiguration?>(repeating: nil, count: unarchivedFiles.count)
+ var configs: [TunnelConfiguration?] = Array(repeating: nil, count: unarchivedFiles.count)
for (index, file) in unarchivedFiles.enumerated() {
- if (index > 0 && file == unarchivedFiles[index - 1]) {
+ if index > 0 && file == unarchivedFiles[index - 1] {
continue
}
guard let fileContents = String(data: file.contents, encoding: .utf8) else {
static func isAllEndpointsAlreadyResolved(endpoints: [Endpoint?]) -> Bool {
for endpoint in endpoints {
guard let endpoint = endpoint else { continue }
- if (!endpoint.hasHostAsIPAddress()) {
+ if !endpoint.hasHostAsIPAddress() {
return false
}
}
static func resolveSync(endpoints: [Endpoint?]) throws -> [Endpoint?] {
let dispatchGroup: DispatchGroup = DispatchGroup()
- if (isAllEndpointsAlreadyResolved(endpoints: endpoints)) {
+ if isAllEndpointsAlreadyResolved(endpoints: endpoints) {
return endpoints
}
- var resolvedEndpoints: [Endpoint?] = Array<Endpoint?>(repeating: nil, count: endpoints.count)
+ var resolvedEndpoints: [Endpoint?] = Array(repeating: nil, count: endpoints.count)
for (index, endpoint) in endpoints.enumerated() {
guard let endpoint = endpoint else { continue }
- if (endpoint.hasHostAsIPAddress()) {
+ if endpoint.hasHostAsIPAddress() {
resolvedEndpoints[index] = endpoint
} else {
let workItem = DispatchWorkItem {
let endpoint = tuple.0
let resolvedEndpoint = tuple.1
if let endpoint = endpoint {
- if (resolvedEndpoint == nil) {
+ if resolvedEndpoint == nil {
// DNS resolution failed
guard let hostname = endpoint.hostname() else { fatalError() }
hostnamesWithDnsResolutionFailure.append(hostname)
}
}
}
- if (!hostnamesWithDnsResolutionFailure.isEmpty) {
+ if !hostnamesWithDnsResolutionFailure.isEmpty {
throw DNSResolverError.dnsResolutionFailed(hostnames: hostnamesWithDnsResolutionFailure)
}
return resolvedEndpoints
ai_addr: nil,
ai_next: nil)
var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0))
- switch (endpoint.host) {
+ switch endpoint.host {
case .name(let name, _):
// The endpoint is a hostname and needs DNS resolution
let returnValue = getaddrinfo(
"\(endpoint.port)".cString(using: .utf8), // Port
&hints,
&resultPointer)
- if (returnValue == 0) {
+ if returnValue == 0 {
// getaddrinfo succeeded
let ipv4Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET_ADDRSTRLEN))
let ipv6Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
var ipv4AddressString: String?
var ipv6AddressString: String?
- while (resultPointer != nil) {
+ while resultPointer != nil {
let result = resultPointer!.pointee
resultPointer = result.ai_next
- if (result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size) {
+ if result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size {
var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee
- if (inet_ntop(result.ai_family, &sa4.sin_addr, ipv4Buffer, socklen_t(INET_ADDRSTRLEN)) != nil) {
+ if inet_ntop(result.ai_family, &sa4.sin_addr, ipv4Buffer, socklen_t(INET_ADDRSTRLEN)) != nil {
ipv4AddressString = String(cString: ipv4Buffer)
// If we found an IPv4 address, we can stop
break
}
- } else if (result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size) {
- if (ipv6AddressString != nil) {
+ } else if result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size {
+ if ipv6AddressString != nil {
// If we already have an IPv6 address, we can skip this one
continue
}
var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee
- if (inet_ntop(result.ai_family, &sa6.sin6_addr, ipv6Buffer, socklen_t(INET6_ADDRSTRLEN)) != nil) {
+ if inet_ntop(result.ai_family, &sa6.sin6_addr, ipv6Buffer, socklen_t(INET6_ADDRSTRLEN)) != nil {
ipv6AddressString = String(cString: ipv6Buffer)
}
}
class ErrorNotifier {
static func errorMessage(for error: PacketTunnelProviderError) -> (String, String)? {
- switch (error) {
+ switch error {
case .savedProtocolConfigurationIsInvalid:
return ("Activation failure", "Could not retrieve tunnel information from the saved configuration")
case .dnsResolutionFailure:
// Setup packetTunnelSettingsGenerator
- let packetTunnelSettingsGenerator = PacketTunnelSettingsGenerator(tunnelConfiguration: tunnelConfiguration,
- resolvedEndpoints: resolvedEndpoints)
+ let packetTunnelSettingsGenerator = PacketTunnelSettingsGenerator(tunnelConfiguration: tunnelConfiguration, resolvedEndpoints: resolvedEndpoints)
// Bring up wireguard-go backend
- let fd = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
- if fd < 0 {
+ let fileDescriptor = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
+ if fileDescriptor < 0 {
wg_log(.error, staticMessage: "Starting tunnel failed: Could not determine file descriptor")
ErrorNotifier.notify(PacketTunnelProviderError.couldNotStartWireGuard, from: self)
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotStartWireGuard)
}
networkMonitor?.start(queue: DispatchQueue(label: "NetworkMonitor"))
- handle = connect(interfaceName: tunnelConfiguration.interface.name, settings: wireguardSettings, fd: fd)
+ handle = connect(interfaceName: tunnelConfiguration.interface.name, settings: wireguardSettings, fileDescriptor: fileDescriptor)
if handle < 0 {
wg_log(.error, staticMessage: "Starting tunnel failed: Could not start WireGuard")
}
}
- private func connect(interfaceName: String, settings: String, fd: Int32) -> Int32 { // swiftlint:disable:this cyclomatic_complexity
+ private func connect(interfaceName: String, settings: String, fileDescriptor: Int32) -> Int32 {
return withStringsAsGoStrings(interfaceName, settings) { (nameGoStr, settingsGoStr) -> Int32 in
- return wgTurnOn(nameGoStr, settingsGoStr, fd)
+ return wgTurnOn(nameGoStr, settingsGoStr, fileDescriptor)
}
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS: "
var msgLine = formatter.string(from: Date()) + message
- if (msgLine.last! != "\n") {
+ if msgLine.last! != "\n" {
msgLine.append("\n")
}
let data = msgLine.data(using: .utf8)
if let listenPort = tunnelConfiguration.interface.listenPort {
wgSettings.append("listen_port=\(listenPort)\n")
}
- if (tunnelConfiguration.peers.count > 0) {
+ if tunnelConfiguration.peers.count > 0 {
wgSettings.append("replace_peers=true\n")
}
assert(tunnelConfiguration.peers.count == resolvedEndpoints.count)
}
let persistentKeepAlive = peer.persistentKeepAlive ?? 0
wgSettings.append("persistent_keepalive_interval=\(persistentKeepAlive)\n")
- if (!peer.allowedIPs.isEmpty) {
+ if !peer.allowedIPs.isEmpty {
wgSettings.append("replace_allowed_ips=true\n")
- for ip in peer.allowedIPs {
- wgSettings.append("allowed_ip=\(ip.stringRepresentation())\n")
- }
+ peer.allowedIPs.forEach { wgSettings.append("allowed_ip=\($0.stringRepresentation())\n") }
}
}
return wgSettings
var remoteAddress: String = "0.0.0.0"
let endpointsCompact = resolvedEndpoints.compactMap({ $0 })
if endpointsCompact.count == 1 {
- switch (endpointsCompact.first!.host) {
+ switch endpointsCompact.first!.host {
case .ipv4(let address):
remoteAddress = "\(address)"
case .ipv6(let address):
// MTU
let mtu = tunnelConfiguration.interface.mtu ?? 0
- if (mtu == 0) {
+ if mtu == 0 {
// 0 imples automatic MTU, where we set overhead as 80 bytes, which is the worst case for WireGuard
networkSettings.tunnelOverheadBytes = 80
} else {
var ipv6NetworkPrefixLengths: [NSNumber] = []
for addressRange in tunnelConfiguration.interface.addresses {
- if (addressRange.address is IPv4Address) {
+ if addressRange.address is IPv4Address {
ipv4Addresses.append("\(addressRange.address)")
ipv4SubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange))
- } else if (addressRange.address is IPv6Address) {
+ } else if addressRange.address is IPv6Address {
ipv6Addresses.append("\(addressRange.address)")
ipv6NetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength))
}
for peer in tunnelConfiguration.peers {
for addressRange in peer.allowedIPs {
- if (addressRange.address is IPv4Address) {
+ if addressRange.address is IPv4Address {
ipv4IncludedRouteAddresses.append("\(addressRange.address)")
ipv4IncludedRouteSubnetMasks.append(PacketTunnelSettingsGenerator.ipv4SubnetMaskString(of: addressRange))
- } else if (addressRange.address is IPv6Address) {
+ } else if addressRange.address is IPv6Address {
ipv6IncludedRouteAddresses.append("\(addressRange.address)")
ipv6IncludedRouteNetworkPrefixLengths.append(NSNumber(value: addressRange.networkPrefixLength))
}
for endpoint in resolvedEndpoints {
guard let endpoint = endpoint else { continue }
- switch (endpoint.host) {
+ switch endpoint.host {
case .ipv4(let address):
ipv4ExcludedRouteAddresses.append("\(address)")
ipv4ExcludedRouteSubnetMasks.append("255.255.255.255") // A single IPv4 address