Replace with Convenience entities
- About - Alerts - Dialogs - InApp - Reviewer - SingleOptionViewController - StrongTableModel
This commit is contained in:
parent
ea5d3a48ab
commit
2cd6677e16
|
@ -26,6 +26,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
||||||
|
@ -54,7 +55,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
// splitViewController.preferredDisplayMode = .primaryOverlay
|
// splitViewController.preferredDisplayMode = .primaryOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
InAppHelper.shared.requestProducts(withIdentifiers: InApp.allIdentifiers(), completionHandler: nil)
|
ProductManager.shared.listProducts(completionHandler: nil)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,11 +44,11 @@ extension OpenVPN.ConfigurationParser.Result {
|
||||||
} catch let e as ConfigurationError {
|
} catch let e as ConfigurationError {
|
||||||
switch e {
|
switch e {
|
||||||
case .encryptionPassphrase, .unableToDecrypt(_):
|
case .encryptionPassphrase, .unableToDecrypt(_):
|
||||||
let alert = Macros.alert(url.normalizedFilename, L10n.Core.ParsedFile.Alerts.EncryptionPassphrase.message)
|
let alert = UIAlertController.asAlert(url.normalizedFilename, L10n.Core.ParsedFile.Alerts.EncryptionPassphrase.message)
|
||||||
alert.addTextField { (field) in
|
alert.addTextField { (field) in
|
||||||
field.isSecureTextEntry = true
|
field.isSecureTextEntry = true
|
||||||
}
|
}
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
guard let passphrase = alert.textFields?.first?.text else {
|
guard let passphrase = alert.textFields?.first?.text else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,8 @@ extension OpenVPN.ConfigurationParser.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func alertImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
private static func alertImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
||||||
let alert = Macros.alert(url.normalizedFilename, message)
|
let alert = UIAlertController.asAlert(url.normalizedFilename, message)
|
||||||
// alert.addDefaultAction(L10n.Core.ParsedFile.Alerts.Buttons.report) {
|
// alert.addPreferredAction(L10n.Core.ParsedFile.Alerts.Buttons.report) {
|
||||||
// var attach = IssueReporter.Attachments(debugLog: false, configurationURL: url)
|
// var attach = IssueReporter.Attachments(debugLog: false, configurationURL: url)
|
||||||
// attach.description = message
|
// attach.description = message
|
||||||
// IssueReporter.shared.present(in: vc, withAttachments: attach)
|
// IssueReporter.shared.present(in: vc, withAttachments: attach)
|
||||||
|
@ -91,8 +91,8 @@ extension OpenVPN.ConfigurationParser.Result {
|
||||||
|
|
||||||
static func alertImportWarning(url: URL, in vc: UIViewController, withWarning warning: ConfigurationError, completionHandler: @escaping (Bool) -> Void) {
|
static func alertImportWarning(url: URL, in vc: UIViewController, withWarning warning: ConfigurationError, completionHandler: @escaping (Bool) -> Void) {
|
||||||
let message = details(forWarning: warning)
|
let message = details(forWarning: warning)
|
||||||
let alert = Macros.alert(url.normalizedFilename, L10n.Core.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
let alert = UIAlertController.asAlert(url.normalizedFilename, L10n.Core.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
completionHandler(true)
|
completionHandler(true)
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel) {
|
alert.addCancelAction(L10n.Core.Global.cancel) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// Donation.swift
|
||||||
|
// Passepartout-iOS
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/11/19.
|
||||||
|
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum Donation: String {
|
||||||
|
case tiny = "com.algoritmico.ios.Passepartout.donations.Tiny"
|
||||||
|
|
||||||
|
case small = "com.algoritmico.ios.Passepartout.donations.Small"
|
||||||
|
|
||||||
|
case medium = "com.algoritmico.ios.Passepartout.donations.Medium"
|
||||||
|
|
||||||
|
case big = "com.algoritmico.ios.Passepartout.donations.Big"
|
||||||
|
|
||||||
|
case huge = "com.algoritmico.ios.Passepartout.donations.Huge"
|
||||||
|
|
||||||
|
case maxi = "com.algoritmico.ios.Passepartout.donations.Maxi"
|
||||||
|
|
||||||
|
static let all: [Donation] = [
|
||||||
|
.tiny,
|
||||||
|
.small,
|
||||||
|
.medium,
|
||||||
|
.big,
|
||||||
|
.huge,
|
||||||
|
.maxi
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,101 +0,0 @@
|
||||||
//
|
|
||||||
// Downloader.swift
|
|
||||||
// Passepartout-iOS
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 4/10/19.
|
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import MBProgressHUD
|
|
||||||
import SwiftyBeaver
|
|
||||||
import PassepartoutCore
|
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
|
||||||
|
|
||||||
class Downloader: NSObject {
|
|
||||||
static let shared = Downloader(temporaryURL: GroupConstants.App.cachesURL.appendingPathComponent("downloaded.tmp"))
|
|
||||||
|
|
||||||
private let temporaryURL: URL
|
|
||||||
|
|
||||||
private var hud: MBProgressHUD?
|
|
||||||
|
|
||||||
private var completionHandler: ((URL?, Error?) -> Void)?
|
|
||||||
|
|
||||||
init(temporaryURL: URL) {
|
|
||||||
self.temporaryURL = temporaryURL
|
|
||||||
}
|
|
||||||
|
|
||||||
func download(url: URL, in view: UIView, completionHandler: @escaping (URL?, Error?) -> Void) -> Bool {
|
|
||||||
guard hud == nil else {
|
|
||||||
log.info("Download in progress, skipping")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Downloading from: \(url)")
|
|
||||||
let session = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
|
|
||||||
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: AppConstants.Web.timeout)
|
|
||||||
let task = session.downloadTask(with: request)
|
|
||||||
|
|
||||||
hud = MBProgressHUD.showAdded(to: view, animated: true)
|
|
||||||
hud?.mode = .annularDeterminate
|
|
||||||
hud?.progressObject = task.progress
|
|
||||||
|
|
||||||
self.completionHandler = completionHandler
|
|
||||||
task.resume()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Downloader: URLSessionDelegate, URLSessionDownloadDelegate {
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
||||||
if let error = error {
|
|
||||||
log.error("Download failed: \(error)")
|
|
||||||
hud?.hide(animated: true)
|
|
||||||
hud = nil
|
|
||||||
completionHandler?(nil, error)
|
|
||||||
completionHandler = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
completionHandler = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
|
||||||
log.info("Download complete!")
|
|
||||||
if let url = downloadTask.originalRequest?.url {
|
|
||||||
log.info("\tFrom: \(url)")
|
|
||||||
}
|
|
||||||
log.debug("\tTo: \(location)")
|
|
||||||
|
|
||||||
let fm = FileManager.default
|
|
||||||
do {
|
|
||||||
try? fm.removeItem(at: temporaryURL)
|
|
||||||
try fm.copyItem(at: location, to: temporaryURL)
|
|
||||||
} catch let e {
|
|
||||||
log.error("Failed to copy downloaded file: \(e)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hud?.hide(animated: true)
|
|
||||||
hud = nil
|
|
||||||
completionHandler?(temporaryURL, nil)
|
|
||||||
completionHandler = nil
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
//
|
|
||||||
// HUD.swift
|
|
||||||
// Passepartout-iOS
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 9/18/18.
|
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
// This file incorporates work covered by the following copyright and
|
|
||||||
// permission notice:
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018-Present Private Internet Access
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import MBProgressHUD
|
|
||||||
|
|
||||||
class HUD {
|
|
||||||
private let backend: MBProgressHUD
|
|
||||||
|
|
||||||
init(label: String? = nil) {
|
|
||||||
guard let window = UIApplication.shared.windows.first else {
|
|
||||||
fatalError("Could not locate front window?")
|
|
||||||
}
|
|
||||||
|
|
||||||
backend = MBProgressHUD.showAdded(to: window, animated: true)
|
|
||||||
backend.label.text = label
|
|
||||||
backend.backgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
|
||||||
backend.mode = .indeterminate
|
|
||||||
backend.removeFromSuperViewOnHide = true
|
|
||||||
|
|
||||||
// Theme.current.applyOverlay(hud.backgroundView)
|
|
||||||
// Theme.current.applyOverlay(hud.bezelView)
|
|
||||||
}
|
|
||||||
|
|
||||||
func show() {
|
|
||||||
backend.show(animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hide() {
|
|
||||||
backend.hide(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,8 +42,8 @@ class IssueReporter: NSObject {
|
||||||
let app = UIApplication.shared
|
let app = UIApplication.shared
|
||||||
let V = AppConstants.IssueReporter.Email.self
|
let V = AppConstants.IssueReporter.Email.self
|
||||||
let body = V.body(V.template, DebugLog(raw: "--").decoratedString())
|
let body = V.body(V.template, DebugLog(raw: "--").decoratedString())
|
||||||
guard let url = Utils.mailto(to: V.recipient, subject: V.subject, body: body), app.canOpenURL(url) else {
|
guard let url = URL.mailto(to: V.recipient, subject: V.subject, body: body), app.canOpenURL(url) else {
|
||||||
let alert = Macros.alert(L10n.Core.IssueReporter.title, L10n.Core.Global.emailNotConfigured)
|
let alert = UIAlertController.asAlert(L10n.Core.IssueReporter.title, L10n.Core.Global.emailNotConfigured)
|
||||||
alert.addCancelAction(L10n.Core.Global.ok)
|
alert.addCancelAction(L10n.Core.Global.ok)
|
||||||
viewController.present(alert, animated: true, completion: nil)
|
viewController.present(alert, animated: true, completion: nil)
|
||||||
return
|
return
|
||||||
|
@ -55,8 +55,8 @@ class IssueReporter: NSObject {
|
||||||
self.viewController = viewController
|
self.viewController = viewController
|
||||||
|
|
||||||
if issue.debugLog {
|
if issue.debugLog {
|
||||||
let alert = Macros.alert(L10n.Core.IssueReporter.title, L10n.Core.IssueReporter.message)
|
let alert = UIAlertController.asAlert(L10n.Core.IssueReporter.title, L10n.Core.IssueReporter.message)
|
||||||
alert.addDefaultAction(L10n.Core.IssueReporter.Buttons.accept) {
|
alert.addPreferredAction(L10n.Core.IssueReporter.Buttons.accept) {
|
||||||
VPN.shared.requestDebugLog(fallback: AppConstants.Log.debugSnapshot) {
|
VPN.shared.requestDebugLog(fallback: AppConstants.Log.debugSnapshot) {
|
||||||
self.composeEmail(withDebugLog: $0, configurationURL: issue.configurationURL, description: issue.description)
|
self.composeEmail(withDebugLog: $0, configurationURL: issue.configurationURL, description: issue.description)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,55 +25,6 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class Macros {
|
|
||||||
static func alert(_ title: String?, _ message: String?) -> UIAlertController {
|
|
||||||
return UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func actionSheet(_ title: String?, _ message: String?) -> UIAlertController {
|
|
||||||
return UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIAlertController {
|
|
||||||
@discardableResult func addDefaultAction(_ title: String, handler: @escaping () -> Void) -> UIAlertAction {
|
|
||||||
let action = UIAlertAction(title: title, style: .default) { (action) in
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
addAction(action)
|
|
||||||
preferredAction = action
|
|
||||||
return action
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func addCancelAction(_ title: String, handler: (() -> Void)? = nil) -> UIAlertAction {
|
|
||||||
let action = UIAlertAction(title: title, style: .cancel) { (action) in
|
|
||||||
handler?()
|
|
||||||
}
|
|
||||||
addAction(action)
|
|
||||||
if actions.count == 1 {
|
|
||||||
preferredAction = action
|
|
||||||
}
|
|
||||||
return action
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func addAction(_ title: String, handler: @escaping () -> Void) -> UIAlertAction {
|
|
||||||
let action = UIAlertAction(title: title, style: .default) { (action) in
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
addAction(action)
|
|
||||||
return action
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func addDestructiveAction(_ title: String, handler: @escaping () -> Void) -> UIAlertAction {
|
|
||||||
let action = UIAlertAction(title: title, style: .destructive) { (action) in
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
addAction(action)
|
|
||||||
preferredAction = action
|
|
||||||
return action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIView {
|
extension UIView {
|
||||||
static func get<T: UIView>() -> T {
|
static func get<T: UIView>() -> T {
|
||||||
let name = String(describing: T.self)
|
let name = String(describing: T.self)
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
//
|
|
||||||
// OptionViewController.swift
|
|
||||||
// Passepartout-iOS
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 9/5/18.
|
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class OptionViewController<T: Hashable>: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
|
||||||
private lazy var tableView = UITableView(frame: .zero, style: .grouped)
|
|
||||||
|
|
||||||
var options: [T] = []
|
|
||||||
|
|
||||||
var selectedOption: T?
|
|
||||||
|
|
||||||
var descriptionBlock: ((T) -> String)?
|
|
||||||
|
|
||||||
var selectionBlock: ((T) -> Void)?
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
tableView.register(SettingTableViewCell.self, forCellReuseIdentifier: SettingTableViewCell.Provider.identifier)
|
|
||||||
tableView.frame = view.bounds
|
|
||||||
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
view.addSubview(tableView)
|
|
||||||
|
|
||||||
tableView.dataSource = self
|
|
||||||
tableView.delegate = self
|
|
||||||
|
|
||||||
if let selectedOption = selectedOption, let row = options.firstIndex(of: selectedOption) {
|
|
||||||
tableView.reloadData()
|
|
||||||
tableView.scrollToRowAsync(at: IndexPath(row: row, section: 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
return options.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
let opt = options[indexPath.row]
|
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
|
||||||
cell.leftText = descriptionBlock?(opt)
|
|
||||||
cell.applyChecked(opt == selectedOption, Theme.current)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
||||||
let opt = options[indexPath.row]
|
|
||||||
selectionBlock?(opt)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// InApp.swift
|
// ProductManager.swift
|
||||||
// Passepartout-iOS
|
// Passepartout-iOS
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 4/6/19.
|
// Created by Davide De Rosa on 4/6/19.
|
||||||
|
@ -25,32 +25,28 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import StoreKit
|
import StoreKit
|
||||||
|
import Convenience
|
||||||
|
|
||||||
struct InApp {
|
struct ProductManager {
|
||||||
enum Donation: String {
|
static let shared = ProductManager()
|
||||||
static let all: [Donation] = [
|
|
||||||
.tiny,
|
private let inApp: InApp<Donation>
|
||||||
.small,
|
|
||||||
.medium,
|
private init() {
|
||||||
.big,
|
inApp = InApp()
|
||||||
.huge,
|
}
|
||||||
.maxi
|
|
||||||
]
|
func listProducts(completionHandler: (([SKProduct]) -> Void)?) {
|
||||||
|
guard inApp.products.isEmpty else {
|
||||||
case tiny = "com.algoritmico.ios.Passepartout.donations.Tiny"
|
completionHandler?(inApp.products)
|
||||||
|
return
|
||||||
case small = "com.algoritmico.ios.Passepartout.donations.Small"
|
}
|
||||||
|
inApp.requestProducts(withIdentifiers: Donation.all) { _ in
|
||||||
case medium = "com.algoritmico.ios.Passepartout.donations.Medium"
|
completionHandler?(self.inApp.products)
|
||||||
|
}
|
||||||
case big = "com.algoritmico.ios.Passepartout.donations.Big"
|
|
||||||
|
|
||||||
case huge = "com.algoritmico.ios.Passepartout.donations.Huge"
|
|
||||||
|
|
||||||
case maxi = "com.algoritmico.ios.Passepartout.donations.Maxi"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func allIdentifiers() -> Set<String> {
|
func purchase(_ product: SKProduct, completionHandler: @escaping (InAppPurchaseResult, Error?) -> Void) {
|
||||||
return Set<String>(Donation.all.map { $0.rawValue })
|
inApp.purchase(product: product, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Convenience
|
||||||
|
|
||||||
extension UIViewController {
|
extension UIViewController {
|
||||||
func applyMasterTitle(_ theme: Theme) {
|
func applyMasterTitle(_ theme: Theme) {
|
||||||
|
@ -35,9 +36,9 @@ extension UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TableModel {
|
extension StrongTableModel {
|
||||||
func headerHeight(for section: Int) -> CGFloat {
|
func headerHeight(for section: Int) -> CGFloat {
|
||||||
guard let title = header(for: section) else {
|
guard let title = header(forSection: section) else {
|
||||||
return 1.0
|
return 1.0
|
||||||
}
|
}
|
||||||
guard !title.isEmpty else {
|
guard !title.isEmpty else {
|
||||||
|
@ -47,7 +48,7 @@ extension TableModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func footerHeight(for section: Int) -> CGFloat {
|
func footerHeight(for section: Int) -> CGFloat {
|
||||||
guard let title = footer(for: section) else {
|
guard let title = footer(forSection: section) else {
|
||||||
return 1.0
|
return 1.0
|
||||||
}
|
}
|
||||||
guard !title.isEmpty else {
|
guard !title.isEmpty else {
|
||||||
|
|
|
@ -25,25 +25,26 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
class AboutViewController: UITableViewController, TableModelHost {
|
class AboutViewController: UITableViewController, StrongTableHost {
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = {
|
let model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.add(.info)
|
model.add(.info)
|
||||||
model.add(.github)
|
model.add(.github)
|
||||||
model.add(.web)
|
model.add(.web)
|
||||||
model.add(.share)
|
model.add(.share)
|
||||||
model.setHeader("", for: .info)
|
model.setHeader("", forSection: .info)
|
||||||
model.setHeader("GitHub", for: .github)
|
model.setHeader("GitHub", forSection: .github)
|
||||||
model.setHeader(L10n.Core.About.Sections.Web.header, for: .web)
|
model.setHeader(L10n.Core.About.Sections.Web.header, forSection: .web)
|
||||||
model.setHeader(L10n.Core.About.Sections.Share.header, for: .share)
|
model.setHeader(L10n.Core.About.Sections.Share.header, forSection: .share)
|
||||||
model.set([.version, .credits], in: .info)
|
model.set([.version, .credits], forSection: .info)
|
||||||
model.set([.readme, .changelog], in: .github)
|
model.set([.readme, .changelog], forSection: .github)
|
||||||
model.set([.website, .faq, .disclaimer, .privacyPolicy], in: .web)
|
model.set([.website, .faq, .disclaimer, .privacyPolicy], forSection: .web)
|
||||||
model.set([.shareTwitter, .shareGeneric], in: .share)
|
model.set([.shareTwitter, .shareGeneric], forSection: .share)
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -126,15 +127,15 @@ extension AboutViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
@ -146,7 +147,7 @@ extension AboutViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -154,7 +155,7 @@ extension AboutViewController {
|
||||||
switch model.row(at: indexPath) {
|
switch model.row(at: indexPath) {
|
||||||
case .version:
|
case .version:
|
||||||
cell.leftText = L10n.Core.Version.title
|
cell.leftText = L10n.Core.Version.title
|
||||||
cell.rightText = Utils.versionString()
|
cell.rightText = ApplicationInfo.appVersion
|
||||||
|
|
||||||
case .credits:
|
case .credits:
|
||||||
cell.leftText = L10n.Core.About.Cells.Credits.caption
|
cell.leftText = L10n.Core.About.Cells.Credits.caption
|
||||||
|
|
|
@ -25,8 +25,9 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
class CreditsViewController: UITableViewController, TableModelHost {
|
class CreditsViewController: UITableViewController, StrongTableHost {
|
||||||
private let licenses = AppConstants.License.all
|
private let licenses = AppConstants.License.all
|
||||||
|
|
||||||
private let notices = AppConstants.Notice.all
|
private let notices = AppConstants.Notice.all
|
||||||
|
@ -35,22 +36,22 @@ class CreditsViewController: UITableViewController, TableModelHost {
|
||||||
return Utils.localizedLanguage($0) < Utils.localizedLanguage($1)
|
return Utils.localizedLanguage($0) < Utils.localizedLanguage($1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
var model: TableModel<SectionType, RowType> = TableModel()
|
var model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
model.add(.licenses)
|
model.add(.licenses)
|
||||||
model.add(.notices)
|
model.add(.notices)
|
||||||
model.add(.translations)
|
model.add(.translations)
|
||||||
|
|
||||||
model.setHeader(L10n.Core.Credits.Sections.Licenses.header, for: .licenses)
|
model.setHeader(L10n.Core.Credits.Sections.Licenses.header, forSection: .licenses)
|
||||||
model.setHeader(L10n.Core.Credits.Sections.Notices.header, for: .notices)
|
model.setHeader(L10n.Core.Credits.Sections.Notices.header, forSection: .notices)
|
||||||
model.setHeader(L10n.Core.Credits.Sections.Translations.header, for: .translations)
|
model.setHeader(L10n.Core.Credits.Sections.Translations.header, forSection: .translations)
|
||||||
|
|
||||||
model.set(.license, count: licenses.count, in: .licenses)
|
model.set(.license, count: licenses.count, forSection: .licenses)
|
||||||
model.set(.notice, count: notices.count, in: .notices)
|
model.set(.notice, count: notices.count, forSection: .notices)
|
||||||
model.set(.translation, count: languages.count, in: .translations)
|
model.set(.translation, count: languages.count, forSection: .translations)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
@ -108,15 +109,15 @@ extension CreditsViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
class VersionViewController: UIViewController {
|
class VersionViewController: UIViewController {
|
||||||
@IBOutlet private weak var scrollView: UIScrollView?
|
@IBOutlet private weak var scrollView: UIScrollView?
|
||||||
|
@ -46,7 +47,7 @@ class VersionViewController: UIViewController {
|
||||||
|
|
||||||
title = L10n.Core.Version.title
|
title = L10n.Core.Version.title
|
||||||
labelTitle?.text = GroupConstants.App.name
|
labelTitle?.text = GroupConstants.App.name
|
||||||
labelVersion?.text = Utils.versionString()
|
labelVersion?.text = ApplicationInfo.appVersion
|
||||||
labelIntro?.text = L10n.Core.Version.Labels.intro
|
labelIntro?.text = L10n.Core.Version.Labels.intro
|
||||||
|
|
||||||
scrollView?.applyPrimaryBackground(Theme.current)
|
scrollView?.applyPrimaryBackground(Theme.current)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
protocol AccountViewControllerDelegate: class {
|
protocol AccountViewControllerDelegate: class {
|
||||||
func accountController(_: AccountViewController, didEnterCredentials credentials: Credentials)
|
func accountController(_: AccountViewController, didEnterCredentials credentials: Credentials)
|
||||||
|
@ -32,7 +33,7 @@ protocol AccountViewControllerDelegate: class {
|
||||||
func accountControllerDidComplete(_: AccountViewController)
|
func accountControllerDidComplete(_: AccountViewController)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountViewController: UIViewController, TableModelHost {
|
class AccountViewController: UIViewController, StrongTableHost {
|
||||||
@IBOutlet private weak var tableView: UITableView?
|
@IBOutlet private weak var tableView: UITableView?
|
||||||
|
|
||||||
private weak var cellUsername: FieldTableViewCell?
|
private weak var cellUsername: FieldTableViewCell?
|
||||||
|
@ -101,32 +102,32 @@ class AccountViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
weak var delegate: AccountViewControllerDelegate?
|
weak var delegate: AccountViewControllerDelegate?
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
var model: TableModel<SectionType, RowType> = TableModel()
|
var model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
model.clear()
|
model.clear()
|
||||||
|
|
||||||
model.add(.credentials)
|
model.add(.credentials)
|
||||||
model.setHeader(L10n.App.Account.Sections.Credentials.header, for: .credentials)
|
model.setHeader(L10n.App.Account.Sections.Credentials.header, forSection: .credentials)
|
||||||
model.set([.username, .password], in: .credentials)
|
model.set([.username, .password], forSection: .credentials)
|
||||||
|
|
||||||
if let _ = infrastructureName {
|
if let _ = infrastructureName {
|
||||||
if let guidanceString = guidanceString {
|
if let guidanceString = guidanceString {
|
||||||
if let _ = guidanceURL {
|
if let _ = guidanceURL {
|
||||||
model.add(.guidance)
|
model.add(.guidance)
|
||||||
model.setFooter(guidanceString, for: .guidance)
|
model.setFooter(guidanceString, forSection: .guidance)
|
||||||
model.set([.openGuide], in: .guidance)
|
model.set([.openGuide], forSection: .guidance)
|
||||||
} else {
|
} else {
|
||||||
model.setFooter(guidanceString, for: .credentials)
|
model.setFooter(guidanceString, forSection: .credentials)
|
||||||
}
|
}
|
||||||
model.setHeader("", for: .registration)
|
model.setHeader("", forSection: .registration)
|
||||||
}
|
}
|
||||||
// if let _ = referralURL {
|
// if let _ = referralURL {
|
||||||
// model.add(.registration)
|
// model.add(.registration)
|
||||||
// model.setFooter(L10n.Core.Account.Sections.Registration.footer(name.rawValue), for: .registration)
|
// model.setFooter(L10n.Core.Account.Sections.Registration.footer(name.rawValue), forSection: .registration)
|
||||||
// model.set([.signUp], in: .registration)
|
// model.set([.signUp], forSection: .registration)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,15 +211,15 @@ extension AccountViewController: UITableViewDataSource, UITableViewDelegate, Fie
|
||||||
private static let footerButtonTag = 1000
|
private static let footerButtonTag = 1000
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
@ -226,7 +227,7 @@ extension AccountViewController: UITableViewDataSource, UITableViewDelegate, Fie
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
|
@ -27,10 +27,11 @@ import UIKit
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
class ConfigurationViewController: UIViewController, TableModelHost {
|
class ConfigurationViewController: UIViewController, StrongTableHost {
|
||||||
@IBOutlet private weak var tableView: UITableView!
|
@IBOutlet private weak var tableView: UITableView!
|
||||||
|
|
||||||
private lazy var itemRefresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
|
private lazy var itemRefresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
|
||||||
|
@ -47,10 +48,10 @@ class ConfigurationViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
weak var delegate: ConfigurationModificationDelegate?
|
weak var delegate: ConfigurationModificationDelegate?
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
lazy var model: TableModel<SectionType, RowType> = {
|
lazy var model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
// sections
|
// sections
|
||||||
model.add(.communication)
|
model.add(.communication)
|
||||||
|
@ -62,24 +63,24 @@ class ConfigurationViewController: UIViewController, TableModelHost {
|
||||||
model.add(.other)
|
model.add(.other)
|
||||||
|
|
||||||
// headers
|
// headers
|
||||||
model.setHeader(L10n.Core.Configuration.Sections.Communication.header, for: .communication)
|
model.setHeader(L10n.Core.Configuration.Sections.Communication.header, forSection: .communication)
|
||||||
model.setHeader(L10n.Core.Configuration.Sections.Tls.header, for: .tls)
|
model.setHeader(L10n.Core.Configuration.Sections.Tls.header, forSection: .tls)
|
||||||
model.setHeader(L10n.Core.Configuration.Sections.Compression.header, for: .compression)
|
model.setHeader(L10n.Core.Configuration.Sections.Compression.header, forSection: .compression)
|
||||||
model.setHeader(L10n.Core.Configuration.Sections.Other.header, for: .other)
|
model.setHeader(L10n.Core.Configuration.Sections.Other.header, forSection: .other)
|
||||||
|
|
||||||
// footers
|
// footers
|
||||||
if isEditable {
|
if isEditable {
|
||||||
model.setFooter(L10n.Core.Configuration.Sections.Reset.footer, for: .reset)
|
model.setFooter(L10n.Core.Configuration.Sections.Reset.footer, forSection: .reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
model.set([.cipher, .digest], in: .communication)
|
model.set([.cipher, .digest], forSection: .communication)
|
||||||
if isEditable {
|
if isEditable {
|
||||||
model.set([.resetOriginal], in: .reset)
|
model.set([.resetOriginal], forSection: .reset)
|
||||||
}
|
}
|
||||||
model.set([.client, .tlsWrapping, .eku], in: .tls)
|
model.set([.client, .tlsWrapping, .eku], forSection: .tls)
|
||||||
model.set([.compressionFraming, .compressionAlgorithm], in: .compression)
|
model.set([.compressionFraming, .compressionAlgorithm], forSection: .compression)
|
||||||
model.set([.keepAlive, .renegSeconds, .randomEndpoint], in: .other)
|
model.set([.keepAlive, .renegSeconds, .randomEndpoint], forSection: .other)
|
||||||
|
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
@ -193,15 +194,15 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
@ -209,7 +210,7 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -313,7 +314,7 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
|
|
||||||
switch model.row(at: indexPath) {
|
switch model.row(at: indexPath) {
|
||||||
case .cipher:
|
case .cipher:
|
||||||
let vc = OptionViewController<OpenVPN.Cipher>()
|
let vc = SingleOptionViewController<OpenVPN.Cipher>()
|
||||||
vc.title = settingCell?.leftText
|
vc.title = settingCell?.leftText
|
||||||
vc.options = OpenVPN.Cipher.available
|
vc.options = OpenVPN.Cipher.available
|
||||||
vc.selectedOption = configuration.cipher
|
vc.selectedOption = configuration.cipher
|
||||||
|
@ -325,7 +326,7 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
navigationController?.pushViewController(vc, animated: true)
|
navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
case .digest:
|
case .digest:
|
||||||
let vc = OptionViewController<OpenVPN.Digest>()
|
let vc = SingleOptionViewController<OpenVPN.Digest>()
|
||||||
vc.title = settingCell?.leftText
|
vc.title = settingCell?.leftText
|
||||||
vc.options = OpenVPN.Digest.available
|
vc.options = OpenVPN.Digest.available
|
||||||
vc.selectedOption = configuration.digest
|
vc.selectedOption = configuration.digest
|
||||||
|
@ -337,7 +338,7 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
navigationController?.pushViewController(vc, animated: true)
|
navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
case .compressionFraming:
|
case .compressionFraming:
|
||||||
let vc = OptionViewController<OpenVPN.CompressionFraming>()
|
let vc = SingleOptionViewController<OpenVPN.CompressionFraming>()
|
||||||
vc.title = settingCell?.leftText
|
vc.title = settingCell?.leftText
|
||||||
vc.options = OpenVPN.CompressionFraming.available
|
vc.options = OpenVPN.CompressionFraming.available
|
||||||
vc.selectedOption = configuration.compressionFraming ?? .disabled
|
vc.selectedOption = configuration.compressionFraming ?? .disabled
|
||||||
|
@ -356,7 +357,7 @@ extension ConfigurationViewController: UITableViewDataSource, UITableViewDelegat
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let vc = OptionViewController<OpenVPN.CompressionAlgorithm>()
|
let vc = SingleOptionViewController<OpenVPN.CompressionAlgorithm>()
|
||||||
vc.title = settingCell?.leftText
|
vc.title = settingCell?.leftText
|
||||||
vc.options = OpenVPN.CompressionAlgorithm.available
|
vc.options = OpenVPN.CompressionAlgorithm.available
|
||||||
vc.selectedOption = configuration.compressionAlgorithm ?? .disabled
|
vc.selectedOption = configuration.compressionAlgorithm ?? .disabled
|
||||||
|
|
|
@ -84,7 +84,7 @@ class DebugLogViewController: UIViewController {
|
||||||
|
|
||||||
@IBAction private func share(_ sender: Any?) {
|
@IBAction private func share(_ sender: Any?) {
|
||||||
guard let raw = textLog?.text, !raw.isEmpty else {
|
guard let raw = textLog?.text, !raw.isEmpty else {
|
||||||
let alert = Macros.alert(title, L10n.Core.DebugLog.Alerts.EmptyLog.message)
|
let alert = UIAlertController.asAlert(title, L10n.Core.DebugLog.Alerts.EmptyLog.message)
|
||||||
alert.addCancelAction(L10n.Core.Global.ok)
|
alert.addCancelAction(L10n.Core.Global.ok)
|
||||||
present(alert, animated: true, completion: nil)
|
present(alert, animated: true, completion: nil)
|
||||||
return
|
return
|
||||||
|
|
|
@ -26,12 +26,13 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
protocol EndpointViewControllerDelegate: class {
|
protocol EndpointViewControllerDelegate: class {
|
||||||
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?)
|
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?)
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndpointViewController: UIViewController, TableModelHost {
|
class EndpointViewController: UIViewController, StrongTableHost {
|
||||||
@IBOutlet private weak var tableView: UITableView!
|
@IBOutlet private weak var tableView: UITableView!
|
||||||
|
|
||||||
private lazy var itemRefresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
|
private lazy var itemRefresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
|
||||||
|
@ -58,28 +59,28 @@ class EndpointViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
weak var modificationDelegate: ConfigurationModificationDelegate?
|
weak var modificationDelegate: ConfigurationModificationDelegate?
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
lazy var model: TableModel<SectionType, RowType> = {
|
lazy var model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
model.add(.locationAddresses)
|
model.add(.locationAddresses)
|
||||||
model.add(.locationProtocols)
|
model.add(.locationProtocols)
|
||||||
|
|
||||||
model.setHeader(L10n.App.Endpoint.Sections.LocationAddresses.header, for: .locationAddresses)
|
model.setHeader(L10n.App.Endpoint.Sections.LocationAddresses.header, forSection: .locationAddresses)
|
||||||
model.setHeader(L10n.App.Endpoint.Sections.LocationProtocols.header, for: .locationProtocols)
|
model.setHeader(L10n.App.Endpoint.Sections.LocationProtocols.header, forSection: .locationProtocols)
|
||||||
|
|
||||||
if dataSource.canCustomizeEndpoint {
|
if dataSource.canCustomizeEndpoint {
|
||||||
var addressRows: [RowType] = Array(repeating: .availableAddress, count: dataSource.addresses.count)
|
var addressRows: [RowType] = Array(repeating: .availableAddress, count: dataSource.addresses.count)
|
||||||
addressRows.insert(.anyAddress, at: 0)
|
addressRows.insert(.anyAddress, at: 0)
|
||||||
model.set(addressRows, in: .locationAddresses)
|
model.set(addressRows, forSection: .locationAddresses)
|
||||||
|
|
||||||
var protocolRows: [RowType] = Array(repeating: .availableProtocol, count: dataSource.protocols.count)
|
var protocolRows: [RowType] = Array(repeating: .availableProtocol, count: dataSource.protocols.count)
|
||||||
protocolRows.insert(.anyProtocol, at: 0)
|
protocolRows.insert(.anyProtocol, at: 0)
|
||||||
model.set(protocolRows, in: .locationProtocols)
|
model.set(protocolRows, forSection: .locationProtocols)
|
||||||
} else {
|
} else {
|
||||||
model.set(.availableAddress, count: dataSource.addresses.count, in: .locationAddresses)
|
model.set(.availableAddress, count: dataSource.addresses.count, forSection: .locationAddresses)
|
||||||
model.set(.availableProtocol, count: dataSource.protocols.count, in: .locationProtocols)
|
model.set(.availableProtocol, count: dataSource.protocols.count, forSection: .locationProtocols)
|
||||||
}
|
}
|
||||||
|
|
||||||
return model
|
return model
|
||||||
|
@ -188,19 +189,19 @@ extension EndpointViewController: UITableViewDataSource, UITableViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
|
import Convenience
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
@ -57,9 +58,9 @@ class NetworkSettingsViewController: UITableViewController {
|
||||||
|
|
||||||
private let networkSettings = ProfileNetworkSettings()
|
private let networkSettings = ProfileNetworkSettings()
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
model.clear()
|
model.clear()
|
||||||
|
@ -77,24 +78,24 @@ class NetworkSettingsViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// headers
|
// headers
|
||||||
model.setHeader("", for: .choices)
|
model.setHeader("", forSection: .choices)
|
||||||
model.setHeader(L10n.Core.NetworkSettings.Gateway.title, for: .manualGateway)
|
model.setHeader(L10n.Core.NetworkSettings.Gateway.title, forSection: .manualGateway)
|
||||||
model.setHeader(L10n.Core.NetworkSettings.Dns.title, for: .manualDNS)
|
model.setHeader(L10n.Core.NetworkSettings.Dns.title, forSection: .manualDNS)
|
||||||
model.setHeader(L10n.Core.NetworkSettings.Proxy.title, for: .manualProxy)
|
model.setHeader(L10n.Core.NetworkSettings.Proxy.title, forSection: .manualProxy)
|
||||||
|
|
||||||
// footers
|
// footers
|
||||||
// model.setFooter(L10n.Core.Configuration.Sections.Reset.footer, for: .reset)
|
// model.setFooter(L10n.Core.Configuration.Sections.Reset.footer, for: .reset)
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
model.set([.gateway, .dns, .proxy], in: .choices)
|
model.set([.gateway, .dns, .proxy], forSection: .choices)
|
||||||
model.set([.gatewayIPv4, .gatewayIPv6], in: .manualGateway)
|
model.set([.gatewayIPv4, .gatewayIPv6], forSection: .manualGateway)
|
||||||
|
|
||||||
var dnsRows: [RowType] = Array(repeating: .dnsAddress, count: networkSettings.dnsServers?.count ?? 0)
|
var dnsRows: [RowType] = Array(repeating: .dnsAddress, count: networkSettings.dnsServers?.count ?? 0)
|
||||||
dnsRows.insert(.dnsDomain, at: 0)
|
dnsRows.insert(.dnsDomain, at: 0)
|
||||||
if networkChoices.dns == .manual {
|
if networkChoices.dns == .manual {
|
||||||
dnsRows.append(.dnsAddAddress)
|
dnsRows.append(.dnsAddAddress)
|
||||||
}
|
}
|
||||||
model.set(dnsRows, in: .manualDNS)
|
model.set(dnsRows, forSection: .manualDNS)
|
||||||
|
|
||||||
var proxyRows: [RowType] = Array(repeating: .proxyBypass, count: networkSettings.proxyBypassDomains?.count ?? 0)
|
var proxyRows: [RowType] = Array(repeating: .proxyBypass, count: networkSettings.proxyBypassDomains?.count ?? 0)
|
||||||
proxyRows.insert(.proxyAddress, at: 0)
|
proxyRows.insert(.proxyAddress, at: 0)
|
||||||
|
@ -102,7 +103,7 @@ class NetworkSettingsViewController: UITableViewController {
|
||||||
if networkChoices.proxy == .manual {
|
if networkChoices.proxy == .manual {
|
||||||
proxyRows.append(.proxyAddBypass)
|
proxyRows.append(.proxyAddBypass)
|
||||||
}
|
}
|
||||||
model.set(proxyRows, in: .manualProxy)
|
model.set(proxyRows, forSection: .manualProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
@ -262,15 +263,15 @@ extension NetworkSettingsViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
@ -278,7 +279,7 @@ extension NetworkSettingsViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -287,19 +288,19 @@ extension NetworkSettingsViewController {
|
||||||
switch row {
|
switch row {
|
||||||
case .gateway:
|
case .gateway:
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||||
cell.leftText = model.header(for: .manualGateway)
|
cell.leftText = model.header(forSection: .manualGateway)
|
||||||
cell.rightText = networkChoices.gateway.description
|
cell.rightText = networkChoices.gateway.description
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .dns:
|
case .dns:
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||||
cell.leftText = model.header(for: .manualDNS)
|
cell.leftText = model.header(forSection: .manualDNS)
|
||||||
cell.rightText = networkChoices.dns.description
|
cell.rightText = networkChoices.dns.description
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .proxy:
|
case .proxy:
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||||
cell.leftText = model.header(for: .manualProxy)
|
cell.leftText = model.header(forSection: .manualProxy)
|
||||||
cell.rightText = networkChoices.proxy.description
|
cell.rightText = networkChoices.proxy.description
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
|
@ -405,7 +406,7 @@ extension NetworkSettingsViewController {
|
||||||
|
|
||||||
switch model.row(at: indexPath) {
|
switch model.row(at: indexPath) {
|
||||||
case .gateway:
|
case .gateway:
|
||||||
let vc = OptionViewController<NetworkChoice>()
|
let vc = SingleOptionViewController<NetworkChoice>()
|
||||||
vc.title = (cell as? SettingTableViewCell)?.leftText
|
vc.title = (cell as? SettingTableViewCell)?.leftText
|
||||||
vc.options = NetworkChoice.choices(for: profile)
|
vc.options = NetworkChoice.choices(for: profile)
|
||||||
vc.descriptionBlock = { $0.description }
|
vc.descriptionBlock = { $0.description }
|
||||||
|
@ -418,7 +419,7 @@ extension NetworkSettingsViewController {
|
||||||
navigationController?.pushViewController(vc, animated: true)
|
navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
case .dns:
|
case .dns:
|
||||||
let vc = OptionViewController<NetworkChoice>()
|
let vc = SingleOptionViewController<NetworkChoice>()
|
||||||
vc.title = (cell as? SettingTableViewCell)?.leftText
|
vc.title = (cell as? SettingTableViewCell)?.leftText
|
||||||
vc.options = NetworkChoice.choices(for: profile)
|
vc.options = NetworkChoice.choices(for: profile)
|
||||||
vc.descriptionBlock = { $0.description }
|
vc.descriptionBlock = { $0.description }
|
||||||
|
@ -431,7 +432,7 @@ extension NetworkSettingsViewController {
|
||||||
navigationController?.pushViewController(vc, animated: true)
|
navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
case .proxy:
|
case .proxy:
|
||||||
let vc = OptionViewController<NetworkChoice>()
|
let vc = SingleOptionViewController<NetworkChoice>()
|
||||||
vc.title = (cell as? SettingTableViewCell)?.leftText
|
vc.title = (cell as? SettingTableViewCell)?.leftText
|
||||||
vc.options = NetworkChoice.choices(for: profile)
|
vc.options = NetworkChoice.choices(for: profile)
|
||||||
vc.descriptionBlock = { $0.description }
|
vc.descriptionBlock = { $0.description }
|
||||||
|
|
|
@ -26,9 +26,10 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import StoreKit
|
import StoreKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
class DonationViewController: UITableViewController, TableModelHost {
|
class DonationViewController: UITableViewController, StrongTableHost {
|
||||||
private var donationList: [InApp.Donation] = []
|
private var donationList: [Donation] = []
|
||||||
|
|
||||||
private var productsByIdentifier: [String: SKProduct] = [:]
|
private var productsByIdentifier: [String: SKProduct] = [:]
|
||||||
|
|
||||||
|
@ -44,35 +45,34 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: TableModel
|
// MARK: StrongTableModel
|
||||||
|
|
||||||
var model: TableModel<SectionType, RowType> = TableModel()
|
var model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
donationList = []
|
donationList = []
|
||||||
model.clear()
|
model.clear()
|
||||||
|
|
||||||
model.add(.oneTime)
|
model.add(.oneTime)
|
||||||
model.setHeader(L10n.Core.Donation.Sections.OneTime.header, for: .oneTime)
|
model.setHeader(L10n.Core.Donation.Sections.OneTime.header, forSection: .oneTime)
|
||||||
model.setFooter(L10n.Core.Donation.Sections.OneTime.footer, for: .oneTime)
|
model.setFooter(L10n.Core.Donation.Sections.OneTime.footer, forSection: .oneTime)
|
||||||
|
|
||||||
guard !isLoading else {
|
guard !isLoading else {
|
||||||
model.set([.loading], in: .oneTime)
|
model.set([.loading], forSection: .oneTime)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let completeList: [InApp.Donation] = [.tiny, .small, .medium, .big, .huge, .maxi]
|
for row in Donation.all {
|
||||||
for row in completeList {
|
|
||||||
guard let _ = productsByIdentifier[row.rawValue] else {
|
guard let _ = productsByIdentifier[row.rawValue] else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
donationList.append(row)
|
donationList.append(row)
|
||||||
}
|
}
|
||||||
model.set(.donation, count: donationList.count, in: .oneTime)
|
model.set(.donation, count: donationList.count, forSection: .oneTime)
|
||||||
|
|
||||||
if isPurchasing {
|
if isPurchasing {
|
||||||
model.add(.activity)
|
model.add(.activity)
|
||||||
model.set([.purchasing], in: .activity)
|
model.set([.purchasing], forSection: .activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,15 +84,9 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
title = L10n.Core.Donation.title
|
title = L10n.Core.Donation.title
|
||||||
reloadModel()
|
reloadModel()
|
||||||
|
|
||||||
let inApp = InAppHelper.shared
|
ProductManager.shared.listProducts {
|
||||||
if inApp.products.isEmpty {
|
self.isLoading = false
|
||||||
inApp.requestProducts(withIdentifiers: InApp.allIdentifiers()) {
|
self.setProducts($0)
|
||||||
self.isLoading = false
|
|
||||||
self.setProducts($0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
isLoading = false
|
|
||||||
setProducts(inApp.products)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,19 +97,19 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
// MARK: UITableViewController
|
// MARK: UITableViewController
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -159,7 +153,7 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
reloadModel()
|
reloadModel()
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
|
|
||||||
InAppHelper.shared.purchase(product: product) {
|
ProductManager.shared.purchase(product) {
|
||||||
self.handlePurchase(result: $0, error: $1)
|
self.handlePurchase(result: $0, error: $1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +162,7 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handlePurchase(result: InAppHelper.PurchaseResult, error: Error?) {
|
private func handlePurchase(result: InAppPurchaseResult, error: Error?) {
|
||||||
let alert: UIAlertController
|
let alert: UIAlertController
|
||||||
switch result {
|
switch result {
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
|
@ -178,10 +172,10 @@ class DonationViewController: UITableViewController, TableModelHost {
|
||||||
return
|
return
|
||||||
|
|
||||||
case .success:
|
case .success:
|
||||||
alert = Macros.alert(L10n.Core.Donation.Alerts.Purchase.Success.title, L10n.Core.Donation.Alerts.Purchase.Success.message)
|
alert = UIAlertController.asAlert(L10n.Core.Donation.Alerts.Purchase.Success.title, L10n.Core.Donation.Alerts.Purchase.Success.message)
|
||||||
|
|
||||||
case .failure:
|
case .failure:
|
||||||
alert = Macros.alert(title, L10n.Core.Donation.Alerts.Purchase.Failure.message(error?.localizedDescription ?? ""))
|
alert = UIAlertController.asAlert(title, L10n.Core.Donation.Alerts.Purchase.Failure.message(error?.localizedDescription ?? ""))
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.ok) {
|
alert.addCancelAction(L10n.Core.Global.ok) {
|
||||||
self.isPurchasing = false
|
self.isPurchasing = false
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
guard !pendingConfigurationURLs.isEmpty else {
|
guard !pendingConfigurationURLs.isEmpty else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
title,
|
title,
|
||||||
L10n.Core.Organizer.Alerts.AddHost.message
|
L10n.Core.Organizer.Alerts.AddHost.message
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,10 +27,11 @@ import UIKit
|
||||||
import StoreKit
|
import StoreKit
|
||||||
import MessageUI
|
import MessageUI
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
// XXX: convoluted due to the separation of provider/host profiles
|
// XXX: convoluted due to the separation of provider/host profiles
|
||||||
|
|
||||||
class OrganizerViewController: UITableViewController, TableModelHost {
|
class OrganizerViewController: UITableViewController, StrongTableHost {
|
||||||
private let service = TransientStore.shared.service
|
private let service = TransientStore.shared.service
|
||||||
|
|
||||||
private var providers: [String] = []
|
private var providers: [String] = []
|
||||||
|
@ -41,10 +42,10 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
|
|
||||||
private var didShowSubreddit = false
|
private var didShowSubreddit = false
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = {
|
let model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.add(.vpn)
|
model.add(.vpn)
|
||||||
model.add(.providers)
|
model.add(.providers)
|
||||||
model.add(.hosts)
|
model.add(.hosts)
|
||||||
|
@ -55,27 +56,27 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
model.add(.feedback)
|
model.add(.feedback)
|
||||||
model.add(.about)
|
model.add(.about)
|
||||||
model.add(.destruction)
|
model.add(.destruction)
|
||||||
model.setHeader(L10n.App.Service.Sections.Vpn.header, for: .vpn)
|
model.setHeader(L10n.App.Service.Sections.Vpn.header, forSection: .vpn)
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Providers.header, for: .providers)
|
model.setHeader(L10n.Core.Organizer.Sections.Providers.header, forSection: .providers)
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Hosts.header, for: .hosts)
|
model.setHeader(L10n.Core.Organizer.Sections.Hosts.header, forSection: .hosts)
|
||||||
model.setFooter(L10n.Core.Organizer.Sections.Providers.footer, for: .providers)
|
model.setFooter(L10n.Core.Organizer.Sections.Providers.footer, forSection: .providers)
|
||||||
model.setFooter(L10n.Core.Organizer.Sections.Hosts.footer, for: .hosts)
|
model.setFooter(L10n.Core.Organizer.Sections.Hosts.footer, forSection: .hosts)
|
||||||
if #available(iOS 12, *) {
|
if #available(iOS 12, *) {
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Siri.header, for: .siri)
|
model.setHeader(L10n.Core.Organizer.Sections.Siri.header, forSection: .siri)
|
||||||
model.setFooter(L10n.Core.Organizer.Sections.Siri.footer, for: .siri)
|
model.setFooter(L10n.Core.Organizer.Sections.Siri.footer, forSection: .siri)
|
||||||
model.set([.siriShortcuts], in: .siri)
|
model.set([.siriShortcuts], forSection: .siri)
|
||||||
}
|
}
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Support.header, for: .support)
|
model.setHeader(L10n.Core.Organizer.Sections.Support.header, forSection: .support)
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Feedback.header, for: .feedback)
|
model.setHeader(L10n.Core.Organizer.Sections.Feedback.header, forSection: .feedback)
|
||||||
model.set([.connectionStatus], in: .vpn)
|
model.set([.connectionStatus], forSection: .vpn)
|
||||||
model.set([.donate, .translate], in: .support)
|
model.set([.donate, .translate], forSection: .support)
|
||||||
model.set([.joinCommunity, .writeReview], in: .feedback)
|
model.set([.joinCommunity, .writeReview], forSection: .feedback)
|
||||||
model.set([.openAbout], in: .about)
|
model.set([.openAbout], forSection: .about)
|
||||||
model.set([.uninstall], in: .destruction)
|
model.set([.uninstall], forSection: .destruction)
|
||||||
if AppConstants.Flags.isBeta {
|
if AppConstants.Flags.isBeta {
|
||||||
model.add(.test)
|
model.add(.test)
|
||||||
model.setHeader("Beta", for: .test)
|
model.setHeader("Beta", forSection: .test)
|
||||||
model.set([.testDisplayLog, .testTermination], in: .test)
|
model.set([.testDisplayLog, .testTermination], forSection: .test)
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
@ -89,8 +90,8 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
providerRows.append(.addProvider)
|
providerRows.append(.addProvider)
|
||||||
hostRows.append(.addHost)
|
hostRows.append(.addHost)
|
||||||
|
|
||||||
model.set(providerRows, in: .providers)
|
model.set(providerRows, forSection: .providers)
|
||||||
model.set(hostRows, in: .hosts)
|
model.set(hostRows, forSection: .hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
@ -125,8 +126,8 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
if !didShowSubreddit && !TransientStore.didHandleSubreddit {
|
if !didShowSubreddit && !TransientStore.didHandleSubreddit {
|
||||||
didShowSubreddit = true
|
didShowSubreddit = true
|
||||||
|
|
||||||
let alert = Macros.alert(L10n.Core.Reddit.title, L10n.Core.Reddit.message)
|
let alert = UIAlertController.asAlert(L10n.Core.Reddit.title, L10n.Core.Reddit.message)
|
||||||
alert.addDefaultAction(L10n.Core.Reddit.Buttons.subscribe) {
|
alert.addPreferredAction(L10n.Core.Reddit.Buttons.subscribe) {
|
||||||
TransientStore.didHandleSubreddit = true
|
TransientStore.didHandleSubreddit = true
|
||||||
self.subscribeSubreddit()
|
self.subscribeSubreddit()
|
||||||
}
|
}
|
||||||
|
@ -185,7 +186,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
private func addNewProvider() {
|
private func addNewProvider() {
|
||||||
let names = service.availableProviderNames()
|
let names = service.availableProviderNames()
|
||||||
guard !names.isEmpty else {
|
guard !names.isEmpty else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Organizer.Sections.Providers.header,
|
L10n.Core.Organizer.Sections.Providers.header,
|
||||||
L10n.Core.Organizer.Alerts.ExhaustedProviders.message
|
L10n.Core.Organizer.Alerts.ExhaustedProviders.message
|
||||||
)
|
)
|
||||||
|
@ -207,7 +208,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
|
|
||||||
private func donateToDeveloper() {
|
private func donateToDeveloper() {
|
||||||
guard SKPaymentQueue.canMakePayments() else {
|
guard SKPaymentQueue.canMakePayments() else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Organizer.Cells.Donate.caption,
|
L10n.Core.Organizer.Cells.Donate.caption,
|
||||||
L10n.Core.Organizer.Alerts.CannotDonate.message
|
L10n.Core.Organizer.Alerts.CannotDonate.message
|
||||||
)
|
)
|
||||||
|
@ -230,8 +231,8 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
|
|
||||||
guard MFMailComposeViewController.canSendMail() else {
|
guard MFMailComposeViewController.canSendMail() else {
|
||||||
let app = UIApplication.shared
|
let app = UIApplication.shared
|
||||||
guard let url = Utils.mailto(to: recipient, subject: subject, body: body), app.canOpenURL(url) else {
|
guard let url = URL.mailto(to: recipient, subject: subject, body: body), app.canOpenURL(url) else {
|
||||||
let alert = Macros.alert(L10n.Core.Translations.title, L10n.Core.Global.emailNotConfigured)
|
let alert = UIAlertController.asAlert(L10n.Core.Translations.title, L10n.Core.Global.emailNotConfigured)
|
||||||
alert.addCancelAction(L10n.Core.Global.ok)
|
alert.addCancelAction(L10n.Core.Global.ok)
|
||||||
present(alert, animated: true, completion: nil)
|
present(alert, animated: true, completion: nil)
|
||||||
return
|
return
|
||||||
|
@ -254,7 +255,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removeProfile(at indexPath: IndexPath) {
|
private func removeProfile(at indexPath: IndexPath) {
|
||||||
let sectionObject = model.section(for: indexPath.section)
|
let sectionObject = model.section(forIndex: indexPath.section)
|
||||||
let rowProfile = profileKey(at: indexPath)
|
let rowProfile = profileKey(at: indexPath)
|
||||||
switch sectionObject {
|
switch sectionObject {
|
||||||
case .providers:
|
case .providers:
|
||||||
|
@ -288,7 +289,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
model.deleteRow(in: sectionObject, at: indexPath.row)
|
model.deleteRow(at: indexPath.row, ofSection: sectionObject)
|
||||||
tableView.deleteRows(at: [indexPath], with: .automatic)
|
tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||||
// if let fallbackSection = fallbackSection {
|
// if let fallbackSection = fallbackSection {
|
||||||
// let section = model.index(ofSection: fallbackSection)
|
// let section = model.index(ofSection: fallbackSection)
|
||||||
|
@ -303,11 +304,11 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func confirmVpnProfileDeletion() {
|
private func confirmVpnProfileDeletion() {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Organizer.Cells.Uninstall.caption,
|
L10n.Core.Organizer.Cells.Uninstall.caption,
|
||||||
L10n.Core.Organizer.Alerts.DeleteVpnProfile.message
|
L10n.Core.Organizer.Alerts.DeleteVpnProfile.message
|
||||||
)
|
)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
VPN.shared.uninstall(completionHandler: nil)
|
VPN.shared.uninstall(completionHandler: nil)
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel)
|
alert.addCancelAction(L10n.Core.Global.cancel)
|
||||||
|
@ -329,7 +330,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
guard let log = try? String(contentsOf: AppConstants.Log.fileURL) else {
|
guard let log = try? String(contentsOf: AppConstants.Log.fileURL) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let alert = Macros.alert("Debug log", log)
|
let alert = UIAlertController.asAlert("Debug log", log)
|
||||||
alert.addCancelAction(L10n.Core.Global.ok)
|
alert.addCancelAction(L10n.Core.Global.ok)
|
||||||
present(alert, animated: true, completion: nil)
|
present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -399,19 +400,19 @@ extension OrganizerViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -480,7 +481,7 @@ extension OrganizerViewController {
|
||||||
case .openAbout:
|
case .openAbout:
|
||||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||||
cell.leftText = L10n.Core.Organizer.Cells.About.caption(GroupConstants.App.name)
|
cell.leftText = L10n.Core.Organizer.Cells.About.caption(GroupConstants.App.name)
|
||||||
cell.rightText = Utils.versionString()
|
cell.rightText = ApplicationInfo.appVersion
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .uninstall:
|
case .uninstall:
|
||||||
|
@ -562,7 +563,7 @@ extension OrganizerViewController {
|
||||||
|
|
||||||
private func sectionProfiles(at indexPath: IndexPath) -> [String] {
|
private func sectionProfiles(at indexPath: IndexPath) -> [String] {
|
||||||
let ids: [String]
|
let ids: [String]
|
||||||
let sectionObject = model.section(for: indexPath.section)
|
let sectionObject = model.section(forIndex: indexPath.section)
|
||||||
switch sectionObject {
|
switch sectionObject {
|
||||||
case .providers:
|
case .providers:
|
||||||
ids = providers
|
ids = providers
|
||||||
|
@ -580,7 +581,7 @@ extension OrganizerViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func profileKey(at indexPath: IndexPath) -> ProfileKey {
|
private func profileKey(at indexPath: IndexPath) -> ProfileKey {
|
||||||
let section = model.section(for: indexPath.section)
|
let section = model.section(forIndex: indexPath.section)
|
||||||
switch section {
|
switch section {
|
||||||
case .providers:
|
case .providers:
|
||||||
return ProfileKey(.provider, providers[indexPath.row])
|
return ProfileKey(.provider, providers[indexPath.row])
|
||||||
|
@ -595,7 +596,7 @@ extension OrganizerViewController {
|
||||||
|
|
||||||
private func profile(at indexPath: IndexPath) -> ConnectionProfile {
|
private func profile(at indexPath: IndexPath) -> ConnectionProfile {
|
||||||
let id = sectionProfiles(at: indexPath)[indexPath.row]
|
let id = sectionProfiles(at: indexPath)[indexPath.row]
|
||||||
let section = model.section(for: indexPath.section)
|
let section = model.section(forIndex: indexPath.section)
|
||||||
let context: Context
|
let context: Context
|
||||||
switch section {
|
switch section {
|
||||||
case .providers:
|
case .providers:
|
||||||
|
|
|
@ -27,10 +27,11 @@ import UIKit
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
class WizardHostViewController: UITableViewController, TableModelHost {
|
class WizardHostViewController: UITableViewController, StrongTableHost {
|
||||||
@IBOutlet private weak var itemNext: UIBarButtonItem!
|
@IBOutlet private weak var itemNext: UIBarButtonItem!
|
||||||
|
|
||||||
private let existingHosts: [String] = {
|
private let existingHosts: [String] = {
|
||||||
|
@ -47,18 +48,18 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
|
|
||||||
private var createdProfile: HostConnectionProfile?
|
private var createdProfile: HostConnectionProfile?
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
lazy var model: TableModel<SectionType, RowType> = {
|
lazy var model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.add(.meta)
|
model.add(.meta)
|
||||||
model.setFooter(L10n.Core.Global.Host.TitleInput.message, for: .meta)
|
model.setFooter(L10n.Core.Global.Host.TitleInput.message, forSection: .meta)
|
||||||
if !existingHosts.isEmpty {
|
if !existingHosts.isEmpty {
|
||||||
model.add(.existing)
|
model.add(.existing)
|
||||||
model.setHeader(L10n.App.Wizards.Host.Sections.Existing.header, for: .existing)
|
model.setHeader(L10n.App.Wizards.Host.Sections.Existing.header, forSection: .existing)
|
||||||
}
|
}
|
||||||
model.set([.titleInput], in: .meta)
|
model.set([.titleInput], forSection: .meta)
|
||||||
model.set(.existingHost, count: existingHosts.count, in: .existing)
|
model.set(.existingHost, count: existingHosts.count, forSection: .existing)
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -110,8 +111,8 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
let service = TransientStore.shared.service
|
let service = TransientStore.shared.service
|
||||||
guard !service.containsProfile(profile) else {
|
guard !service.containsProfile(profile) else {
|
||||||
let replacedProfile = service.profile(withContext: profile.context, id: profile.id)
|
let replacedProfile = service.profile(withContext: profile.context, id: profile.id)
|
||||||
let alert = Macros.alert(title, L10n.Core.Wizards.Host.Alerts.Existing.message)
|
let alert = UIAlertController.asAlert(title, L10n.Core.Wizards.Host.Alerts.Existing.message)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
self.next(withProfile: profile, replacedProfile: replacedProfile)
|
self.next(withProfile: profile, replacedProfile: replacedProfile)
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel)
|
alert.addCancelAction(L10n.Core.Global.cancel)
|
||||||
|
@ -177,26 +178,26 @@ extension WizardHostViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var cellTitle: FieldTableViewCell? {
|
private var cellTitle: FieldTableViewCell? {
|
||||||
guard let ip = model.indexPath(row: .titleInput, section: .meta) else {
|
guard let ip = model.indexPath(forRow: .titleInput, ofSection: .meta) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return tableView.cellForRow(at: ip) as? FieldTableViewCell
|
return tableView.cellForRow(at: ip) as? FieldTableViewCell
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -223,7 +224,7 @@ extension WizardHostViewController {
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
switch model.row(at: indexPath) {
|
switch model.row(at: indexPath) {
|
||||||
case .existingHost:
|
case .existingHost:
|
||||||
guard let titleIndexPath = model.indexPath(row: .titleInput, section: .meta) else {
|
guard let titleIndexPath = model.indexPath(forRow: .titleInput, ofSection: .meta) else {
|
||||||
fatalError("Could not found title cell?")
|
fatalError("Could not found title cell?")
|
||||||
}
|
}
|
||||||
let hostTitle = existingHosts[indexPath.row]
|
let hostTitle = existingHosts[indexPath.row]
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
protocol ProviderPoolViewControllerDelegate: class {
|
protocol ProviderPoolViewControllerDelegate: class {
|
||||||
func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool)
|
func providerPoolController(_: ProviderPoolViewController, didSelectPool pool: Pool)
|
||||||
|
@ -148,7 +149,7 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
||||||
guard group.pools.count > 1 else {
|
guard group.pools.count > 1 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let vc = OptionViewController<Pool>()
|
let vc = SingleOptionViewController<Pool>()
|
||||||
vc.title = group.localizedCountry
|
vc.title = group.localizedCountry
|
||||||
vc.options = group.pools.sorted {
|
vc.options = group.pools.sorted {
|
||||||
guard let lnum = $0.num else {
|
guard let lnum = $0.num else {
|
||||||
|
|
|
@ -28,8 +28,9 @@ import NetworkExtension
|
||||||
import MBProgressHUD
|
import MBProgressHUD
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
class ServiceViewController: UIViewController, TableModelHost {
|
class ServiceViewController: UIViewController, StrongTableHost {
|
||||||
@IBOutlet private weak var tableView: UITableView!
|
@IBOutlet private weak var tableView: UITableView!
|
||||||
|
|
||||||
@IBOutlet private weak var viewWelcome: UIView!
|
@IBOutlet private weak var viewWelcome: UIView!
|
||||||
|
@ -38,6 +39,11 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
@IBOutlet private weak var itemEdit: UIBarButtonItem!
|
@IBOutlet private weak var itemEdit: UIBarButtonItem!
|
||||||
|
|
||||||
|
private let downloader = FileDownloader(
|
||||||
|
temporaryURL: GroupConstants.App.cachesURL.appendingPathComponent("downloaded.tmp"),
|
||||||
|
timeout: AppConstants.Web.timeout
|
||||||
|
)
|
||||||
|
|
||||||
private var profile: ConnectionProfile?
|
private var profile: ConnectionProfile?
|
||||||
|
|
||||||
private let service = TransientStore.shared.service
|
private let service = TransientStore.shared.service
|
||||||
|
@ -54,7 +60,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
// MARK: Table
|
// MARK: Table
|
||||||
|
|
||||||
var model: TableModel<SectionType, RowType> = TableModel()
|
var model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
|
|
||||||
private let trustedNetworks = TrustedNetworksModel()
|
private let trustedNetworks = TrustedNetworksModel()
|
||||||
|
|
||||||
|
@ -217,13 +223,13 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func renameProfile() {
|
@IBAction private func renameProfile() {
|
||||||
let alert = Macros.alert(L10n.Core.Service.Alerts.Rename.title, L10n.Core.Global.Host.TitleInput.message)
|
let alert = UIAlertController.asAlert(L10n.Core.Service.Alerts.Rename.title, L10n.Core.Global.Host.TitleInput.message)
|
||||||
alert.addTextField { (field) in
|
alert.addTextField { (field) in
|
||||||
field.text = self.profile?.id
|
field.text = self.profile?.id
|
||||||
field.applyProfileId(Theme.current)
|
field.applyProfileId(Theme.current)
|
||||||
field.delegate = self
|
field.delegate = self
|
||||||
}
|
}
|
||||||
pendingRenameAction = alert.addDefaultAction(L10n.Core.Global.ok) {
|
pendingRenameAction = alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
guard let newId = alert.textFields?.first?.text else {
|
guard let newId = alert.textFields?.first?.text else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -245,7 +251,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
IntentDispatcher.donateConnection(with: uncheckedProfile)
|
IntentDispatcher.donateConnection(with: uncheckedProfile)
|
||||||
}
|
}
|
||||||
guard !service.needsCredentials(for: uncheckedProfile) else {
|
guard !service.needsCredentials(for: uncheckedProfile) else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.App.Service.Sections.Vpn.header,
|
L10n.App.Service.Sections.Vpn.header,
|
||||||
L10n.Core.Service.Alerts.CredentialsNeeded.message
|
L10n.Core.Service.Alerts.CredentialsNeeded.message
|
||||||
)
|
)
|
||||||
|
@ -283,11 +289,11 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
private func confirmVpnReconnection() {
|
private func confirmVpnReconnection() {
|
||||||
guard vpn.status == .disconnected else {
|
guard vpn.status == .disconnected else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Cells.ConnectionStatus.caption,
|
L10n.Core.Service.Cells.ConnectionStatus.caption,
|
||||||
L10n.Core.Service.Alerts.ReconnectVpn.message
|
L10n.Core.Service.Alerts.ReconnectVpn.message
|
||||||
)
|
)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
self.vpn.reconnect(completionHandler: nil)
|
self.vpn.reconnect(completionHandler: nil)
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel)
|
alert.addCancelAction(L10n.Core.Global.cancel)
|
||||||
|
@ -300,7 +306,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
private func refreshProviderInfrastructure() {
|
private func refreshProviderInfrastructure() {
|
||||||
let name = uncheckedProviderProfile.name
|
let name = uncheckedProviderProfile.name
|
||||||
|
|
||||||
let hud = HUD()
|
let hud = HUD(view: view.window!)
|
||||||
let isUpdating = InfrastructureFactory.shared.update(name, notBeforeInterval: AppConstants.Web.minimumUpdateInterval) { (response, error) in
|
let isUpdating = InfrastructureFactory.shared.update(name, notBeforeInterval: AppConstants.Web.minimumUpdateInterval) { (response, error) in
|
||||||
hud.hide()
|
hud.hide()
|
||||||
guard let response = response else {
|
guard let response = response else {
|
||||||
|
@ -347,11 +353,11 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
completionHandler()
|
completionHandler()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Sections.Trusted.header,
|
L10n.Core.Service.Sections.Trusted.header,
|
||||||
L10n.Core.Service.Alerts.Trusted.WillDisconnectPolicy.message
|
L10n.Core.Service.Alerts.Trusted.WillDisconnectPolicy.message
|
||||||
)
|
)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel) {
|
alert.addCancelAction(L10n.Core.Global.cancel) {
|
||||||
|
@ -361,11 +367,11 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func confirmPotentialTrustedDisconnection(at rowIndex: Int?, completionHandler: @escaping () -> Void) {
|
private func confirmPotentialTrustedDisconnection(at rowIndex: Int?, completionHandler: @escaping () -> Void) {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Sections.Trusted.header,
|
L10n.Core.Service.Sections.Trusted.header,
|
||||||
L10n.Core.Service.Alerts.Trusted.WillDisconnectTrusted.message
|
L10n.Core.Service.Alerts.Trusted.WillDisconnectTrusted.message
|
||||||
)
|
)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel) {
|
alert.addCancelAction(L10n.Core.Global.cancel) {
|
||||||
|
@ -380,12 +386,12 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func testInternetConnectivity() {
|
private func testInternetConnectivity() {
|
||||||
let hud = HUD()
|
let hud = HUD(view: view.window!)
|
||||||
Utils.checkConnectivityURL(AppConstants.Web.connectivityURL, timeout: AppConstants.Web.connectivityTimeout) {
|
Utils.checkConnectivityURL(AppConstants.Web.connectivityURL, timeout: AppConstants.Web.connectivityTimeout) {
|
||||||
hud.hide()
|
hud.hide()
|
||||||
|
|
||||||
let V = L10n.Core.Service.Alerts.TestConnectivity.Messages.self
|
let V = L10n.Core.Service.Alerts.TestConnectivity.Messages.self
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Alerts.TestConnectivity.title,
|
L10n.Core.Service.Alerts.TestConnectivity.title,
|
||||||
$0 ? V.success : V.failure
|
$0 ? V.success : V.failure
|
||||||
)
|
)
|
||||||
|
@ -396,7 +402,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
// private func displayDataCount() {
|
// private func displayDataCount() {
|
||||||
// guard vpn.isEnabled else {
|
// guard vpn.isEnabled else {
|
||||||
// let alert = Macros.alert(
|
// let alert = UIAlertController.asAlert(
|
||||||
// L10n.Core.Service.Cells.DataCount.caption,
|
// L10n.Core.Service.Cells.DataCount.caption,
|
||||||
// L10n.Core.Service.Alerts.DataCount.Messages.notAvailable
|
// L10n.Core.Service.Alerts.DataCount.Messages.notAvailable
|
||||||
// )
|
// )
|
||||||
|
@ -412,7 +418,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
// } else {
|
// } else {
|
||||||
// message = L10n.Core.Service.Alerts.DataCount.Messages.notAvailable
|
// message = L10n.Core.Service.Alerts.DataCount.Messages.notAvailable
|
||||||
// }
|
// }
|
||||||
// let alert = Macros.alert(
|
// let alert = UIAlertController.asAlert(
|
||||||
// L10n.Core.Service.Cells.DataCount.caption,
|
// L10n.Core.Service.Cells.DataCount.caption,
|
||||||
// message
|
// message
|
||||||
// )
|
// )
|
||||||
|
@ -428,7 +434,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard vpn.status == .disconnected else {
|
guard vpn.status == .disconnected else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Cells.MasksPrivateData.caption,
|
L10n.Core.Service.Cells.MasksPrivateData.caption,
|
||||||
L10n.Core.Service.Alerts.MasksPrivateData.Messages.mustReconnect
|
L10n.Core.Service.Alerts.MasksPrivateData.Messages.mustReconnect
|
||||||
)
|
)
|
||||||
|
@ -462,26 +468,26 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Alerts.Download.title,
|
L10n.Core.Service.Alerts.Download.title,
|
||||||
L10n.Core.Service.Alerts.Download.message(providerProfile.name.rawValue)
|
L10n.Core.Service.Alerts.Download.message(providerProfile.name.rawValue)
|
||||||
)
|
)
|
||||||
alert.addCancelAction(L10n.Core.Global.cancel)
|
alert.addCancelAction(L10n.Core.Global.cancel)
|
||||||
alert.addDefaultAction(L10n.Core.Global.ok) {
|
alert.addPreferredAction(L10n.Core.Global.ok) {
|
||||||
self.confirmDownload(URL(string: downloadURL)!)
|
self.confirmDownload(URL(string: downloadURL)!)
|
||||||
}
|
}
|
||||||
present(alert, animated: true, completion: nil)
|
present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func confirmDownload(_ url: URL) {
|
private func confirmDownload(_ url: URL) {
|
||||||
_ = Downloader.shared.download(url: url, in: view) { (url, error) in
|
_ = downloader.download(url: url, in: view) { (url, error) in
|
||||||
self.handleDownloadedProviderResources(url: url, error: error)
|
self.handleDownloadedProviderResources(url: url, error: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleDownloadedProviderResources(url: URL?, error: Error?) {
|
private func handleDownloadedProviderResources(url: URL?, error: Error?) {
|
||||||
guard let url = url else {
|
guard let url = url else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Alerts.Download.title,
|
L10n.Core.Service.Alerts.Download.title,
|
||||||
L10n.Core.Service.Alerts.Download.failed(error?.localizedDescription ?? "")
|
L10n.Core.Service.Alerts.Download.failed(error?.localizedDescription ?? "")
|
||||||
)
|
)
|
||||||
|
@ -490,7 +496,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let hud = HUD(label: L10n.Core.Service.Alerts.Download.Hud.extracting)
|
let hud = HUD(view: view.window!, label: L10n.Core.Service.Alerts.Download.Hud.extracting)
|
||||||
hud.show()
|
hud.show()
|
||||||
uncheckedProviderProfile.name.importExternalResources(from: url) {
|
uncheckedProviderProfile.name.importExternalResources(from: url) {
|
||||||
hud.hide()
|
hud.hide()
|
||||||
|
@ -615,22 +621,22 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
}
|
}
|
||||||
|
|
||||||
private var statusIndexPath: IndexPath? {
|
private var statusIndexPath: IndexPath? {
|
||||||
return model.indexPath(row: .connectionStatus, section: .vpn)
|
return model.indexPath(forRow: .connectionStatus, ofSection: .vpn)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var dataCountIndexPath: IndexPath? {
|
private var dataCountIndexPath: IndexPath? {
|
||||||
return model.indexPath(row: .dataCount, section: .diagnostics)
|
return model.indexPath(forRow: .dataCount, ofSection: .diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var endpointIndexPath: IndexPath {
|
private var endpointIndexPath: IndexPath {
|
||||||
guard let ip = model.indexPath(row: .endpoint, section: .configuration) else {
|
guard let ip = model.indexPath(forRow: .endpoint, ofSection: .configuration) else {
|
||||||
fatalError("Could not locate endpointIndexPath")
|
fatalError("Could not locate endpointIndexPath")
|
||||||
}
|
}
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
private var providerPresetIndexPath: IndexPath {
|
private var providerPresetIndexPath: IndexPath {
|
||||||
guard let ip = model.indexPath(row: .providerPreset, section: .configuration) else {
|
guard let ip = model.indexPath(forRow: .providerPreset, ofSection: .configuration) else {
|
||||||
fatalError("Could not locate presetIndexPath")
|
fatalError("Could not locate presetIndexPath")
|
||||||
}
|
}
|
||||||
return ip
|
return ip
|
||||||
|
@ -650,19 +656,19 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
let rows = model.rows(for: section)
|
let rows = model.rows(forSection: section)
|
||||||
if rows.contains(.providerRefresh), let date = lastInfrastructureUpdate {
|
if rows.contains(.providerRefresh), let date = lastInfrastructureUpdate {
|
||||||
return L10n.Core.Service.Sections.ProviderInfrastructure.footer(date.timestamp)
|
return L10n.Core.Service.Sections.ProviderInfrastructure.footer(date.timestamp)
|
||||||
}
|
}
|
||||||
return model.footer(for: section)
|
return model.footer(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
@ -670,7 +676,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -931,7 +937,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
}
|
}
|
||||||
|
|
||||||
guard trustedNetworks.addCurrentWifi() else {
|
guard trustedNetworks.addCurrentWifi() else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Service.Sections.Trusted.header,
|
L10n.Core.Service.Sections.Trusted.header,
|
||||||
L10n.Core.Service.Alerts.Trusted.NoNetwork.message
|
L10n.Core.Service.Alerts.Trusted.NoNetwork.message
|
||||||
)
|
)
|
||||||
|
@ -1033,31 +1039,31 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
}
|
}
|
||||||
|
|
||||||
// headers
|
// headers
|
||||||
model.setHeader(L10n.App.Service.Sections.Vpn.header, for: .vpn)
|
model.setHeader(L10n.App.Service.Sections.Vpn.header, forSection: .vpn)
|
||||||
if isProvider {
|
if isProvider {
|
||||||
model.setHeader(L10n.App.Service.Sections.Configuration.header, for: .authentication)
|
model.setHeader(L10n.App.Service.Sections.Configuration.header, forSection: .authentication)
|
||||||
} else {
|
} else {
|
||||||
model.setHeader(L10n.App.Service.Sections.Configuration.header, for: .configuration)
|
model.setHeader(L10n.App.Service.Sections.Configuration.header, forSection: .configuration)
|
||||||
}
|
}
|
||||||
if isActiveProfile {
|
if isActiveProfile {
|
||||||
if isProvider {
|
if isProvider {
|
||||||
model.setHeader("", for: .vpnResolvesHostname)
|
model.setHeader("", forSection: .vpnResolvesHostname)
|
||||||
model.setHeader("", for: .vpnSurvivesSleep)
|
model.setHeader("", forSection: .vpnSurvivesSleep)
|
||||||
}
|
}
|
||||||
model.setHeader(L10n.Core.Service.Sections.Trusted.header, for: .trusted)
|
model.setHeader(L10n.Core.Service.Sections.Trusted.header, forSection: .trusted)
|
||||||
model.setHeader(L10n.Core.Service.Sections.Diagnostics.header, for: .diagnostics)
|
model.setHeader(L10n.Core.Service.Sections.Diagnostics.header, forSection: .diagnostics)
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Feedback.header, for: .feedback)
|
model.setHeader(L10n.Core.Organizer.Sections.Feedback.header, forSection: .feedback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// footers
|
// footers
|
||||||
if isActiveProfile {
|
if isActiveProfile {
|
||||||
model.setFooter(L10n.Core.Service.Sections.Vpn.footer, for: .vpn)
|
model.setFooter(L10n.Core.Service.Sections.Vpn.footer, forSection: .vpn)
|
||||||
if isProvider {
|
if isProvider {
|
||||||
model.setFooter(L10n.Core.Service.Sections.VpnResolvesHostname.footer, for: .vpnResolvesHostname)
|
model.setFooter(L10n.Core.Service.Sections.VpnResolvesHostname.footer, forSection: .vpnResolvesHostname)
|
||||||
}
|
}
|
||||||
model.setFooter(L10n.Core.Service.Sections.VpnSurvivesSleep.footer, for: .vpnSurvivesSleep)
|
model.setFooter(L10n.Core.Service.Sections.VpnSurvivesSleep.footer, forSection: .vpnSurvivesSleep)
|
||||||
model.setFooter(L10n.Core.Service.Sections.Trusted.footer, for: .trustedPolicy)
|
model.setFooter(L10n.Core.Service.Sections.Trusted.footer, forSection: .trustedPolicy)
|
||||||
model.setFooter(L10n.Core.Service.Sections.Diagnostics.footer, for: .diagnostics)
|
model.setFooter(L10n.Core.Service.Sections.Diagnostics.footer, forSection: .diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
@ -1066,30 +1072,30 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
|
||||||
if vpn.isEnabled {
|
if vpn.isEnabled {
|
||||||
rows.append(.reconnect)
|
rows.append(.reconnect)
|
||||||
}
|
}
|
||||||
model.set(rows, in: .vpn)
|
model.set(rows, forSection: .vpn)
|
||||||
} else {
|
} else {
|
||||||
model.set([.useProfile], in: .vpn)
|
model.set([.useProfile], forSection: .vpn)
|
||||||
}
|
}
|
||||||
if isProvider {
|
if isProvider {
|
||||||
model.set([.account], in: .authentication)
|
model.set([.account], forSection: .authentication)
|
||||||
model.set([.providerPool, .endpoint, .providerPreset, .networkSettings], in: .configuration)
|
model.set([.providerPool, .endpoint, .providerPreset, .networkSettings], forSection: .configuration)
|
||||||
model.set([.providerRefresh], in: .providerInfrastructure)
|
model.set([.providerRefresh], forSection: .providerInfrastructure)
|
||||||
} else {
|
} else {
|
||||||
model.set([.account, .endpoint, .hostParameters, .networkSettings], in: .configuration)
|
model.set([.account, .endpoint, .hostParameters, .networkSettings], forSection: .configuration)
|
||||||
}
|
}
|
||||||
if isActiveProfile {
|
if isActiveProfile {
|
||||||
if isProvider {
|
if isProvider {
|
||||||
model.set([.vpnResolvesHostname], in: .vpnResolvesHostname)
|
model.set([.vpnResolvesHostname], forSection: .vpnResolvesHostname)
|
||||||
}
|
}
|
||||||
model.set([.vpnSurvivesSleep], in: .vpnSurvivesSleep)
|
model.set([.vpnSurvivesSleep], forSection: .vpnSurvivesSleep)
|
||||||
model.set([.trustedPolicy], in: .trustedPolicy)
|
model.set([.trustedPolicy], forSection: .trustedPolicy)
|
||||||
model.set([.dataCount, .debugLog, .masksPrivateData], in: .diagnostics)
|
model.set([.dataCount, .debugLog, .masksPrivateData], forSection: .diagnostics)
|
||||||
model.set([.reportIssue], in: .feedback)
|
model.set([.reportIssue], forSection: .feedback)
|
||||||
}
|
}
|
||||||
|
|
||||||
trustedNetworks.delegate = self
|
trustedNetworks.delegate = self
|
||||||
trustedNetworks.load(from: service.preferences)
|
trustedNetworks.load(from: service.preferences)
|
||||||
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, in: .trusted)
|
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, forSection: .trusted)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadVpnStatus() {
|
private func reloadVpnStatus() {
|
||||||
|
@ -1170,7 +1176,7 @@ extension ServiceViewController: TrustedNetworksModelDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func trustedNetworks(_: TrustedNetworksModel, shouldInsertWifiAt rowIndex: Int) {
|
func trustedNetworks(_: TrustedNetworksModel, shouldInsertWifiAt rowIndex: Int) {
|
||||||
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, in: .trusted)
|
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, forSection: .trusted)
|
||||||
tableView.insertRows(at: [IndexPath(row: rowIndex, section: trustedSectionIndex)], with: .bottom)
|
tableView.insertRows(at: [IndexPath(row: rowIndex, section: trustedSectionIndex)], with: .bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1186,7 +1192,7 @@ extension ServiceViewController: TrustedNetworksModelDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func trustedNetworks(_: TrustedNetworksModel, shouldDeleteWifiAt rowIndex: Int) {
|
func trustedNetworks(_: TrustedNetworksModel, shouldDeleteWifiAt rowIndex: Int) {
|
||||||
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, in: .trusted)
|
model.set(trustedNetworks.rows.map { mappedTrustedNetworksRow($0) }, forSection: .trusted)
|
||||||
tableView.deleteRows(at: [IndexPath(row: rowIndex, section: trustedSectionIndex)], with: .top)
|
tableView.deleteRows(at: [IndexPath(row: rowIndex, section: trustedSectionIndex)], with: .top)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,24 +26,25 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Intents
|
import Intents
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
@available(iOS 12, *)
|
@available(iOS 12, *)
|
||||||
class ShortcutsAddViewController: UITableViewController, TableModelHost {
|
class ShortcutsAddViewController: UITableViewController, StrongTableHost {
|
||||||
weak var delegate: ShortcutsIntentDelegate?
|
weak var delegate: ShortcutsIntentDelegate?
|
||||||
|
|
||||||
// MARK: TableModel
|
// MARK: StrongTableModel
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = {
|
let model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.add(.vpn)
|
model.add(.vpn)
|
||||||
model.add(.wifi)
|
model.add(.wifi)
|
||||||
model.add(.cellular)
|
model.add(.cellular)
|
||||||
model.set([.connect, .enableVPN, .disableVPN], in: .vpn)
|
model.set([.connect, .enableVPN, .disableVPN], forSection: .vpn)
|
||||||
model.set([.trustCurrentWiFi, .untrustCurrentWiFi], in: .wifi)
|
model.set([.trustCurrentWiFi, .untrustCurrentWiFi], forSection: .wifi)
|
||||||
model.set([.trustCellular, .untrustCellular], in: .cellular)
|
model.set([.trustCellular, .untrustCellular], forSection: .cellular)
|
||||||
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Vpn.header, for: .vpn)
|
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Vpn.header, forSection: .vpn)
|
||||||
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Wifi.header, for: .wifi)
|
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Wifi.header, forSection: .wifi)
|
||||||
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Cellular.header, for: .cellular)
|
model.setHeader(L10n.Core.Shortcuts.Add.Sections.Cellular.header, forSection: .cellular)
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -85,15 +86,15 @@ class ShortcutsAddViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
@ -158,7 +159,7 @@ class ShortcutsAddViewController: UITableViewController, TableModelHost {
|
||||||
|
|
||||||
private func addConnect() {
|
private func addConnect() {
|
||||||
guard TransientStore.shared.service.hasProfiles() else {
|
guard TransientStore.shared.service.hasProfiles() else {
|
||||||
let alert = Macros.alert(
|
let alert = UIAlertController.asAlert(
|
||||||
L10n.Core.Shortcuts.Add.Cells.Connect.caption,
|
L10n.Core.Shortcuts.Add.Cells.Connect.caption,
|
||||||
L10n.Core.Shortcuts.Add.Alerts.NoProfiles.message
|
L10n.Core.Shortcuts.Add.Alerts.NoProfiles.message
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,9 +27,10 @@ import UIKit
|
||||||
import Intents
|
import Intents
|
||||||
import IntentsUI
|
import IntentsUI
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
@available(iOS 12, *)
|
@available(iOS 12, *)
|
||||||
class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, TableModelHost {
|
class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, StrongTableHost {
|
||||||
private let service = TransientStore.shared.service
|
private let service = TransientStore.shared.service
|
||||||
|
|
||||||
private var providers: [String] = []
|
private var providers: [String] = []
|
||||||
|
@ -40,12 +41,12 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
|
||||||
|
|
||||||
weak var delegate: ShortcutsIntentDelegate?
|
weak var delegate: ShortcutsIntentDelegate?
|
||||||
|
|
||||||
// MARK: TableModelHost
|
// MARK: StrongTableHost
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = {
|
let model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Providers.header, for: .providers)
|
model.setHeader(L10n.Core.Organizer.Sections.Providers.header, forSection: .providers)
|
||||||
model.setHeader(L10n.Core.Organizer.Sections.Hosts.header, for: .hosts)
|
model.setHeader(L10n.Core.Organizer.Sections.Hosts.header, forSection: .hosts)
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -55,11 +56,11 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
|
||||||
|
|
||||||
if !providers.isEmpty {
|
if !providers.isEmpty {
|
||||||
model.add(.providers)
|
model.add(.providers)
|
||||||
model.set(.providerShortcut, count: providers.count, in: .providers)
|
model.set(.providerShortcut, count: providers.count, forSection: .providers)
|
||||||
}
|
}
|
||||||
if !hosts.isEmpty {
|
if !hosts.isEmpty {
|
||||||
model.add(.hosts)
|
model.add(.hosts)
|
||||||
model.set(.hostShortcut, count: hosts.count, in: .hosts)
|
model.set(.hostShortcut, count: hosts.count, forSection: .hosts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,15 +109,15 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import UIKit
|
||||||
import Intents
|
import Intents
|
||||||
import IntentsUI
|
import IntentsUI
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
import Convenience
|
||||||
|
|
||||||
@available(iOS 12, *)
|
@available(iOS 12, *)
|
||||||
protocol ShortcutsIntentDelegate: class {
|
protocol ShortcutsIntentDelegate: class {
|
||||||
|
@ -63,27 +64,27 @@ private struct ShortcutWrapper: Comparable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 12, *)
|
@available(iOS 12, *)
|
||||||
class ShortcutsViewController: UITableViewController, INUIAddVoiceShortcutViewControllerDelegate, INUIEditVoiceShortcutViewControllerDelegate, ShortcutsIntentDelegate, TableModelHost {
|
class ShortcutsViewController: UITableViewController, INUIAddVoiceShortcutViewControllerDelegate, INUIEditVoiceShortcutViewControllerDelegate, ShortcutsIntentDelegate, StrongTableHost {
|
||||||
private var wrappers: [ShortcutWrapper]?
|
private var wrappers: [ShortcutWrapper]?
|
||||||
|
|
||||||
private var pendingShortcut: INShortcut?
|
private var pendingShortcut: INShortcut?
|
||||||
|
|
||||||
private var editedIndexPath: IndexPath?
|
private var editedIndexPath: IndexPath?
|
||||||
|
|
||||||
// MARK: TableModel
|
// MARK: StrongTableModel
|
||||||
|
|
||||||
let model: TableModel<SectionType, RowType> = {
|
let model: StrongTableModel<SectionType, RowType> = {
|
||||||
let model: TableModel<SectionType, RowType> = TableModel()
|
let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
|
||||||
model.add(.all)
|
model.add(.all)
|
||||||
model.setHeader(L10n.Core.Shortcuts.Edit.Sections.All.header, for: .all)
|
model.setHeader(L10n.Core.Shortcuts.Edit.Sections.All.header, forSection: .all)
|
||||||
model.set([], in: .all)
|
model.set([], forSection: .all)
|
||||||
return model
|
return model
|
||||||
}()
|
}()
|
||||||
|
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
var rows = [RowType](repeating: .shortcut, count: wrappers?.count ?? 0)
|
var rows = [RowType](repeating: .shortcut, count: wrappers?.count ?? 0)
|
||||||
rows.append(.addShortcut)
|
rows.append(.addShortcut)
|
||||||
model.set(rows, in: .all)
|
model.set(rows, forSection: .all)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
@ -119,7 +120,7 @@ class ShortcutsViewController: UITableViewController, INUIAddVoiceShortcutViewCo
|
||||||
private func handleShortcutsFetchError(_ error: Error?) {
|
private func handleShortcutsFetchError(_ error: Error?) {
|
||||||
|
|
||||||
// TODO: really show it?
|
// TODO: really show it?
|
||||||
// let alert = Macros.alert(
|
// let alert = UIAlertController.asAlert(
|
||||||
// title,
|
// title,
|
||||||
// L10n.Core.Shortcuts.Edit.message(error?.localizedDescription ?? "")
|
// L10n.Core.Shortcuts.Edit.message(error?.localizedDescription ?? "")
|
||||||
// )
|
// )
|
||||||
|
@ -167,15 +168,15 @@ class ShortcutsViewController: UITableViewController, INUIAddVoiceShortcutViewCo
|
||||||
}
|
}
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return model.count
|
return model.numberOfSections
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
return model.header(for: section)
|
return model.header(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return model.count(for: section)
|
return model.numberOfRows(forSection: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
//
|
|
||||||
// TableModel.swift
|
|
||||||
// Passepartout-iOS
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 6/25/18.
|
|
||||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
protocol TableModelHost {
|
|
||||||
associatedtype S: Hashable
|
|
||||||
|
|
||||||
associatedtype R: Equatable
|
|
||||||
|
|
||||||
var model: TableModel<S, R> { get }
|
|
||||||
|
|
||||||
func reloadModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
class TableModel<S: Hashable, R: Equatable> {
|
|
||||||
private var sections: [S]
|
|
||||||
|
|
||||||
private var headerBySection: [S: String]
|
|
||||||
|
|
||||||
private var footerBySection: [S: String]
|
|
||||||
|
|
||||||
private var rowsBySection: [S: [R]]
|
|
||||||
|
|
||||||
init() {
|
|
||||||
sections = []
|
|
||||||
headerBySection = [:]
|
|
||||||
footerBySection = [:]
|
|
||||||
rowsBySection = [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func clear() {
|
|
||||||
sections = []
|
|
||||||
headerBySection = [:]
|
|
||||||
footerBySection = [:]
|
|
||||||
rowsBySection = [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Access
|
|
||||||
|
|
||||||
var count: Int {
|
|
||||||
return sections.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func section(for sectionIndex: Int) -> S {
|
|
||||||
return sections[sectionIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(ofSection sectionObject: S) -> Int {
|
|
||||||
guard let sectionIndex = sections.firstIndex(of: sectionObject) else {
|
|
||||||
fatalError("Missing section: \(sectionObject)")
|
|
||||||
}
|
|
||||||
return sectionIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
func rows(for sectionIndex: Int) -> [R] {
|
|
||||||
let sectionObject = sections[sectionIndex]
|
|
||||||
guard let rows = rowsBySection[sectionObject] else {
|
|
||||||
fatalError("Missing section: \(sectionObject)")
|
|
||||||
}
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
func row(at indexPath: IndexPath) -> R {
|
|
||||||
return rows(for: indexPath.section)[indexPath.row]
|
|
||||||
}
|
|
||||||
|
|
||||||
func count(for sectionIndex: Int) -> Int {
|
|
||||||
return rows(for: sectionIndex).count
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexPath(row rowObject: R, section sectionObject: S) -> IndexPath? {
|
|
||||||
guard let sectionIndex = sections.firstIndex(of: sectionObject) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let row = rowsBySection[sectionObject]?.firstIndex(of: rowObject) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return IndexPath(row: row, section: sectionIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func header(for sectionIndex: Int) -> String? {
|
|
||||||
let sectionObject = sections[sectionIndex]
|
|
||||||
return headerBySection[sectionObject]
|
|
||||||
}
|
|
||||||
|
|
||||||
func header(for sectionObject: S) -> String? {
|
|
||||||
return headerBySection[sectionObject]
|
|
||||||
}
|
|
||||||
|
|
||||||
func footer(for sectionIndex: Int) -> String? {
|
|
||||||
let sectionObject = sections[sectionIndex]
|
|
||||||
return footerBySection[sectionObject]
|
|
||||||
}
|
|
||||||
|
|
||||||
func footer(for sectionObject: S) -> String? {
|
|
||||||
return footerBySection[sectionObject]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Modification
|
|
||||||
|
|
||||||
func add(_ section: S) {
|
|
||||||
sections.append(section)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setHeader(_ header: String, for sectionObject: S) {
|
|
||||||
headerBySection[sectionObject] = header
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeHeader(for sectionObject: S) {
|
|
||||||
headerBySection.removeValue(forKey: sectionObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFooter(_ footer: String, for sectionObject: S) {
|
|
||||||
footerBySection[sectionObject] = footer
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeFooter(for sectionObject: S) {
|
|
||||||
footerBySection.removeValue(forKey: sectionObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(_ rows: [R], in sectionObject: S) {
|
|
||||||
rowsBySection[sectionObject] = rows
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(_ row: R, count: Int, in sectionObject: S) {
|
|
||||||
rowsBySection[sectionObject] = [R](repeating: row, count: count)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteRow(at indexPath: IndexPath) {
|
|
||||||
deleteRow(in: section(for: indexPath.section), at: indexPath.row)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteRow(in sectionObject: S, at rowIndex: Int) {
|
|
||||||
rowsBySection[sectionObject]?.remove(at: rowIndex)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 50;
|
objectVersion = 51;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */; };
|
0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */; };
|
||||||
0E1D72B4213C118500BA1586 /* ConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */; };
|
0E1D72B4213C118500BA1586 /* ConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */; };
|
||||||
0E24273A225950450064A1A3 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E24273C225950450064A1A3 /* About.storyboard */; };
|
0E24273A225950450064A1A3 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E24273C225950450064A1A3 /* About.storyboard */; };
|
||||||
0E242740225951B00064A1A3 /* InApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E24273F225951B00064A1A3 /* InApp.swift */; };
|
0E242740225951B00064A1A3 /* ProductManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E24273F225951B00064A1A3 /* ProductManager.swift */; };
|
||||||
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E242741225956AC0064A1A3 /* DonationViewController.swift */; };
|
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E242741225956AC0064A1A3 /* DonationViewController.swift */; };
|
||||||
0E2AC24522EC3AC10037B4B0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */; };
|
0E2AC24522EC3AC10037B4B0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */; };
|
||||||
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B493F20FCFF990094784C /* Theme+Titles.swift */; };
|
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B493F20FCFF990094784C /* Theme+Titles.swift */; };
|
||||||
|
@ -27,7 +27,6 @@
|
||||||
0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCF2214DA9310035E9DE /* AppConstants.swift */; };
|
0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCF2214DA9310035E9DE /* AppConstants.swift */; };
|
||||||
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */; };
|
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */; };
|
||||||
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DED20C93E4C004C739C /* GroupConstants.swift */; };
|
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DED20C93E4C004C739C /* GroupConstants.swift */; };
|
||||||
0E3152BE223FA03D00F61841 /* Reviewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E78179E21BE852200950C58 /* Reviewer.swift */; };
|
|
||||||
0E3152C0223FA03D00F61841 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
0E3152C0223FA03D00F61841 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
||||||
0E3152C1223FA04800F61841 /* GracefulVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */; };
|
0E3152C1223FA04800F61841 /* GracefulVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */; };
|
||||||
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7E020D3E4C5002221FF /* MockVPNProvider.swift */; };
|
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7E020D3E4C5002221FF /* MockVPNProvider.swift */; };
|
||||||
|
@ -57,6 +56,7 @@
|
||||||
0E3152DA223FA05800F61841 /* PlaceholderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */; };
|
0E3152DA223FA05800F61841 /* PlaceholderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */; };
|
||||||
0E3152DB223FA05800F61841 /* ProfileKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D14021919F5600BB5FB2 /* ProfileKey.swift */; };
|
0E3152DB223FA05800F61841 /* ProfileKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D14021919F5600BB5FB2 /* ProfileKey.swift */; };
|
||||||
0E3152DC223FA05800F61841 /* ProviderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */; };
|
0E3152DC223FA05800F61841 /* ProviderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */; };
|
||||||
|
0E3419AD2350815E00419E18 /* Donation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3419AC2350815E00419E18 /* Donation.swift */; };
|
||||||
0E3586FE225BD34800509A4D /* ActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */; };
|
0E3586FE225BD34800509A4D /* ActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */; };
|
||||||
0E36D24D2240234B006AF062 /* ShortcutsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */; };
|
0E36D24D2240234B006AF062 /* ShortcutsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */; };
|
||||||
0E36D25822403469006AF062 /* Shortcuts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E36D25A22403469006AF062 /* Shortcuts.storyboard */; };
|
0E36D25822403469006AF062 /* Shortcuts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E36D25A22403469006AF062 /* Shortcuts.storyboard */; };
|
||||||
|
@ -90,25 +90,20 @@
|
||||||
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; };
|
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; };
|
||||||
0ECC60DE2256B68A0020BEAC /* SwiftGen+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */; };
|
0ECC60DE2256B68A0020BEAC /* SwiftGen+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */; };
|
||||||
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB109224FECEA00E9E551 /* DataUnit.swift */; };
|
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB109224FECEA00E9E551 /* DataUnit.swift */; };
|
||||||
0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44D20E1122200A6BB43 /* TableModel.swift */; };
|
|
||||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; };
|
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; };
|
||||||
0ECF12D7230612F5008E4924 /* InAppHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF12D6230612F5008E4924 /* InAppHelper.swift */; };
|
|
||||||
0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2820CF2A340027975F /* AccountViewController.swift */; };
|
0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2820CF2A340027975F /* AccountViewController.swift */; };
|
||||||
0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2B20CF2D6F0027975F /* ProviderPoolViewController.swift */; };
|
0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C2B20CF2D6F0027975F /* ProviderPoolViewController.swift */; };
|
||||||
0ED31C3A20CF39510027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
0ED31C3A20CF39510027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
||||||
0ED31C3D20CF396E0027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
0ED31C3D20CF396E0027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
||||||
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AD8213F33150004D387 /* WizardHostViewController.swift */; };
|
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AD8213F33150004D387 /* WizardHostViewController.swift */; };
|
||||||
0ED38ADA213F44D00004D387 /* Organizer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED38ADC213F44D00004D387 /* Organizer.storyboard */; };
|
0ED38ADA213F44D00004D387 /* Organizer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED38ADC213F44D00004D387 /* Organizer.storyboard */; };
|
||||||
0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE9214054A50004D387 /* OptionViewController.swift */; };
|
|
||||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */; };
|
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */; };
|
||||||
0ED993B1223FF8C700B0F9C9 /* IntentDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED993B0223FF8C700B0F9C9 /* IntentDispatcher.swift */; };
|
0ED993B1223FF8C700B0F9C9 /* IntentDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED993B0223FF8C700B0F9C9 /* IntentDispatcher.swift */; };
|
||||||
0EDE8DC420C86910004C739C /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DC320C86910004C739C /* PacketTunnelProvider.swift */; };
|
0EDE8DC420C86910004C739C /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DC320C86910004C739C /* PacketTunnelProvider.swift */; };
|
||||||
0EDE8DC820C86910004C739C /* Passepartout-Tunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
0EDE8DC820C86910004C739C /* Passepartout-Tunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */; };
|
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */; };
|
||||||
0EEB53B2225D525B00746300 /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EEB53B1225D525B00746300 /* Downloader.swift */; };
|
|
||||||
0EEF23412321AC55000AEBE3 /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EEF23402321AC55000AEBE3 /* Issue.swift */; };
|
0EEF23412321AC55000AEBE3 /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EEF23402321AC55000AEBE3 /* Issue.swift */; };
|
||||||
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; };
|
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; };
|
||||||
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5CF242141CE58004FF1BD /* HUD.swift */; };
|
|
||||||
0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
||||||
0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */; };
|
0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */; };
|
||||||
0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */; };
|
0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */; };
|
||||||
|
@ -172,7 +167,7 @@
|
||||||
0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationViewController.swift; sourceTree = "<group>"; };
|
0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationViewController.swift; sourceTree = "<group>"; };
|
||||||
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||||
0E24273B225950450064A1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = "<group>"; };
|
0E24273B225950450064A1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = "<group>"; };
|
||||||
0E24273F225951B00064A1A3 /* InApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InApp.swift; sourceTree = "<group>"; };
|
0E24273F225951B00064A1A3 /* ProductManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductManager.swift; sourceTree = "<group>"; };
|
||||||
0E242741225956AC0064A1A3 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = "<group>"; };
|
0E242741225956AC0064A1A3 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = "<group>"; };
|
||||||
0E2AC24422EC3AC10037B4B0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
0E2AC24422EC3AC10037B4B0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||||
0E2B493F20FCFF990094784C /* Theme+Titles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Titles.swift"; sourceTree = "<group>"; };
|
0E2B493F20FCFF990094784C /* Theme+Titles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Titles.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -184,6 +179,7 @@
|
||||||
0E31529B223F9EF400F61841 /* PassepartoutCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PassepartoutCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E31529B223F9EF400F61841 /* PassepartoutCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PassepartoutCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E31529D223F9EF500F61841 /* PassepartoutCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PassepartoutCore.h; sourceTree = "<group>"; };
|
0E31529D223F9EF500F61841 /* PassepartoutCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PassepartoutCore.h; sourceTree = "<group>"; };
|
||||||
0E31529E223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
0E31529E223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
0E3419AC2350815E00419E18 /* Donation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Donation.swift; sourceTree = "<group>"; };
|
||||||
0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTableViewCell.swift; sourceTree = "<group>"; };
|
0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsAddViewController.swift; sourceTree = "<group>"; };
|
0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsAddViewController.swift; sourceTree = "<group>"; };
|
||||||
0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConnectToViewController.swift; sourceTree = "<group>"; };
|
0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConnectToViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -243,7 +239,6 @@
|
||||||
0E77663E229D0DA60023FA76 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
|
0E77663E229D0DA60023FA76 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0E77663F229D0DA70023FA76 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Intents.strings; sourceTree = "<group>"; };
|
0E77663F229D0DA70023FA76 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0E776640229D0DA80023FA76 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
0E776640229D0DA80023FA76 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0E78179E21BE852200950C58 /* Reviewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reviewer.swift; sourceTree = "<group>"; };
|
|
||||||
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderConnectionProfile.swift; sourceTree = "<group>"; };
|
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderConnectionProfile.swift; sourceTree = "<group>"; };
|
||||||
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileKey.swift; sourceTree = "<group>"; };
|
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileKey.swift; sourceTree = "<group>"; };
|
||||||
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
|
@ -270,9 +265,7 @@
|
||||||
0ECEB107224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Shortcuts.storyboard; sourceTree = "<group>"; };
|
0ECEB107224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Shortcuts.storyboard; sourceTree = "<group>"; };
|
||||||
0ECEB108224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
0ECEB108224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
0ECEB109224FECEA00E9E551 /* DataUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataUnit.swift; sourceTree = "<group>"; };
|
0ECEB109224FECEA00E9E551 /* DataUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataUnit.swift; sourceTree = "<group>"; };
|
||||||
0ECEE44D20E1122200A6BB43 /* TableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableModel.swift; sourceTree = "<group>"; };
|
|
||||||
0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Cells.swift"; sourceTree = "<group>"; };
|
0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Cells.swift"; sourceTree = "<group>"; };
|
||||||
0ECF12D6230612F5008E4924 /* InAppHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InAppHelper.swift; path = Submodules/Core/Passepartout/Sources/InAppHelper.swift; sourceTree = SOURCE_ROOT; };
|
|
||||||
0ED31C0F20CF09A30027975F /* Pool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
|
0ED31C0F20CF09A30027975F /* Pool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
|
||||||
0ED31C1120CF0ABA0027975F /* Infrastructure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Infrastructure.swift; sourceTree = "<group>"; };
|
0ED31C1120CF0ABA0027975F /* Infrastructure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Infrastructure.swift; sourceTree = "<group>"; };
|
||||||
0ED31C2820CF2A340027975F /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
0ED31C2820CF2A340027975F /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -282,7 +275,6 @@
|
||||||
0ED31C3B20CF39510027975F /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = "<group>"; };
|
0ED31C3B20CF39510027975F /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = "<group>"; };
|
||||||
0ED38AD8213F33150004D387 /* WizardHostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardHostViewController.swift; sourceTree = "<group>"; };
|
0ED38AD8213F33150004D387 /* WizardHostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardHostViewController.swift; sourceTree = "<group>"; };
|
||||||
0ED38AE621404F100004D387 /* EndpointDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointDataSource.swift; sourceTree = "<group>"; };
|
0ED38AE621404F100004D387 /* EndpointDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointDataSource.swift; sourceTree = "<group>"; };
|
||||||
0ED38AE9214054A50004D387 /* OptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionViewController.swift; sourceTree = "<group>"; };
|
|
||||||
0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationModificationDelegate.swift; sourceTree = "<group>"; };
|
0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationModificationDelegate.swift; sourceTree = "<group>"; };
|
||||||
0ED38AF1214177920004D387 /* VPNProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNProvider.swift; sourceTree = "<group>"; };
|
0ED38AF1214177920004D387 /* VPNProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNProvider.swift; sourceTree = "<group>"; };
|
||||||
0ED824C920D12B8700F2FE9E /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = "<group>"; };
|
0ED824C920D12B8700F2FE9E /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
@ -297,10 +289,8 @@
|
||||||
0EDE8DE620C93945004C739C /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
|
0EDE8DE620C93945004C739C /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
|
||||||
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstants.swift; sourceTree = "<group>"; };
|
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstants.swift; sourceTree = "<group>"; };
|
||||||
0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||||
0EEB53B1225D525B00746300 /* Downloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; };
|
|
||||||
0EEF23402321AC55000AEBE3 /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Issue.swift; path = Submodules/Core/Passepartout/Sources/Issue.swift; sourceTree = SOURCE_ROOT; };
|
0EEF23402321AC55000AEBE3 /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Issue.swift; path = Submodules/Core/Passepartout/Sources/Issue.swift; sourceTree = SOURCE_ROOT; };
|
||||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
||||||
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
|
|
||||||
0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNetworkSettings.swift; sourceTree = "<group>"; };
|
0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNetworkSettings.swift; sourceTree = "<group>"; };
|
||||||
0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsViewController.swift; sourceTree = "<group>"; };
|
0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsViewController.swift; sourceTree = "<group>"; };
|
||||||
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
|
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -452,7 +442,6 @@
|
||||||
children = (
|
children = (
|
||||||
0EDE8DEC20C93E3B004C739C /* Global */,
|
0EDE8DEC20C93E3B004C739C /* Global */,
|
||||||
0E1066CA20E0F85C004F98B7 /* Cells */,
|
0E1066CA20E0F85C004F98B7 /* Cells */,
|
||||||
0ECEE44C20E1120F00A6BB43 /* Tables */,
|
|
||||||
0EDE8DF120C93ED8004C739C /* Scenes */,
|
0EDE8DF120C93ED8004C739C /* Scenes */,
|
||||||
0E23B4A12298559800304C30 /* Config.xcconfig */,
|
0E23B4A12298559800304C30 /* Config.xcconfig */,
|
||||||
0EDE8DE220C86A13004C739C /* Passepartout.entitlements */,
|
0EDE8DE220C86A13004C739C /* Passepartout.entitlements */,
|
||||||
|
@ -494,14 +483,6 @@
|
||||||
path = Profiles;
|
path = Profiles;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
0ECEE44C20E1120F00A6BB43 /* Tables */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
0ECEE44D20E1122200A6BB43 /* TableModel.swift */,
|
|
||||||
);
|
|
||||||
path = Tables;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
0ED31C0E20CF09890027975F /* Model */ = {
|
0ED31C0E20CF09890027975F /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -554,12 +535,10 @@
|
||||||
children = (
|
children = (
|
||||||
0E45E6E222BD793800F19312 /* App.strings */,
|
0E45E6E222BD793800F19312 /* App.strings */,
|
||||||
0EA068F3218475F800C320AD /* ConfigurationParserResult+Alerts.swift */,
|
0EA068F3218475F800C320AD /* ConfigurationParserResult+Alerts.swift */,
|
||||||
0EEB53B1225D525B00746300 /* Downloader.swift */,
|
0E3419AC2350815E00419E18 /* Donation.swift */,
|
||||||
0EF5CF242141CE58004FF1BD /* HUD.swift */,
|
|
||||||
0E24273F225951B00064A1A3 /* InApp.swift */,
|
|
||||||
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
||||||
0E4FD7F020D58618002221FF /* Macros.swift */,
|
0E4FD7F020D58618002221FF /* Macros.swift */,
|
||||||
0ED38AE9214054A50004D387 /* OptionViewController.swift */,
|
0E24273F225951B00064A1A3 /* ProductManager.swift */,
|
||||||
0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */,
|
0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */,
|
||||||
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
||||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
||||||
|
@ -591,9 +570,7 @@
|
||||||
0E39BCF2214DA9310035E9DE /* AppConstants.swift */,
|
0E39BCF2214DA9310035E9DE /* AppConstants.swift */,
|
||||||
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */,
|
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */,
|
||||||
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */,
|
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */,
|
||||||
0ECF12D6230612F5008E4924 /* InAppHelper.swift */,
|
|
||||||
0EEF23402321AC55000AEBE3 /* Issue.swift */,
|
0EEF23402321AC55000AEBE3 /* Issue.swift */,
|
||||||
0E78179E21BE852200950C58 /* Reviewer.swift */,
|
|
||||||
0E4FD7ED20D539A0002221FF /* Utils.swift */,
|
0E4FD7ED20D539A0002221FF /* Utils.swift */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
|
@ -965,10 +942,8 @@
|
||||||
0E3152CC223FA04D00F61841 /* WebServices.swift in Sources */,
|
0E3152CC223FA04D00F61841 /* WebServices.swift in Sources */,
|
||||||
0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */,
|
0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */,
|
||||||
0E3152CA223FA04D00F61841 /* InfrastructurePreset.swift in Sources */,
|
0E3152CA223FA04D00F61841 /* InfrastructurePreset.swift in Sources */,
|
||||||
0ECF12D7230612F5008E4924 /* InAppHelper.swift in Sources */,
|
|
||||||
0E3152CE223FA05400F61841 /* ConnectionService.swift in Sources */,
|
0E3152CE223FA05400F61841 /* ConnectionService.swift in Sources */,
|
||||||
0ED993B1223FF8C700B0F9C9 /* IntentDispatcher.swift in Sources */,
|
0ED993B1223FF8C700B0F9C9 /* IntentDispatcher.swift in Sources */,
|
||||||
0E3152BE223FA03D00F61841 /* Reviewer.swift in Sources */,
|
|
||||||
0EEF23412321AC55000AEBE3 /* Issue.swift in Sources */,
|
0EEF23412321AC55000AEBE3 /* Issue.swift in Sources */,
|
||||||
0E3152C3223FA04800F61841 /* StandardVPNProvider.swift in Sources */,
|
0E3152C3223FA04800F61841 /* StandardVPNProvider.swift in Sources */,
|
||||||
0E3152D1223FA05400F61841 /* Credentials.swift in Sources */,
|
0E3152D1223FA05400F61841 /* Credentials.swift in Sources */,
|
||||||
|
@ -989,12 +964,10 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0ECEE44E20E1122200A6BB43 /* TableModel.swift in Sources */,
|
|
||||||
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */,
|
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */,
|
||||||
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */,
|
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */,
|
||||||
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */,
|
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */,
|
||||||
0E4FD7F120D58618002221FF /* Macros.swift in Sources */,
|
0E4FD7F120D58618002221FF /* Macros.swift in Sources */,
|
||||||
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */,
|
|
||||||
0E45E6E422BD799700F19312 /* SwiftGen+Strings.swift in Sources */,
|
0E45E6E422BD799700F19312 /* SwiftGen+Strings.swift in Sources */,
|
||||||
0E05C5D720D1645F006EE732 /* ToggleTableViewCell.swift in Sources */,
|
0E05C5D720D1645F006EE732 /* ToggleTableViewCell.swift in Sources */,
|
||||||
0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */,
|
0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */,
|
||||||
|
@ -1005,14 +978,14 @@
|
||||||
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */,
|
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */,
|
||||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
||||||
0E776642229D0DAE0023FA76 /* Intents.intentdefinition in Sources */,
|
0E776642229D0DAE0023FA76 /* Intents.intentdefinition in Sources */,
|
||||||
0EEB53B2225D525B00746300 /* Downloader.swift in Sources */,
|
|
||||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
||||||
0E242740225951B00064A1A3 /* InApp.swift in Sources */,
|
0E242740225951B00064A1A3 /* ProductManager.swift in Sources */,
|
||||||
0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */,
|
0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */,
|
||||||
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */,
|
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */,
|
||||||
0E3DA371215CB5BF00B40FC9 /* VersionViewController.swift in Sources */,
|
0E3DA371215CB5BF00B40FC9 /* VersionViewController.swift in Sources */,
|
||||||
0E05C5D620D1645F006EE732 /* SwiftGen+Scenes.swift in Sources */,
|
0E05C5D620D1645F006EE732 /* SwiftGen+Scenes.swift in Sources */,
|
||||||
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */,
|
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */,
|
||||||
|
0E3419AD2350815E00419E18 /* Donation.swift in Sources */,
|
||||||
0EFD9440215BED8E00529B64 /* LabelViewController.swift in Sources */,
|
0EFD9440215BED8E00529B64 /* LabelViewController.swift in Sources */,
|
||||||
0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */,
|
0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */,
|
||||||
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */,
|
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */,
|
||||||
|
@ -1020,7 +993,6 @@
|
||||||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */,
|
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */,
|
||||||
0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */,
|
0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */,
|
||||||
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */,
|
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */,
|
||||||
0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */,
|
|
||||||
0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */,
|
0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */,
|
||||||
0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */,
|
0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */,
|
||||||
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */,
|
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue