Files
wireguard-apple/Sources/WireGuardApp/UI/iOS/ViewController/SettingsTableViewController.swift
T
Jason A. Donenfeld a4fc0f64b8 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-17 22:44:30 +01:00

174 lines
6.5 KiB
Swift

// SPDX-License-Identifier: MIT
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
import UIKit
import os.log
class SettingsTableViewController: UITableViewController {
enum SettingsFields {
case iosAppVersion
case goBackendVersion
case exportZipArchive
case viewLog
var localizedUIString: String {
switch self {
case .iosAppVersion: return tr("settingsVersionKeyWireGuardForIOS")
case .goBackendVersion: return tr("settingsVersionKeyWireGuardGoBackend")
case .exportZipArchive: return tr("settingsExportZipButtonTitle")
case .viewLog: return tr("settingsViewLogButtonTitle")
}
}
}
let settingsFieldsBySection: [[SettingsFields]] = [
[.iosAppVersion, .goBackendVersion],
[.exportZipArchive],
[.viewLog]
]
let tunnelsManager: TunnelsManager?
var wireguardCaptionedImage: (view: UIView, size: CGSize)?
init(tunnelsManager: TunnelsManager?) {
self.tunnelsManager = tunnelsManager
super.init(style: .grouped)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
title = tr("settingsViewTitle")
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped))
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableView.automaticDimension
tableView.allowsSelection = false
tableView.register(KeyValueCell.self)
tableView.register(ButtonCell.self)
tableView.tableFooterView = UIImageView(image: UIImage(named: "wireguard.pdf"))
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let logo = tableView.tableFooterView else { return }
let bottomPadding = max(tableView.layoutMargins.bottom, 10)
let fullHeight = max(tableView.contentSize.height, tableView.bounds.size.height - tableView.layoutMargins.top - bottomPadding)
let imageAspectRatio = logo.intrinsicContentSize.width / logo.intrinsicContentSize.height
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
}
let needsReload = height != logo.frame.height
logo.frame = CGRect(x: (view.bounds.size.width - width) / 2, y: fullHeight - height, width: width, height: height)
if needsReload {
tableView.tableFooterView = logo
}
}
@objc func doneTapped() {
dismiss(animated: true, completion: nil)
}
func exportConfigurationsAsZipFile(sourceView: UIView) {
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)
}
}
}
func presentLogView() {
let logVC = LogViewController()
navigationController?.pushViewController(logVC, animated: true)
}
}
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? {
switch section {
case 0:
return tr("settingsSectionTitleAbout")
case 1:
return tr("settingsSectionTitleExportConfigurations")
case 2:
return tr("settingsSectionTitleTunnelLog")
default:
return nil
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let field = settingsFieldsBySection[indexPath.section][indexPath.row]
if field == .iosAppVersion || field == .goBackendVersion {
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
cell.copyableGesture = false
cell.key = field.localizedUIString
if field == .iosAppVersion {
var appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown version"
if let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String {
appVersion += " (\(appBuild))"
}
cell.value = appVersion
} else if field == .goBackendVersion {
cell.value = WIREGUARD_GO_VERSION
}
return cell
} else if field == .exportZipArchive {
let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath)
cell.buttonText = field.localizedUIString
cell.onTapped = { [weak self] in
self?.exportConfigurationsAsZipFile(sourceView: cell.button)
}
return cell
} else if field == .viewLog {
let cell: ButtonCell = tableView.dequeueReusableCell(for: indexPath)
cell.buttonText = field.localizedUIString
cell.onTapped = { [weak self] in
self?.presentLogView()
}
return cell
}
fatalError()
}
}