Nuke trailing spaces

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2018-12-21 23:34:56 +01:00
parent 1fecd8eb6c
commit 7b9d4cb9e3
26 changed files with 173 additions and 173 deletions

View File

@ -7,17 +7,17 @@ import NetworkExtension
protocol LegacyModel: Decodable { protocol LegacyModel: Decodable {
associatedtype Model associatedtype Model
var migrated: Model { get } var migrated: Model { get }
} }
struct LegacyDNSServer: LegacyModel { struct LegacyDNSServer: LegacyModel {
let address: IPAddress let address: IPAddress
var migrated: DNSServer { var migrated: DNSServer {
return DNSServer(address: address) return DNSServer(address: address)
} }
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer() let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self) var data = try container.decode(Data.self)
@ -33,7 +33,7 @@ struct LegacyDNSServer: LegacyModel {
} }
address = ipAddress address = ipAddress
} }
enum DecodingError: Error { enum DecodingError: Error {
case invalidData case invalidData
} }
@ -48,11 +48,11 @@ extension Array where Element == LegacyDNSServer {
struct LegacyEndpoint: LegacyModel { struct LegacyEndpoint: LegacyModel {
let host: Network.NWEndpoint.Host let host: Network.NWEndpoint.Host
let port: Network.NWEndpoint.Port let port: Network.NWEndpoint.Port
var migrated: Endpoint { var migrated: Endpoint {
return Endpoint(host: host, port: port) return Endpoint(host: host, port: port)
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer() let container = try decoder.singleValueContainer()
let endpointString = try container.decode(String.self) let endpointString = try container.decode(String.self)
@ -81,7 +81,7 @@ struct LegacyEndpoint: LegacyModel {
host = NWEndpoint.Host(hostString) host = NWEndpoint.Host(hostString)
port = endpointPort port = endpointPort
} }
enum DecodingError: Error { enum DecodingError: Error {
case invalidData case invalidData
} }
@ -94,7 +94,7 @@ struct LegacyInterfaceConfiguration: LegacyModel {
let listenPort: UInt16? let listenPort: UInt16?
let mtu: UInt16? let mtu: UInt16?
let dns: [LegacyDNSServer] let dns: [LegacyDNSServer]
var migrated: InterfaceConfiguration { var migrated: InterfaceConfiguration {
var interface = InterfaceConfiguration(name: name, privateKey: privateKey) var interface = InterfaceConfiguration(name: name, privateKey: privateKey)
interface.addresses = addresses.migrated interface.addresses = addresses.migrated
@ -108,11 +108,11 @@ struct LegacyInterfaceConfiguration: LegacyModel {
struct LegacyIPAddressRange: LegacyModel { struct LegacyIPAddressRange: LegacyModel {
let address: IPAddress let address: IPAddress
let networkPrefixLength: UInt8 let networkPrefixLength: UInt8
var migrated: IPAddressRange { var migrated: IPAddressRange {
return IPAddressRange(address: address, networkPrefixLength: networkPrefixLength) return IPAddressRange(address: address, networkPrefixLength: networkPrefixLength)
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer() let container = try decoder.singleValueContainer()
var data = try container.decode(Data.self) var data = try container.decode(Data.self)
@ -127,7 +127,7 @@ struct LegacyIPAddressRange: LegacyModel {
guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData } guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData }
address = ipAddress address = ipAddress
} }
enum DecodingError: Error { enum DecodingError: Error {
case invalidData case invalidData
} }
@ -145,7 +145,7 @@ struct LegacyPeerConfiguration: LegacyModel {
let allowedIPs: [LegacyIPAddressRange] let allowedIPs: [LegacyIPAddressRange]
let endpoint: LegacyEndpoint? let endpoint: LegacyEndpoint?
let persistentKeepAlive: UInt16? let persistentKeepAlive: UInt16?
var migrated: PeerConfiguration { var migrated: PeerConfiguration {
var configuration = PeerConfiguration(publicKey: publicKey) var configuration = PeerConfiguration(publicKey: publicKey)
configuration.preSharedKey = preSharedKey configuration.preSharedKey = preSharedKey
@ -165,14 +165,14 @@ extension Array where Element == LegacyPeerConfiguration {
final class LegacyTunnelConfiguration: LegacyModel { final class LegacyTunnelConfiguration: LegacyModel {
let interface: LegacyInterfaceConfiguration let interface: LegacyInterfaceConfiguration
let peers: [LegacyPeerConfiguration] let peers: [LegacyPeerConfiguration]
var migrated: TunnelConfiguration { var migrated: TunnelConfiguration {
return TunnelConfiguration(interface: interface.migrated, peers: peers.migrated) return TunnelConfiguration(interface: interface.migrated, peers: peers.migrated)
} }
} }
extension NETunnelProviderProtocol { extension NETunnelProviderProtocol {
@discardableResult @discardableResult
func migrateConfigurationIfNeeded() -> Bool { func migrateConfigurationIfNeeded() -> Bool {
guard let configurationVersion = providerConfiguration?["tunnelConfigurationVersion"] as? Int else { return false } guard let configurationVersion = providerConfiguration?["tunnelConfigurationVersion"] as? Int else { return false }
@ -183,11 +183,11 @@ extension NETunnelProviderProtocol {
} }
return true return true
} }
private func migrateFromConfigurationV1() { private func migrateFromConfigurationV1() {
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return } guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return }
guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return } guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return }
providerConfiguration = [Keys.wgQuickConfig.rawValue: configuration.migrated.asWgQuickConfig()] providerConfiguration = [Keys.wgQuickConfig.rawValue: configuration.migrated.asWgQuickConfig()]
} }
} }

View File

@ -8,7 +8,7 @@ public class Logger {
enum LoggerError: Error { enum LoggerError: Error {
case openFailure case openFailure
} }
static var global: Logger? static var global: Logger?
var log: OpaquePointer var log: OpaquePointer

View File

@ -6,7 +6,7 @@ import Network
struct DNSServer { struct DNSServer {
let address: IPAddress let address: IPAddress
init(address: IPAddress) { init(address: IPAddress) {
self.address = address self.address = address
} }
@ -16,7 +16,7 @@ extension DNSServer {
var stringRepresentation: String { var stringRepresentation: String {
return "\(address)" return "\(address)"
} }
init?(from addressString: String) { init?(from addressString: String) {
if let addr = IPv4Address(addressString) { if let addr = IPv4Address(addressString) {
address = addr address = addr

View File

@ -7,7 +7,7 @@ import Network
struct Endpoint { struct Endpoint {
let host: NWEndpoint.Host let host: NWEndpoint.Host
let port: NWEndpoint.Port let port: NWEndpoint.Port
init(host: NWEndpoint.Host, port: NWEndpoint.Port) { init(host: NWEndpoint.Host, port: NWEndpoint.Port) {
self.host = host self.host = host
self.port = port self.port = port
@ -25,7 +25,7 @@ extension Endpoint {
return "[\(address)]:\(port)" return "[\(address)]:\(port)"
} }
} }
init?(from string: String) { init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in // Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/WireGuard/tree/src/tools/config.c // https://git.zx2c4.com/WireGuard/tree/src/tools/config.c

View File

@ -7,7 +7,7 @@ import Network
struct IPAddressRange { struct IPAddressRange {
let address: IPAddress let address: IPAddress
var networkPrefixLength: UInt8 var networkPrefixLength: UInt8
init(address: IPAddress, networkPrefixLength: UInt8) { init(address: IPAddress, networkPrefixLength: UInt8) {
self.address = address self.address = address
self.networkPrefixLength = networkPrefixLength self.networkPrefixLength = networkPrefixLength
@ -18,13 +18,13 @@ extension IPAddressRange {
var stringRepresentation: String { var stringRepresentation: String {
return "\(address)/\(networkPrefixLength)" return "\(address)/\(networkPrefixLength)"
} }
init?(from string: String) { init?(from string: String) {
guard let parsed = IPAddressRange.parseAddressString(string) else { return nil } guard let parsed = IPAddressRange.parseAddressString(string) else { return nil }
address = parsed.0 address = parsed.0
networkPrefixLength = parsed.1 networkPrefixLength = parsed.1
} }
private static func parseAddressString(_ string: String) -> (IPAddress, UInt8)? { private static func parseAddressString(_ string: String) -> (IPAddress, UInt8)? {
let endOfIPAddress = string.lastIndex(of: "/") ?? string.endIndex let endOfIPAddress = string.lastIndex(of: "/") ?? string.endIndex
let addressString = String(string[string.startIndex ..< endOfIPAddress]) let addressString = String(string[string.startIndex ..< endOfIPAddress])
@ -36,7 +36,7 @@ extension IPAddressRange {
} else { } else {
return nil return nil
} }
let maxNetworkPrefixLength: UInt8 = address is IPv4Address ? 32 : 128 let maxNetworkPrefixLength: UInt8 = address is IPv4Address ? 32 : 128
var networkPrefixLength: UInt8 var networkPrefixLength: UInt8
if endOfIPAddress < string.endIndex { // "/" was located if endOfIPAddress < string.endIndex { // "/" was located
@ -48,7 +48,7 @@ extension IPAddressRange {
} else { } else {
networkPrefixLength = maxNetworkPrefixLength networkPrefixLength = maxNetworkPrefixLength
} }
return (address, networkPrefixLength) return (address, networkPrefixLength)
} }
} }

View File

@ -10,7 +10,7 @@ struct InterfaceConfiguration {
var listenPort: UInt16? var listenPort: UInt16?
var mtu: UInt16? var mtu: UInt16?
var dns = [DNSServer]() var dns = [DNSServer]()
init(name: String?, privateKey: Data) { init(name: String?, privateKey: Data) {
self.name = name self.name = name
self.privateKey = privateKey self.privateKey = privateKey

View File

@ -17,7 +17,7 @@ struct PeerConfiguration {
var allowedIPs = [IPAddressRange]() var allowedIPs = [IPAddressRange]()
var endpoint: Endpoint? var endpoint: Endpoint?
var persistentKeepAlive: UInt16? var persistentKeepAlive: UInt16?
init(publicKey: Data) { init(publicKey: Data) {
self.publicKey = publicKey self.publicKey = publicKey
if publicKey.count != TunnelConfiguration.keyLength { if publicKey.count != TunnelConfiguration.keyLength {

View File

@ -6,18 +6,18 @@ import NetworkExtension
private var tunnelNameKey: Void? private var tunnelNameKey: Void?
extension NETunnelProviderProtocol { extension NETunnelProviderProtocol {
enum Keys: String { enum Keys: String {
case wgQuickConfig = "WgQuickConfig" case wgQuickConfig = "WgQuickConfig"
} }
convenience init?(tunnelConfiguration: TunnelConfiguration) { convenience init?(tunnelConfiguration: TunnelConfiguration) {
self.init() self.init()
let appId = Bundle.main.bundleIdentifier! let appId = Bundle.main.bundleIdentifier!
providerBundleIdentifier = "\(appId).network-extension" providerBundleIdentifier = "\(appId).network-extension"
providerConfiguration = [Keys.wgQuickConfig.rawValue: tunnelConfiguration.asWgQuickConfig()] providerConfiguration = [Keys.wgQuickConfig.rawValue: tunnelConfiguration.asWgQuickConfig()]
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint } let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
if endpoints.count == 1 { if endpoints.count == 1 {
serverAddress = endpoints[0].stringRepresentation serverAddress = endpoints[0].stringRepresentation
@ -26,14 +26,14 @@ extension NETunnelProviderProtocol {
} else { } else {
serverAddress = "Multiple endpoints" serverAddress = "Multiple endpoints"
} }
username = tunnelConfiguration.interface.name username = tunnelConfiguration.interface.name
} }
func tunnelConfiguration(name: String?) -> TunnelConfiguration? { func tunnelConfiguration(name: String?) -> TunnelConfiguration? {
migrateConfigurationIfNeeded() migrateConfigurationIfNeeded()
guard let serializedConfig = providerConfiguration?[Keys.wgQuickConfig.rawValue] as? String else { return nil } guard let serializedConfig = providerConfiguration?[Keys.wgQuickConfig.rawValue] as? String else { return nil }
return try? TunnelConfiguration(serializedConfig, name: name) return try? TunnelConfiguration(serializedConfig, name: name)
} }
} }

View File

@ -4,7 +4,7 @@
import Foundation import Foundation
extension String { extension String {
func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] { func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] {
return split(separator: separator) return split(separator: separator)
.map { .map {
@ -15,11 +15,11 @@ extension String {
} }
} }
} }
} }
extension Optional where Wrapped == String { extension Optional where Wrapped == String {
func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] { func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] {
switch self { switch self {
case .none: case .none:
@ -28,5 +28,5 @@ extension Optional where Wrapped == String {
return wrapped.splitToArray(separator: separator, trimmingCharacters: trimmingCharacters) return wrapped.splitToArray(separator: separator, trimmingCharacters: trimmingCharacters)
} }
} }
} }

View File

@ -4,13 +4,13 @@
import Foundation import Foundation
extension TunnelConfiguration { extension TunnelConfiguration {
enum ParserState { enum ParserState {
case inInterfaceSection case inInterfaceSection
case inPeerSection case inPeerSection
case notInASection case notInASection
} }
enum ParseError: Error { enum ParseError: Error {
case invalidLine(_ line: String.SubSequence) case invalidLine(_ line: String.SubSequence)
case noInterface case noInterface
@ -19,17 +19,17 @@ extension TunnelConfiguration {
case multiplePeersWithSamePublicKey case multiplePeersWithSamePublicKey
case invalidPeer case invalidPeer
} }
//swiftlint:disable:next cyclomatic_complexity function_body_length //swiftlint:disable:next cyclomatic_complexity function_body_length
convenience init(_ wgQuickConfig: String, name: String?) throws { convenience init(_ wgQuickConfig: String, name: String?) throws {
var interfaceConfiguration: InterfaceConfiguration? var interfaceConfiguration: InterfaceConfiguration?
var peerConfigurations = [PeerConfiguration]() var peerConfigurations = [PeerConfiguration]()
let lines = wgQuickConfig.split(separator: "\n") let lines = wgQuickConfig.split(separator: "\n")
var parserState = ParserState.notInASection var parserState = ParserState.notInASection
var attributes = [String: String]() var attributes = [String: String]()
for (lineIndex, line) in lines.enumerated() { for (lineIndex, line) in lines.enumerated() {
var trimmedLine: String var trimmedLine: String
if let commentRange = line.range(of: "#") { if let commentRange = line.range(of: "#") {
@ -37,12 +37,12 @@ extension TunnelConfiguration {
} else { } else {
trimmedLine = String(line) trimmedLine = String(line)
} }
trimmedLine = trimmedLine.trimmingCharacters(in: .whitespaces) trimmedLine = trimmedLine.trimmingCharacters(in: .whitespaces)
guard !trimmedLine.isEmpty else { continue } guard !trimmedLine.isEmpty else { continue }
let lowercasedLine = line.lowercased() let lowercasedLine = line.lowercased()
if let equalsIndex = line.firstIndex(of: "=") { if let equalsIndex = line.firstIndex(of: "=") {
// Line contains an attribute // Line contains an attribute
let key = line[..<equalsIndex].trimmingCharacters(in: .whitespaces).lowercased() let key = line[..<equalsIndex].trimmingCharacters(in: .whitespaces).lowercased()
@ -56,9 +56,9 @@ extension TunnelConfiguration {
} else if lowercasedLine != "[interface]" && lowercasedLine != "[peer]" { } else if lowercasedLine != "[interface]" && lowercasedLine != "[peer]" {
throw ParseError.invalidLine(line) throw ParseError.invalidLine(line)
} }
let isLastLine = lineIndex == lines.count - 1 let isLastLine = 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 // Previous section has ended; process the attributes collected so far
if parserState == .inInterfaceSection { if parserState == .inInterfaceSection {
@ -70,7 +70,7 @@ extension TunnelConfiguration {
peerConfigurations.append(peer) peerConfigurations.append(peer)
} }
} }
if lowercasedLine == "[interface]" { if lowercasedLine == "[interface]" {
parserState = .inInterfaceSection parserState = .inInterfaceSection
attributes.removeAll() attributes.removeAll()
@ -79,20 +79,20 @@ extension TunnelConfiguration {
attributes.removeAll() attributes.removeAll()
} }
} }
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey } let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray) let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if peerPublicKeysArray.count != peerPublicKeysSet.count { if peerPublicKeysArray.count != peerPublicKeysSet.count {
throw ParseError.multiplePeersWithSamePublicKey throw ParseError.multiplePeersWithSamePublicKey
} }
if let interfaceConfiguration = interfaceConfiguration { if let interfaceConfiguration = interfaceConfiguration {
self.init(interface: interfaceConfiguration, peers: peerConfigurations) self.init(interface: interfaceConfiguration, peers: peerConfigurations)
} else { } else {
throw ParseError.noInterface throw ParseError.noInterface
} }
} }
func asWgQuickConfig() -> String { func asWgQuickConfig() -> String {
var output = "[Interface]\n" var output = "[Interface]\n"
output.append("PrivateKey = \(interface.privateKey.base64EncodedString())\n") output.append("PrivateKey = \(interface.privateKey.base64EncodedString())\n")
@ -110,7 +110,7 @@ extension TunnelConfiguration {
if let mtu = interface.mtu { if let mtu = interface.mtu {
output.append("MTU = \(mtu)\n") output.append("MTU = \(mtu)\n")
} }
for peer in peers { for peer in peers {
output.append("\n[Peer]\n") output.append("\n[Peer]\n")
output.append("PublicKey = \(peer.publicKey.base64EncodedString())\n") output.append("PublicKey = \(peer.publicKey.base64EncodedString())\n")
@ -128,10 +128,10 @@ extension TunnelConfiguration {
output.append("PersistentKeepalive = \(persistentKeepAlive)\n") output.append("PersistentKeepalive = \(persistentKeepAlive)\n")
} }
} }
return output return output
} }
//swiftlint:disable:next cyclomatic_complexity //swiftlint:disable:next cyclomatic_complexity
private static func collate(interfaceAttributes attributes: [String: String], name: String?) -> InterfaceConfiguration? { private static func collate(interfaceAttributes attributes: [String: String], name: String?) -> InterfaceConfiguration? {
// required wg fields // required wg fields
@ -166,7 +166,7 @@ extension TunnelConfiguration {
} }
return interface return interface
} }
//swiftlint:disable:next cyclomatic_complexity //swiftlint:disable:next cyclomatic_complexity
private static func collate(peerAttributes attributes: [String: String]) -> PeerConfiguration? { private static func collate(peerAttributes attributes: [String: String]) -> PeerConfiguration? {
// required wg fields // required wg fields
@ -196,5 +196,5 @@ extension TunnelConfiguration {
} }
return peer return peer
} }
} }

View File

@ -53,7 +53,7 @@ enum TunnelsManagerActivationAttemptError: WireGuardAppError {
enum TunnelsManagerActivationError: WireGuardAppError { enum TunnelsManagerActivationError: WireGuardAppError {
case activationFailed(wasOnDemandEnabled: Bool) case activationFailed(wasOnDemandEnabled: Bool)
case activationFailedWithExtensionError(title: String, message: String, wasOnDemandEnabled: Bool) case activationFailedWithExtensionError(title: String, message: String, wasOnDemandEnabled: Bool)
var alertText: AlertText { var alertText: AlertText {
switch self { switch self {
case .activationFailed(let wasOnDemandEnabled): case .activationFailed(let wasOnDemandEnabled):

View File

@ -12,7 +12,7 @@ import NetworkExtension
case reasserting // Not a possible state at present case reasserting // Not a possible state at present
case restarting // Restarting tunnel (done after saving modifications to an active tunnel) case restarting // Restarting tunnel (done after saving modifications to an active tunnel)
case waiting // Waiting for another tunnel to be brought down case waiting // Waiting for another tunnel to be brought down
init(from systemStatus: NEVPNStatus) { init(from systemStatus: NEVPNStatus) {
switch systemStatus { switch systemStatus {
case .connected: case .connected:

View File

@ -41,7 +41,7 @@ class TunnelsManager {
completionHandler(.failure(TunnelsManagerError.systemErrorOnListingTunnels(systemError: error))) completionHandler(.failure(TunnelsManagerError.systemErrorOnListingTunnels(systemError: error)))
return return
} }
let tunnelManagers = managers ?? [] let tunnelManagers = managers ?? []
tunnelManagers.forEach { tunnelManager in tunnelManagers.forEach { tunnelManager in
if (tunnelManager.protocolConfiguration as? NETunnelProviderProtocol)?.migrateConfigurationIfNeeded() == true { if (tunnelManager.protocolConfiguration as? NETunnelProviderProtocol)?.migrateConfigurationIfNeeded() == true {
@ -78,9 +78,9 @@ class TunnelsManager {
completionHandler(.failure(TunnelsManagerError.systemErrorOnAddTunnel(systemError: error!))) completionHandler(.failure(TunnelsManagerError.systemErrorOnAddTunnel(systemError: error!)))
return return
} }
guard let self = self else { return } guard let self = self else { return }
let tunnel = TunnelContainer(tunnel: tunnelProviderManager) let tunnel = TunnelContainer(tunnel: tunnelProviderManager)
self.tunnels.append(tunnel) self.tunnels.append(tunnel)
self.tunnels.sort { $0.name < $1.name } self.tunnels.sort { $0.name < $1.name }
@ -126,7 +126,7 @@ class TunnelsManager {
tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration) tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = (tunnelConfiguration).interface.name tunnelProviderManager.localizedDescription = (tunnelConfiguration).interface.name
tunnelProviderManager.isEnabled = true tunnelProviderManager.isEnabled = true
let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && activateOnDemandSetting.isActivateOnDemandEnabled let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && activateOnDemandSetting.isActivateOnDemandEnabled
activateOnDemandSetting.apply(on: tunnelProviderManager) activateOnDemandSetting.apply(on: tunnelProviderManager)
@ -137,7 +137,7 @@ class TunnelsManager {
return return
} }
guard let self = self else { return } guard let self = self else { return }
if isNameChanged { if isNameChanged {
let oldIndex = self.tunnels.firstIndex(of: tunnel)! let oldIndex = self.tunnels.firstIndex(of: tunnel)!
self.tunnels.sort { $0.name < $1.name } self.tunnels.sort { $0.name < $1.name }
@ -351,11 +351,11 @@ class TunnelContainer: NSObject {
var tunnelConfiguration: TunnelConfiguration? { var tunnelConfiguration: TunnelConfiguration? {
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration(name: tunnelProvider.localizedDescription) return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration(name: tunnelProvider.localizedDescription)
} }
var activateOnDemandSetting: ActivateOnDemandSetting { var activateOnDemandSetting: ActivateOnDemandSetting {
return ActivateOnDemandSetting(from: tunnelProvider) return ActivateOnDemandSetting(from: tunnelProvider)
} }
init(tunnel: NETunnelProviderManager) { init(tunnel: NETunnelProviderManager) {
name = tunnel.localizedDescription ?? "Unnamed" name = tunnel.localizedDescription ?? "Unnamed"
let status = TunnelStatus(from: tunnel.connection.status) let status = TunnelStatus(from: tunnel.connection.status)

View File

@ -10,42 +10,42 @@ class BorderedTextButton: UIView {
button.titleLabel?.adjustsFontForContentSizeCategory = true button.titleLabel?.adjustsFontForContentSizeCategory = true
return button return button
}() }()
override var intrinsicContentSize: CGSize { override var intrinsicContentSize: CGSize {
let buttonSize = button.intrinsicContentSize let buttonSize = button.intrinsicContentSize
return CGSize(width: buttonSize.width + 32, height: buttonSize.height + 16) return CGSize(width: buttonSize.width + 32, height: buttonSize.height + 16)
} }
var title: String { var title: String {
get { return button.title(for: .normal) ?? "" } get { return button.title(for: .normal) ?? "" }
set(value) { button.setTitle(value, for: .normal) } set(value) { button.setTitle(value, for: .normal) }
} }
var onTapped: (() -> Void)? var onTapped: (() -> Void)?
init() { init() {
super.init(frame: CGRect.zero) super.init(frame: CGRect.zero)
layer.borderWidth = 1 layer.borderWidth = 1
layer.cornerRadius = 5 layer.cornerRadius = 5
layer.borderColor = button.tintColor.cgColor layer.borderColor = button.tintColor.cgColor
addSubview(button) addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: centerXAnchor), button.centerXAnchor.constraint(equalTo: centerXAnchor),
button.centerYAnchor.constraint(equalTo: centerYAnchor) button.centerYAnchor.constraint(equalTo: centerYAnchor)
]) ])
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func buttonTapped() { @objc func buttonTapped() {
onTapped?() onTapped?()
} }
} }

View File

@ -13,20 +13,20 @@ class ButtonCell: UITableViewCell {
set(value) { button.tintColor = value ? .red : buttonStandardTintColor } set(value) { button.tintColor = value ? .red : buttonStandardTintColor }
} }
var onTapped: (() -> Void)? var onTapped: (() -> Void)?
let button: UIButton = { let button: UIButton = {
let button = UIButton(type: .system) let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body) button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
button.titleLabel?.adjustsFontForContentSizeCategory = true button.titleLabel?.adjustsFontForContentSizeCategory = true
return button return button
}() }()
var buttonStandardTintColor: UIColor var buttonStandardTintColor: UIColor
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
buttonStandardTintColor = button.tintColor buttonStandardTintColor = button.tintColor
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(button) contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -34,18 +34,18 @@ class ButtonCell: UITableViewCell {
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: button.bottomAnchor), contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: button.bottomAnchor),
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor) button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)
]) ])
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
} }
@objc func buttonTapped() { @objc func buttonTapped() {
onTapped?() onTapped?()
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
buttonText = "" buttonText = ""

View File

@ -13,16 +13,16 @@ class CheckmarkCell: UITableViewCell {
accessoryType = isChecked ? .checkmark : .none accessoryType = isChecked ? .checkmark : .none
} }
} }
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
isChecked = false isChecked = false
super.init(style: .default, reuseIdentifier: reuseIdentifier) super.init(style: .default, reuseIdentifier: reuseIdentifier)
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
message = "" message = ""

View File

@ -4,7 +4,7 @@
import UIKit import UIKit
class KeyValueCell: UITableViewCell { class KeyValueCell: UITableViewCell {
let keyLabel: UILabel = { let keyLabel: UILabel = {
let keyLabel = UILabel() let keyLabel = UILabel()
keyLabel.font = UIFont.preferredFont(forTextStyle: .body) keyLabel.font = UIFont.preferredFont(forTextStyle: .body)
@ -13,7 +13,7 @@ class KeyValueCell: UITableViewCell {
keyLabel.textAlignment = .left keyLabel.textAlignment = .left
return keyLabel return keyLabel
}() }()
let valueLabelScrollView: UIScrollView = { let valueLabelScrollView: UIScrollView = {
let scrollView = UIScrollView(frame: .zero) let scrollView = UIScrollView(frame: .zero)
scrollView.isDirectionalLockEnabled = true scrollView.isDirectionalLockEnabled = true
@ -21,7 +21,7 @@ class KeyValueCell: UITableViewCell {
scrollView.showsVerticalScrollIndicator = false scrollView.showsVerticalScrollIndicator = false
return scrollView return scrollView
}() }()
let valueTextField: UITextField = { let valueTextField: UITextField = {
let valueTextField = UITextField() let valueTextField = UITextField()
valueTextField.textAlignment = .right valueTextField.textAlignment = .right
@ -34,7 +34,7 @@ class KeyValueCell: UITableViewCell {
valueTextField.textColor = .gray valueTextField.textColor = .gray
return valueTextField return valueTextField
}() }()
var copyableGesture = true var copyableGesture = true
var key: String { var key: String {
@ -53,7 +53,7 @@ class KeyValueCell: UITableViewCell {
get { return valueTextField.keyboardType } get { return valueTextField.keyboardType }
set(value) { valueTextField.keyboardType = value } set(value) { valueTextField.keyboardType = value }
} }
var isValueValid = true { var isValueValid = true {
didSet { didSet {
if isValueValid { if isValueValid {
@ -63,26 +63,26 @@ class KeyValueCell: UITableViewCell {
} }
} }
} }
var isStackedHorizontally = false var isStackedHorizontally = false
var isStackedVertically = false var isStackedVertically = false
var contentSizeBasedConstraints = [NSLayoutConstraint]() var contentSizeBasedConstraints = [NSLayoutConstraint]()
var onValueChanged: ((String) -> Void)? var onValueChanged: ((String) -> Void)?
var onValueBeingEdited: ((String) -> Void)? var onValueBeingEdited: ((String) -> Void)?
private var textFieldValueOnBeginEditing: String = "" private var textFieldValueOnBeginEditing: String = ""
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(keyLabel) contentView.addSubview(keyLabel)
keyLabel.translatesAutoresizingMaskIntoConstraints = false keyLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor), keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
keyLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5) keyLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 0.5)
]) ])
valueTextField.delegate = self valueTextField.delegate = self
valueLabelScrollView.addSubview(valueTextField) valueLabelScrollView.addSubview(valueTextField)
valueTextField.translatesAutoresizingMaskIntoConstraints = false valueTextField.translatesAutoresizingMaskIntoConstraints = false
@ -96,31 +96,31 @@ class KeyValueCell: UITableViewCell {
let expandToFitValueLabelConstraint = NSLayoutConstraint(item: valueTextField, attribute: .width, relatedBy: .equal, toItem: valueLabelScrollView, attribute: .width, multiplier: 1, constant: 0) let expandToFitValueLabelConstraint = NSLayoutConstraint(item: valueTextField, attribute: .width, relatedBy: .equal, toItem: valueLabelScrollView, attribute: .width, multiplier: 1, constant: 0)
expandToFitValueLabelConstraint.priority = .defaultLow + 1 expandToFitValueLabelConstraint.priority = .defaultLow + 1
expandToFitValueLabelConstraint.isActive = true expandToFitValueLabelConstraint.isActive = true
contentView.addSubview(valueLabelScrollView) contentView.addSubview(valueLabelScrollView)
contentView.addSubview(valueLabelScrollView) contentView.addSubview(valueLabelScrollView)
valueLabelScrollView.translatesAutoresizingMaskIntoConstraints = false valueLabelScrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
valueLabelScrollView.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor), valueLabelScrollView.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueLabelScrollView.bottomAnchor, multiplier: 0.5) contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueLabelScrollView.bottomAnchor, multiplier: 0.5)
]) ])
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal) keyLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
valueLabelScrollView.setContentHuggingPriority(.defaultLow, for: .horizontal) valueLabelScrollView.setContentHuggingPriority(.defaultLow, for: .horizontal)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:))) let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
addGestureRecognizer(gestureRecognizer) addGestureRecognizer(gestureRecognizer)
isUserInteractionEnabled = true isUserInteractionEnabled = true
configureForContentSize() configureForContentSize()
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func configureForContentSize() { func configureForContentSize() {
var constraints = [NSLayoutConstraint]() var constraints = [NSLayoutConstraint]()
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory { if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
@ -152,13 +152,13 @@ class KeyValueCell: UITableViewCell {
contentSizeBasedConstraints = constraints contentSizeBasedConstraints = constraints
} }
} }
@objc func handleTapGesture(_ recognizer: UIGestureRecognizer) { @objc func handleTapGesture(_ recognizer: UIGestureRecognizer) {
if !copyableGesture { if !copyableGesture {
return return
} }
guard recognizer.state == .recognized else { return } guard recognizer.state == .recognized else { return }
if let recognizerView = recognizer.view, if let recognizerView = recognizer.view,
let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder() { let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder() {
let menuController = UIMenuController.shared let menuController = UIMenuController.shared
@ -166,19 +166,19 @@ class KeyValueCell: UITableViewCell {
menuController.setMenuVisible(true, animated: true) menuController.setMenuVisible(true, animated: true)
} }
} }
override var canBecomeFirstResponder: Bool { override var canBecomeFirstResponder: Bool {
return true return true
} }
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return (action == #selector(UIResponderStandardEditActions.copy(_:))) return (action == #selector(UIResponderStandardEditActions.copy(_:)))
} }
override func copy(_ sender: Any?) { override func copy(_ sender: Any?) {
UIPasteboard.general.string = valueTextField.text UIPasteboard.general.string = valueTextField.text
} }
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
copyableGesture = true copyableGesture = true
@ -194,18 +194,18 @@ class KeyValueCell: UITableViewCell {
} }
extension KeyValueCell: UITextFieldDelegate { extension KeyValueCell: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) { func textFieldDidBeginEditing(_ textField: UITextField) {
textFieldValueOnBeginEditing = textField.text ?? "" textFieldValueOnBeginEditing = textField.text ?? ""
isValueValid = true isValueValid = true
} }
func textFieldDidEndEditing(_ textField: UITextField) { func textFieldDidEndEditing(_ textField: UITextField) {
let isModified = textField.text ?? "" != textFieldValueOnBeginEditing let isModified = textField.text ?? "" != textFieldValueOnBeginEditing
guard isModified else { return } guard isModified else { return }
onValueChanged?(textField.text ?? "") onValueChanged?(textField.text ?? "")
} }
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let onValueBeingEdited = onValueBeingEdited { if let onValueBeingEdited = onValueBeingEdited {
let modifiedText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) let modifiedText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
@ -213,5 +213,5 @@ extension KeyValueCell: UITextFieldDelegate {
} }
return true return true
} }
} }

View File

@ -19,14 +19,14 @@ class SwitchCell: UITableViewCell {
textLabel?.textColor = value ? .black : .gray textLabel?.textColor = value ? .black : .gray
} }
} }
var onSwitchToggled: ((Bool) -> Void)? var onSwitchToggled: ((Bool) -> Void)?
let switchView = UISwitch() let switchView = UISwitch()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier) super.init(style: .default, reuseIdentifier: reuseIdentifier)
accessoryView = switchView accessoryView = switchView
switchView.addTarget(self, action: #selector(switchToggled), for: .valueChanged) switchView.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
} }
@ -34,11 +34,11 @@ class SwitchCell: UITableViewCell {
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func switchToggled() { @objc func switchToggled() {
onSwitchToggled?(switchView.isOn) onSwitchToggled?(switchView.isOn)
} }
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
isEnabled = true isEnabled = true

View File

@ -4,40 +4,40 @@
import UIKit import UIKit
class TunnelEditKeyValueCell: KeyValueCell { class TunnelEditKeyValueCell: KeyValueCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
keyLabel.textAlignment = .right keyLabel.textAlignment = .right
valueTextField.textAlignment = .left valueTextField.textAlignment = .left
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 0.4, constant: 0) let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 0.4, constant: 0)
// In case the key doesn't fit into 0.4 * width, // In case the key doesn't fit into 0.4 * width,
// set a CR priority > the 0.4-constraint's priority. // set a CR priority > the 0.4-constraint's priority.
widthRatioConstraint.priority = .defaultHigh + 1 widthRatioConstraint.priority = .defaultHigh + 1
widthRatioConstraint.isActive = true widthRatioConstraint.isActive = true
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }
class TunnelEditEditableKeyValueCell: TunnelEditKeyValueCell { class TunnelEditEditableKeyValueCell: TunnelEditKeyValueCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
copyableGesture = false copyableGesture = false
valueTextField.textColor = .black valueTextField.textColor = .black
valueTextField.isEnabled = true valueTextField.isEnabled = true
valueLabelScrollView.isScrollEnabled = false valueLabelScrollView.isScrollEnabled = false
valueTextField.widthAnchor.constraint(equalTo: valueLabelScrollView.widthAnchor).isActive = true valueTextField.widthAnchor.constraint(equalTo: valueLabelScrollView.widthAnchor).isActive = true
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }

View File

@ -19,7 +19,7 @@ class TunnelListCell: UITableViewCell {
} }
} }
var onSwitchToggled: ((Bool) -> Void)? var onSwitchToggled: ((Bool) -> Void)?
let nameLabel: UILabel = { let nameLabel: UILabel = {
let nameLabel = UILabel() let nameLabel = UILabel()
nameLabel.font = UIFont.preferredFont(forTextStyle: .body) nameLabel.font = UIFont.preferredFont(forTextStyle: .body)
@ -27,35 +27,35 @@ class TunnelListCell: UITableViewCell {
nameLabel.numberOfLines = 0 nameLabel.numberOfLines = 0
return nameLabel return nameLabel
}() }()
let busyIndicator: UIActivityIndicatorView = { let busyIndicator: UIActivityIndicatorView = {
let busyIndicator = UIActivityIndicatorView(style: .gray) let busyIndicator = UIActivityIndicatorView(style: .gray)
busyIndicator.hidesWhenStopped = true busyIndicator.hidesWhenStopped = true
return busyIndicator return busyIndicator
}() }()
let statusSwitch = UISwitch() let statusSwitch = UISwitch()
private var statusObservationToken: AnyObject? private var statusObservationToken: AnyObject?
private var nameObservationToken: AnyObject? private var nameObservationToken: AnyObject?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(statusSwitch) contentView.addSubview(statusSwitch)
statusSwitch.translatesAutoresizingMaskIntoConstraints = false statusSwitch.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
statusSwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), statusSwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
contentView.rightAnchor.constraint(equalTo: statusSwitch.rightAnchor) contentView.rightAnchor.constraint(equalTo: statusSwitch.rightAnchor)
]) ])
contentView.addSubview(busyIndicator) contentView.addSubview(busyIndicator)
busyIndicator.translatesAutoresizingMaskIntoConstraints = false busyIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
busyIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), busyIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
statusSwitch.leftAnchor.constraint(equalToSystemSpacingAfter: busyIndicator.rightAnchor, multiplier: 1) statusSwitch.leftAnchor.constraint(equalToSystemSpacingAfter: busyIndicator.rightAnchor, multiplier: 1)
]) ])
contentView.addSubview(nameLabel) contentView.addSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
@ -67,16 +67,16 @@ class TunnelListCell: UITableViewCell {
busyIndicator.leftAnchor.constraint(equalToSystemSpacingAfter: nameLabel.rightAnchor, multiplier: 1), busyIndicator.leftAnchor.constraint(equalToSystemSpacingAfter: nameLabel.rightAnchor, multiplier: 1),
bottomAnchorConstraint bottomAnchorConstraint
]) ])
accessoryType = .disclosureIndicator accessoryType = .disclosureIndicator
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged) statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
} }
@objc func switchToggled() { @objc func switchToggled() {
onSwitchToggled?(statusSwitch.isOn) onSwitchToggled?(statusSwitch.isOn)
} }
private func update(from status: TunnelStatus?) { private func update(from status: TunnelStatus?) {
guard let status = status else { guard let status = status else {
reset() reset()
@ -93,17 +93,17 @@ class TunnelListCell: UITableViewCell {
} }
} }
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
private func reset() { private func reset() {
statusSwitch.isOn = false statusSwitch.isOn = false
statusSwitch.isUserInteractionEnabled = false statusSwitch.isUserInteractionEnabled = false
busyIndicator.stopAnimating() busyIndicator.stopAnimating()
} }
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
reset() reset()

View File

@ -58,12 +58,12 @@ class SettingsTableViewController: UITableViewController {
override func viewDidLayoutSubviews() { override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
guard let logo = tableView.tableFooterView else { return } guard let logo = tableView.tableFooterView else { return }
let bottomPadding = max(tableView.layoutMargins.bottom, 10) let bottomPadding = max(tableView.layoutMargins.bottom, 10)
let fullHeight = max(tableView.contentSize.height, tableView.bounds.size.height - tableView.layoutMargins.top - bottomPadding) let fullHeight = max(tableView.contentSize.height, tableView.bounds.size.height - tableView.layoutMargins.top - bottomPadding)
let imageAspectRatio = logo.intrinsicContentSize.width / logo.intrinsicContentSize.height let imageAspectRatio = logo.intrinsicContentSize.width / logo.intrinsicContentSize.height
var height = tableView.estimatedRowHeight * 1.5 var height = tableView.estimatedRowHeight * 1.5
var width = height * imageAspectRatio var width = height * imageAspectRatio
let maxWidth = view.bounds.size.width - max(tableView.layoutMargins.left + tableView.layoutMargins.right, 20) let maxWidth = view.bounds.size.width - max(tableView.layoutMargins.left + tableView.layoutMargins.right, 20)
@ -71,11 +71,11 @@ class SettingsTableViewController: UITableViewController {
width = maxWidth width = maxWidth
height = width / imageAspectRatio height = width / imageAspectRatio
} }
let needsReload = height != logo.frame.height let needsReload = height != logo.frame.height
logo.frame = CGRect(x: (view.bounds.size.width - width) / 2, y: fullHeight - height, width: width, height: height) logo.frame = CGRect(x: (view.bounds.size.width - width) / 2, y: fullHeight - height, width: width, height: height)
if needsReload { if needsReload {
tableView.tableFooterView = logo tableView.tableFooterView = logo
} }

View File

@ -43,7 +43,7 @@ class TunnelDetailTableViewController: UITableViewController {
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
deinit { deinit {
onDemandStatusObservationToken = nil onDemandStatusObservationToken = nil
statusObservationToken = nil statusObservationToken = nil
@ -166,7 +166,7 @@ extension TunnelDetailTableViewController {
private func statusCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell { private func statusCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let cell: SwitchCell = tableView.dequeueReusableCell(for: indexPath) let cell: SwitchCell = tableView.dequeueReusableCell(for: indexPath)
let statusUpdate: (SwitchCell, TunnelStatus) -> Void = { cell, status in let statusUpdate: (SwitchCell, TunnelStatus) -> Void = { cell, status in
let text: String let text: String
switch status { switch status {
@ -192,13 +192,13 @@ extension TunnelDetailTableViewController {
} }
cell.isEnabled = status == .active || status == .inactive cell.isEnabled = status == .active || status == .inactive
} }
statusUpdate(cell, tunnel.status) statusUpdate(cell, tunnel.status)
statusObservationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in statusObservationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in
guard let cell = cell else { return } guard let cell = cell else { return }
statusUpdate(cell, tunnel.status) statusUpdate(cell, tunnel.status)
} }
cell.onSwitchToggled = { [weak self] isOn in cell.onSwitchToggled = { [weak self] isOn in
guard let self = self else { return } guard let self = self else { return }
if isOn { if isOn {

View File

@ -17,27 +17,27 @@ class TunnelsListTableViewController: UIViewController {
tableView.register(TunnelListCell.self) tableView.register(TunnelListCell.self)
return tableView return tableView
}() }()
let centeredAddButton: BorderedTextButton = { let centeredAddButton: BorderedTextButton = {
let button = BorderedTextButton() let button = BorderedTextButton()
button.title = tr("tunnelsListCenteredAddTunnelButtonTitle") button.title = tr("tunnelsListCenteredAddTunnelButtonTitle")
button.isHidden = true button.isHidden = true
return button return button
}() }()
let busyIndicator: UIActivityIndicatorView = { let busyIndicator: UIActivityIndicatorView = {
let busyIndicator = UIActivityIndicatorView(style: .gray) let busyIndicator = UIActivityIndicatorView(style: .gray)
busyIndicator.hidesWhenStopped = true busyIndicator.hidesWhenStopped = true
return busyIndicator return busyIndicator
}() }()
override func loadView() { override func loadView() {
view = UIView() view = UIView()
view.backgroundColor = .white view.backgroundColor = .white
tableView.dataSource = self tableView.dataSource = self
tableView.delegate = self tableView.delegate = self
view.addSubview(tableView) view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -53,22 +53,22 @@ class TunnelsListTableViewController: UIViewController {
busyIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor), busyIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
busyIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor) busyIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
]) ])
view.addSubview(centeredAddButton) view.addSubview(centeredAddButton)
centeredAddButton.translatesAutoresizingMaskIntoConstraints = false centeredAddButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
centeredAddButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), centeredAddButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
centeredAddButton.centerYAnchor.constraint(equalTo: view.centerYAnchor) centeredAddButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
]) ])
centeredAddButton.onTapped = { [weak self] in centeredAddButton.onTapped = { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.addButtonTapped(sender: self.centeredAddButton) self.addButtonTapped(sender: self.centeredAddButton)
} }
busyIndicator.startAnimating() busyIndicator.startAnimating()
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -82,7 +82,7 @@ class TunnelsListTableViewController: UIViewController {
func setTunnelsManager(tunnelsManager: TunnelsManager) { func setTunnelsManager(tunnelsManager: TunnelsManager) {
self.tunnelsManager = tunnelsManager self.tunnelsManager = tunnelsManager
tunnelsManager.tunnelsListDelegate = self tunnelsManager.tunnelsListDelegate = self
busyIndicator.stopAnimating() busyIndicator.stopAnimating()
tableView.reloadData() tableView.reloadData()
centeredAddButton.isHidden = tunnelsManager.numberOfTunnels() > 0 centeredAddButton.isHidden = tunnelsManager.numberOfTunnels() > 0
@ -96,7 +96,7 @@ class TunnelsListTableViewController: UIViewController {
@objc func addButtonTapped(sender: AnyObject) { @objc func addButtonTapped(sender: AnyObject) {
guard tunnelsManager != nil else { return } guard tunnelsManager != nil else { return }
let alert = UIAlertController(title: "", message: tr("addTunnelMenuHeader"), preferredStyle: .actionSheet) let alert = UIAlertController(title: "", message: tr("addTunnelMenuHeader"), preferredStyle: .actionSheet)
let importFileAction = UIAlertAction(title: tr("addTunnelMenuImportFile"), style: .default) { [weak self] _ in let importFileAction = UIAlertAction(title: tr("addTunnelMenuImportFile"), style: .default) { [weak self] _ in
self?.presentViewControllerForFileImport() self?.presentViewControllerForFileImport()
@ -129,7 +129,7 @@ class TunnelsListTableViewController: UIViewController {
@objc func settingsButtonTapped(sender: UIBarButtonItem) { @objc func settingsButtonTapped(sender: UIBarButtonItem) {
guard tunnelsManager != nil else { return } guard tunnelsManager != nil else { return }
let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager) let settingsVC = SettingsTableViewController(tunnelsManager: tunnelsManager)
let settingsNC = UINavigationController(rootViewController: settingsVC) let settingsNC = UINavigationController(rootViewController: settingsVC)
settingsNC.modalPresentationStyle = .formSheet settingsNC.modalPresentationStyle = .formSheet

View File

@ -3,6 +3,6 @@
protocol WireGuardAppError: Error { protocol WireGuardAppError: Error {
typealias AlertText = (title: String, message: String) typealias AlertText = (title: String, message: String)
var alertText: AlertText { get } var alertText: AlertText { get }
} }

View File

@ -62,7 +62,7 @@ class DNSResolver {
extension DNSResolver { extension DNSResolver {
// Based on DNS resolution code by Jason Donenfeld <jason@zx2c4.com> // Based on DNS resolution code by Jason Donenfeld <jason@zx2c4.com>
// in parse_endpoint() in src/tools/config.c in the WireGuard codebase // in parse_endpoint() in src/tools/config.c in the WireGuard codebase
//swiftlint:disable:next cyclomatic_complexity //swiftlint:disable:next cyclomatic_complexity
private static func resolveSync(endpoint: Endpoint) -> Endpoint? { private static func resolveSync(endpoint: Endpoint) -> Endpoint? {
switch endpoint.host { switch endpoint.host {

View File

@ -14,7 +14,7 @@ enum PacketTunnelProviderError: Error {
} }
class PacketTunnelProvider: NEPacketTunnelProvider { class PacketTunnelProvider: NEPacketTunnelProvider {
private var wgHandle: Int32? private var wgHandle: Int32?
private var networkMonitor: NWPathMonitor? private var networkMonitor: NWPathMonitor?
private var lastFirstInterface: NWInterface? private var lastFirstInterface: NWInterface?