2018-10-29 12:04:02 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2021-06-17 14:56:46 +00:00
|
|
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
2018-10-29 12:04:02 +00:00
|
|
|
|
|
|
|
import UIKit
|
2018-10-29 17:26:25 +00:00
|
|
|
import os.log
|
2018-10-29 12:04:02 +00:00
|
|
|
|
|
|
|
class SettingsTableViewController: UITableViewController {
|
|
|
|
|
2018-12-18 11:00:16 +00:00
|
|
|
enum SettingsFields {
|
|
|
|
case iosAppVersion
|
|
|
|
case goBackendVersion
|
|
|
|
case exportZipArchive
|
2019-03-28 13:58:27 +00:00
|
|
|
case viewLog
|
2018-12-18 11:00:16 +00:00
|
|
|
|
|
|
|
var localizedUIString: String {
|
|
|
|
switch self {
|
|
|
|
case .iosAppVersion: return tr("settingsVersionKeyWireGuardForIOS")
|
|
|
|
case .goBackendVersion: return tr("settingsVersionKeyWireGuardGoBackend")
|
|
|
|
case .exportZipArchive: return tr("settingsExportZipButtonTitle")
|
2019-03-28 13:58:27 +00:00
|
|
|
case .viewLog: return tr("settingsViewLogButtonTitle")
|
2018-12-18 11:00:16 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
|
|
|
|
2018-11-06 17:11:54 +00:00
|
|
|
let settingsFieldsBySection: [[SettingsFields]] = [
|
UI: iOS: remove donation link
Apple forbids us from having a simple link to wireguard.com/donations/
in the version info window, citing the existence of this link as a form
of payment outside of their in-app purchase framework that requires 30%.
The link had been there for around two years. After rejecting an app
update for a critical networking regression unrelated to this, they
wrote:
Dec 17, 2020 at 8:35 PM
From Apple
3.1.1 - Business - Payments - In-App Purchase
We noticed that your app allows users to contribute donations to the
development of your app with a mechanism other than the in-app
purchase API, which is not appropriate for the App Store.
Next Steps
To resolve this issue, please revise your app to use the in-app
purchase API to pay for this type of transaction. Please note that
even though tipping another individual is optional, the tip is
connected to or associated with the receipt of digital content or
services in your app and must be purchased through in-app purchase
in accordance with guideline 3.1.1 of the App Store Review
Guidelines.
Please see attached screenshot for details.
Trying to appeal this or reason with Apple is not going to be a fruitful
endeavor, so instead we simply cut our losses and remove the donation
link entirely. The goal, anyway, is to get a timely critical update into
the hands of users, and encouraging Apple to block that further would be
a disservice.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-12-15 23:03:34 +00:00
|
|
|
[.iosAppVersion, .goBackendVersion],
|
2018-11-29 19:25:42 +00:00
|
|
|
[.exportZipArchive],
|
2019-03-28 13:58:27 +00:00
|
|
|
[.viewLog]
|
2018-10-29 12:04:02 +00:00
|
|
|
]
|
|
|
|
|
2018-10-29 17:26:25 +00:00
|
|
|
let tunnelsManager: TunnelsManager?
|
2018-11-03 18:35:25 +00:00
|
|
|
var wireguardCaptionedImage: (view: UIView, size: CGSize)?
|
2018-10-29 17:26:25 +00:00
|
|
|
|
|
|
|
init(tunnelsManager: TunnelsManager?) {
|
|
|
|
self.tunnelsManager = tunnelsManager
|
2018-10-29 12:04:02 +00:00
|
|
|
super.init(style: .grouped)
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
2018-12-18 11:00:16 +00:00
|
|
|
title = tr("settingsViewTitle")
|
2018-12-13 15:30:13 +00:00
|
|
|
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped))
|
|
|
|
|
|
|
|
tableView.estimatedRowHeight = 44
|
|
|
|
tableView.rowHeight = UITableView.automaticDimension
|
|
|
|
tableView.allowsSelection = false
|
|
|
|
|
2018-12-14 23:12:59 +00:00
|
|
|
tableView.register(KeyValueCell.self)
|
|
|
|
tableView.register(ButtonCell.self)
|
2018-12-13 15:30:13 +00:00
|
|
|
|
|
|
|
tableView.tableFooterView = UIImageView(image: UIImage(named: "wireguard.pdf"))
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
2018-11-05 01:09:40 +00:00
|
|
|
|
2018-11-04 02:37:09 +00:00
|
|
|
override func viewDidLayoutSubviews() {
|
|
|
|
super.viewDidLayoutSubviews()
|
2018-12-14 23:12:59 +00:00
|
|
|
guard let logo = tableView.tableFooterView else { return }
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
let bottomPadding = max(tableView.layoutMargins.bottom, 10)
|
|
|
|
let fullHeight = max(tableView.contentSize.height, tableView.bounds.size.height - tableView.layoutMargins.top - bottomPadding)
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
let imageAspectRatio = logo.intrinsicContentSize.width / logo.intrinsicContentSize.height
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
var height = tableView.estimatedRowHeight * 1.5
|
|
|
|
var width = height * imageAspectRatio
|
|
|
|
let maxWidth = view.bounds.size.width - max(tableView.layoutMargins.left + tableView.layoutMargins.right, 20)
|
|
|
|
if width > maxWidth {
|
|
|
|
width = maxWidth
|
|
|
|
height = width / imageAspectRatio
|
|
|
|
}
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
let needsReload = height != logo.frame.height
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
logo.frame = CGRect(x: (view.bounds.size.width - width) / 2, y: fullHeight - height, width: width, height: height)
|
2018-12-21 22:34:56 +00:00
|
|
|
|
2018-12-13 15:30:13 +00:00
|
|
|
if needsReload {
|
|
|
|
tableView.tableFooterView = logo
|
|
|
|
}
|
2018-11-04 02:37:09 +00:00
|
|
|
}
|
2018-10-29 12:04:02 +00:00
|
|
|
|
|
|
|
@objc func doneTapped() {
|
|
|
|
dismiss(animated: true, completion: nil)
|
|
|
|
}
|
2018-10-29 17:26:25 +00:00
|
|
|
|
|
|
|
func exportConfigurationsAsZipFile(sourceView: UIView) {
|
2019-02-06 02:23:51 +00:00
|
|
|
PrivateDataConfirmation.confirmAccess(to: tr("iosExportPrivateData")) { [weak self] in
|
|
|
|
guard let self = self else { return }
|
|
|
|
guard let tunnelsManager = self.tunnelsManager else { return }
|
|
|
|
guard let destinationDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
|
|
|
|
|
|
|
|
let destinationURL = destinationDir.appendingPathComponent("wireguard-export.zip")
|
|
|
|
_ = FileManager.deleteFile(at: destinationURL)
|
|
|
|
|
|
|
|
let count = tunnelsManager.numberOfTunnels()
|
|
|
|
let tunnelConfigurations = (0 ..< count).compactMap { tunnelsManager.tunnel(at: $0).tunnelConfiguration }
|
|
|
|
ZipExporter.exportConfigFiles(tunnelConfigurations: tunnelConfigurations, to: destinationURL) { [weak self] error in
|
|
|
|
if let error = error {
|
|
|
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let fileExportVC = UIDocumentPickerViewController(url: destinationURL, in: .exportToService)
|
|
|
|
self?.present(fileExportVC, animated: true, completion: nil)
|
2018-11-15 08:07:59 +00:00
|
|
|
}
|
2018-10-29 17:26:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 13:58:27 +00:00
|
|
|
func presentLogView() {
|
|
|
|
let logVC = LogViewController()
|
|
|
|
navigationController?.pushViewController(logVC, animated: true)
|
2018-11-29 19:25:42 +00:00
|
|
|
|
|
|
|
}
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension SettingsTableViewController {
|
|
|
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
|
|
return settingsFieldsBySection.count
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
|
return settingsFieldsBySection[section].count
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
2018-12-12 18:28:27 +00:00
|
|
|
switch section {
|
2018-10-29 12:04:02 +00:00
|
|
|
case 0:
|
2018-12-18 11:00:16 +00:00
|
|
|
return tr("settingsSectionTitleAbout")
|
2018-11-03 18:53:04 +00:00
|
|
|
case 1:
|
2018-12-18 11:00:16 +00:00
|
|
|
return tr("settingsSectionTitleExportConfigurations")
|
2018-11-29 19:25:42 +00:00
|
|
|
case 2:
|
2018-12-18 11:00:16 +00:00
|
|
|
return tr("settingsSectionTitleTunnelLog")
|
2018-10-29 12:04:02 +00:00
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
|
let field = settingsFieldsBySection[indexPath.section][indexPath.row]
|
2018-12-12 18:28:27 +00:00
|
|
|
if field == .iosAppVersion || field == .goBackendVersion {
|
2018-12-14 23:12:59 +00:00
|
|
|
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
|
|
|
cell.copyableGesture = false
|
2018-12-18 11:00:16 +00:00
|
|
|
cell.key = field.localizedUIString
|
2018-12-12 18:28:27 +00:00
|
|
|
if field == .iosAppVersion {
|
2020-12-14 15:25:12 +00:00
|
|
|
var appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown version"
|
|
|
|
if let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String {
|
2018-11-07 04:45:39 +00:00
|
|
|
appVersion += " (\(appBuild))"
|
|
|
|
}
|
2018-10-29 12:04:02 +00:00
|
|
|
cell.value = appVersion
|
2018-12-12 18:28:27 +00:00
|
|
|
} else if field == .goBackendVersion {
|
2020-12-02 17:03:16 +00:00
|
|
|
cell.value = WIREGUARD_GO_VERSION
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
|
|
|
return cell
|
2018-12-12 18:28:27 +00:00
|
|
|
} else if field == .exportZipArchive {
|
2018-12-14 23:12:59 +00:00
|
|
|
let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath)
|
2018-12-18 11:00:16 +00:00
|
|
|
cell.buttonText = field.localizedUIString
|
2018-10-29 17:26:25 +00:00
|
|
|
cell.onTapped = { [weak self] in
|
|
|
|
self?.exportConfigurationsAsZipFile(sourceView: cell.button)
|
|
|
|
}
|
2018-10-29 12:04:02 +00:00
|
|
|
return cell
|
2019-10-11 19:31:20 +00:00
|
|
|
} else if field == .viewLog {
|
2018-12-14 23:12:59 +00:00
|
|
|
let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath)
|
2018-12-18 11:00:16 +00:00
|
|
|
cell.buttonText = field.localizedUIString
|
2018-11-29 19:25:42 +00:00
|
|
|
cell.onTapped = { [weak self] in
|
2019-03-28 13:58:27 +00:00
|
|
|
self?.presentLogView()
|
2018-11-29 19:25:42 +00:00
|
|
|
}
|
|
|
|
return cell
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
2019-10-12 20:20:30 +00:00
|
|
|
fatalError()
|
2018-10-29 12:04:02 +00:00
|
|
|
}
|
|
|
|
}
|