Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f194fb9cca | |||
| 36a4f93127 | |||
| aae5adef38 | |||
| 51754f737c | |||
| 85ccd835ab | |||
| 8e302bfdde | |||
| b609331b8e | |||
| 59845174e7 | |||
| 0bdb6a626d | |||
| 7f0256cc33 |
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm
|
||||||
|
NymVPM.xcworkspace/
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# List of rules with definitions
|
||||||
|
# https://github.com/realm/SwiftLint/blob/master/Rules.md
|
||||||
|
|
||||||
|
disabled_rules:
|
||||||
|
- todo
|
||||||
|
|
||||||
|
opt_in_rules:
|
||||||
|
- anyobject_protocol
|
||||||
|
- attributes
|
||||||
|
- balanced_xctest_lifecycle
|
||||||
|
- capture_variable
|
||||||
|
- closure_body_length
|
||||||
|
- closure_end_indentation
|
||||||
|
- closure_parameter_position
|
||||||
|
- closure_spacing
|
||||||
|
- collection_alignment
|
||||||
|
- colon
|
||||||
|
- conditional_returns_on_newline
|
||||||
|
- contains_over_filter_count
|
||||||
|
- contains_over_filter_is_empty
|
||||||
|
- contains_over_first_not_nil
|
||||||
|
- contains_over_range_nil_comparison
|
||||||
|
- discouraged_none_name
|
||||||
|
- discouraged_object_literal
|
||||||
|
- empty_collection_literal
|
||||||
|
- empty_count
|
||||||
|
- empty_string
|
||||||
|
- empty_xctest_method
|
||||||
|
- enum_case_associated_values_count
|
||||||
|
- expiring_todo
|
||||||
|
- explicit_init
|
||||||
|
- fallthrough
|
||||||
|
- fatal_error_message
|
||||||
|
- file_header
|
||||||
|
- file_name
|
||||||
|
- file_name_no_space
|
||||||
|
- first_where
|
||||||
|
- flatmap_over_map_reduce
|
||||||
|
- force_cast
|
||||||
|
- force_try
|
||||||
|
- force_unwrapping
|
||||||
|
- function_default_parameter_at_end
|
||||||
|
- identical_operands
|
||||||
|
- implicit_return
|
||||||
|
- indentation_width
|
||||||
|
- joined_default_parameter
|
||||||
|
- last_where
|
||||||
|
- legacy_random
|
||||||
|
- line_length
|
||||||
|
- literal_expression_end_indentation
|
||||||
|
- lower_acl_than_parent
|
||||||
|
- multiline_arguments
|
||||||
|
- multiline_arguments_brackets
|
||||||
|
- multiline_function_chains
|
||||||
|
- multiline_literal_brackets
|
||||||
|
- multiline_parameters
|
||||||
|
- multiline_parameters_brackets
|
||||||
|
- operator_usage_whitespace
|
||||||
|
- overridden_super_call
|
||||||
|
- pattern_matching_keywords
|
||||||
|
- prefer_self_type_over_type_of_self
|
||||||
|
- prefer_zero_over_explicit_init
|
||||||
|
- private_action
|
||||||
|
- private_outlet
|
||||||
|
- prohibited_interface_builder
|
||||||
|
- prohibited_super_call
|
||||||
|
- redundant_nil_coalescing
|
||||||
|
- redundant_type_annotation
|
||||||
|
- sorted_first_last
|
||||||
|
- switch_case_on_newline
|
||||||
|
- test_case_accessibility
|
||||||
|
- toggle_bool
|
||||||
|
- trailing_closure
|
||||||
|
- unavailable_function
|
||||||
|
- unneeded_parentheses_in_closure_argument
|
||||||
|
- unused_declaration
|
||||||
|
- unused_import
|
||||||
|
- vertical_parameter_alignment_on_call
|
||||||
|
- vertical_whitespace_closing_braces
|
||||||
|
- weak_delegate
|
||||||
|
- xct_specific_matcher
|
||||||
|
- yoda_condition
|
||||||
|
|
||||||
|
attributes:
|
||||||
|
always_on_same_line:
|
||||||
|
- "@IBSegueAction"
|
||||||
|
- "@IBAction"
|
||||||
|
- "@NSManaged"
|
||||||
|
- "@objc"
|
||||||
|
- "@MainActor"
|
||||||
|
- "@discardableResult"
|
||||||
|
|
||||||
|
conditional_returns_on_newline:
|
||||||
|
if_only: true
|
||||||
|
|
||||||
|
cyclomatic_complexity:
|
||||||
|
ignores_case_statements: true
|
||||||
|
|
||||||
|
enum_case_associated_values_count:
|
||||||
|
warning: 4
|
||||||
|
error: 6
|
||||||
|
|
||||||
|
function_parameter_count:
|
||||||
|
warning: 6
|
||||||
|
error: 8
|
||||||
|
|
||||||
|
identifier_name:
|
||||||
|
min_length: 2
|
||||||
|
max_length: 60
|
||||||
|
|
||||||
|
indentation_width:
|
||||||
|
indentation_width: 4
|
||||||
|
|
||||||
|
legacy_hashing: error
|
||||||
|
|
||||||
|
line_length:
|
||||||
|
ignores_urls: true
|
||||||
|
ignores_comments: true
|
||||||
|
ignores_function_declarations: false
|
||||||
|
ignores_interpolated_strings: true
|
||||||
|
warning: 120
|
||||||
|
error: 240
|
||||||
|
|
||||||
|
multiline_parameters:
|
||||||
|
allowsSingleLine: false
|
||||||
|
|
||||||
|
nesting:
|
||||||
|
type_level: 3
|
||||||
|
|
||||||
|
private_outlet:
|
||||||
|
allow_private_set: true
|
||||||
|
|
||||||
|
trailing_closure:
|
||||||
|
only_single_muted_parameter: true
|
||||||
|
|
||||||
|
trailing_whitespace:
|
||||||
|
ignores_comments: true
|
||||||
|
|
||||||
|
type_body_length:
|
||||||
|
warning: 400
|
||||||
|
error: 500
|
||||||
|
|
||||||
|
type_name:
|
||||||
|
min_length: 2
|
||||||
|
max_length: 60
|
||||||
|
|
||||||
|
warning_threshold: 15
|
||||||
|
|
||||||
|
reporter: "xcode"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Home",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v16)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Home",
|
||||||
|
targets: ["Home"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../UIComponents"),
|
||||||
|
.package(path: "../Settings")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Home",
|
||||||
|
dependencies: [
|
||||||
|
"UIComponents",
|
||||||
|
"Settings"
|
||||||
|
],
|
||||||
|
path: "Sources"
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "HomeTests",
|
||||||
|
dependencies: ["Home"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Settings
|
||||||
|
|
||||||
|
struct HomeFlowCoordinator<Content: View>: View {
|
||||||
|
@ObservedObject var state: HomeFlowState
|
||||||
|
let content: () -> Content
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack(path: $state.path) {
|
||||||
|
ZStack {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
.navigationDestination(for: HomeLink.self, destination: linkDestination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension HomeFlowCoordinator {
|
||||||
|
@ViewBuilder private func linkDestination(link: HomeLink) -> some View {
|
||||||
|
switch link {
|
||||||
|
case .firstHop(text: _):
|
||||||
|
HopListView(viewModel: HopListViewModel(path: $state.path, type: .first))
|
||||||
|
case .lastHop:
|
||||||
|
HopListView(viewModel: HopListViewModel(path: $state.path, type: .last))
|
||||||
|
case .settings:
|
||||||
|
SettingsView(viewModel: SettingsViewModel(path: $state.path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public class HomeFlowState: ObservableObject {
|
||||||
|
@Published var path = NavigationPath()
|
||||||
|
@Published var presentedItem: HomeLink?
|
||||||
|
@Published var coverItem: HomeLink?
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum HomeLink: Hashable, Identifiable {
|
||||||
|
case firstHop(text: String?)
|
||||||
|
case lastHop
|
||||||
|
case settings
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
String(describing: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
import Modifiers
|
||||||
|
import Theme
|
||||||
|
import UIComponents
|
||||||
|
|
||||||
|
public struct HomeView: View {
|
||||||
|
@ObservedObject private var viewModel: HomeViewModel
|
||||||
|
|
||||||
|
public init(viewModel: HomeViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
HomeFlowCoordinator(state: viewModel, content: content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension HomeView {
|
||||||
|
@ViewBuilder
|
||||||
|
func content() -> some View {
|
||||||
|
VStack {
|
||||||
|
navbar()
|
||||||
|
Spacer()
|
||||||
|
statusAreaSection()
|
||||||
|
Spacer()
|
||||||
|
networkSection()
|
||||||
|
connectionSection()
|
||||||
|
connectButton()
|
||||||
|
}
|
||||||
|
.appearanceUpdate()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background {
|
||||||
|
NymColor.background
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func navbar() -> some View {
|
||||||
|
CustomNavBar(
|
||||||
|
title: "NymVPN".localizedString,
|
||||||
|
rightButton: CustomNavBarButton(type: .settings, action: { viewModel.navigateToSettings() })
|
||||||
|
)
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func statusAreaSection() -> some View {
|
||||||
|
StatusButton(config: .disconnected)
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 8)
|
||||||
|
|
||||||
|
StatusInfoView()
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func networkSection() -> some View {
|
||||||
|
HStack {
|
||||||
|
Text("selectNetwork".localizedString)
|
||||||
|
.textStyle(.Title.Medium.primary)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
NetworkButton(viewModel: NetworkButtonViewModel(type: .mixnet, selectedNetwork: $viewModel.selectedNetwork))
|
||||||
|
.padding(EdgeInsets(top: 0, leading: 16, bottom: 16, trailing: 16))
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.selectedNetwork = .mixnet
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkButton(viewModel: NetworkButtonViewModel(type: .wireguard, selectedNetwork: $viewModel.selectedNetwork))
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.selectedNetwork = .wireguard
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func connectionSection() -> some View {
|
||||||
|
HStack {
|
||||||
|
Text("connectTo".localizedString)
|
||||||
|
.foregroundStyle(NymColor.sysOnSurfaceWhite)
|
||||||
|
.textStyle(.Title.Medium.primary)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
HopButton(hopType: .first, country: Country(name: "Germany", code: "de"))
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.navigateToFirstHopSelection()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
HopButton(hopType: .last, country: Country(name: "Switzerland", code: "ch"))
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.navigateToLastHopSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func connectButton() -> some View {
|
||||||
|
ConnectButton()
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import UIComponents
|
||||||
|
import Tunnels
|
||||||
|
|
||||||
|
public class HomeViewModel: HomeFlowState {
|
||||||
|
private let tunnelsManager: TunnelsManager
|
||||||
|
|
||||||
|
@Published var selectedNetwork: NetworkButtonViewModel.ButtonType
|
||||||
|
|
||||||
|
public init(
|
||||||
|
selectedNetwork: NetworkButtonViewModel.ButtonType,
|
||||||
|
tunnelsManager: TunnelsManager = TunnelsManager.shared
|
||||||
|
) {
|
||||||
|
self.selectedNetwork = selectedNetwork
|
||||||
|
self.tunnelsManager = tunnelsManager
|
||||||
|
|
||||||
|
tunnelsManager.loadConfigurations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Navigation -
|
||||||
|
|
||||||
|
public extension HomeViewModel {
|
||||||
|
func navigateToSettings() {
|
||||||
|
path.append(HomeLink.settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigateToFirstHopSelection() {
|
||||||
|
path.append(HomeLink.firstHop(text: ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigateToLastHopSelection() {
|
||||||
|
path.append(HomeLink.lastHop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Tunnel testing -
|
||||||
|
|
||||||
|
public extension HomeViewModel {
|
||||||
|
func connect() {
|
||||||
|
if let tunnel = tunnelsManager.currentTunnel, tunnel.tunnel.connection.status == .connected {
|
||||||
|
tunnelsManager.disconnect()
|
||||||
|
} else {
|
||||||
|
tunnelsManager.test()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Theme
|
||||||
|
import UIComponents
|
||||||
|
|
||||||
|
public struct HopListView: View {
|
||||||
|
private let viewModel: HopListViewModel
|
||||||
|
|
||||||
|
public init(viewModel: HopListViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
navbar()
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
countryButton()
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
countryButton2()
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
searchView()
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 24)
|
||||||
|
|
||||||
|
availableCountryList()
|
||||||
|
}
|
||||||
|
.navigationBarBackButtonHidden(true)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background {
|
||||||
|
NymColor.background
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension HopListView {
|
||||||
|
@ViewBuilder
|
||||||
|
func navbar() -> some View {
|
||||||
|
CustomNavBar(
|
||||||
|
title: viewModel.type.selectHopLocalizedTitle,
|
||||||
|
leftButton: CustomNavBarButton(type: .back, action: { viewModel.navigateHome() })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func countryButton() -> some View {
|
||||||
|
CountryCellButton(
|
||||||
|
viewModel: CountryCellButtonViewModel(
|
||||||
|
type: .fastest(
|
||||||
|
country: Country(name: "Germany", code: "de")
|
||||||
|
),
|
||||||
|
isSelected: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func countryButton2() -> some View {
|
||||||
|
CountryCellButton(
|
||||||
|
viewModel: CountryCellButtonViewModel(
|
||||||
|
type: .country(
|
||||||
|
country: Country(name: "Germany", code: "de")
|
||||||
|
),
|
||||||
|
isSelected: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func searchView() -> some View {
|
||||||
|
SearchView(viewModel: SearchViewModel())
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func availableCountryList() -> some View {
|
||||||
|
ScrollView {
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
switzerland()
|
||||||
|
germany()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func germany() -> some View {
|
||||||
|
CountryCellButton(
|
||||||
|
viewModel: CountryCellButtonViewModel(
|
||||||
|
type: .country(
|
||||||
|
country: Country(name: "Germany", code: "de")
|
||||||
|
),
|
||||||
|
isSelected: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func switzerland() -> some View {
|
||||||
|
CountryCellButton(
|
||||||
|
viewModel: CountryCellButtonViewModel(
|
||||||
|
type: .country(
|
||||||
|
country: Country(name: "Switzerland", code: "ch")
|
||||||
|
),
|
||||||
|
isSelected: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.ignoresSafeArea(.all)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct HopListViewModel {
|
||||||
|
public let type: HopType
|
||||||
|
|
||||||
|
@Binding var path: NavigationPath
|
||||||
|
|
||||||
|
public init(path: Binding<NavigationPath>, type: HopType) {
|
||||||
|
self.type = type
|
||||||
|
_path = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension HopListViewModel {
|
||||||
|
func navigateHome() {
|
||||||
|
path = .init()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum HopType {
|
||||||
|
case first
|
||||||
|
case last
|
||||||
|
|
||||||
|
var selectHopLocalizedTitle: String {
|
||||||
|
switch self {
|
||||||
|
case .first:
|
||||||
|
"firstHopSelection".localizedString
|
||||||
|
case .last:
|
||||||
|
"lastHopSelection".localizedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import Home
|
||||||
|
|
||||||
|
final class HomeTests: XCTestCase {}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.networkextension.packet-tunnel</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.net.nymtech.vpn</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import NetworkExtension
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
NSLog("🏁 Starting tunnel with options: \(options ?? [:])")
|
||||||
|
os_log("🏁 Starting tunnel with options: \(options ?? [:])")
|
||||||
|
|
||||||
|
fetchData()
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchData() {
|
||||||
|
// Do not use the NSTimer here that will not run in background
|
||||||
|
let popTime = DispatchTime.now() + DispatchTimeInterval.seconds(Int(1))
|
||||||
|
DispatchQueue.global(qos: .background).asyncAfter(deadline: popTime) { [weak self] in
|
||||||
|
// Fetch your data from server and generate local notification by using UserNotifications framework
|
||||||
|
self?.doSomeStuff()
|
||||||
|
self?.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func doSomeStuff() {
|
||||||
|
NSLog("🔥 ROKAS TIMER TIC TAK")
|
||||||
|
os_log("🔥 ROKAS TIMER TIC TAK")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
|
NSLog("🛑 Rokas stopping tunnel")
|
||||||
|
completionHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
||||||
|
|
||||||
|
if let handler = completionHandler {
|
||||||
|
handler(messageData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func sleep(completionHandler: @escaping () -> Void) {
|
||||||
|
completionHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func wake() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,622 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 56;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
D9264E262B3DA062004594A8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D9264E252B3DA062004594A8 /* Assets.xcassets */; };
|
||||||
|
D954C0B92B51C1CA00C6BAAC /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D954C0A62B51C17200C6BAAC /* NetworkExtension.framework */; };
|
||||||
|
D954C0BC2B51C1CA00C6BAAC /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D954C0BB2B51C1CA00C6BAAC /* PacketTunnelProvider.swift */; };
|
||||||
|
D954C0C12B51C1CA00C6BAAC /* NymMixnetTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D954C0B82B51C1CA00C6BAAC /* NymMixnetTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
D96740232B56A67A001F6891 /* Tunnels in Frameworks */ = {isa = PBXBuildFile; productRef = D96740222B56A67A001F6891 /* Tunnels */; };
|
||||||
|
D968BDC92B6AEA9D003F42E5 /* AppSettings in Frameworks */ = {isa = PBXBuildFile; productRef = D968BDC82B6AEA9D003F42E5 /* AppSettings */; };
|
||||||
|
D968BDCB2B6BC98E003F42E5 /* AppVersionProvider in Frameworks */ = {isa = PBXBuildFile; productRef = D968BDCA2B6BC98E003F42E5 /* AppVersionProvider */; };
|
||||||
|
D99A147A2B357E9900F2728B /* NymVPNApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D99A14792B357E9900F2728B /* NymVPNApp.swift */; };
|
||||||
|
D99A148A2B35A73F00F2728B /* Home in Frameworks */ = {isa = PBXBuildFile; productRef = D99A14892B35A73F00F2728B /* Home */; };
|
||||||
|
D99A148C2B35A74200F2728B /* Theme in Frameworks */ = {isa = PBXBuildFile; productRef = D99A148B2B35A74200F2728B /* Theme */; };
|
||||||
|
D9CD835D2B6C32D4008DA864 /* Modifiers in Frameworks */ = {isa = PBXBuildFile; productRef = D9CD835C2B6C32D4008DA864 /* Modifiers */; };
|
||||||
|
D9F699E02B4C1862005576A9 /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = D9F699DF2B4C1862005576A9 /* Settings */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
D954C0BF2B51C1CA00C6BAAC /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = D99A146E2B357E9900F2728B /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D954C0B72B51C1CA00C6BAAC;
|
||||||
|
remoteInfo = NymMixnetTunnel;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
D954C0B32B51C17200C6BAAC /* Embed Foundation Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
D954C0C12B51C1CA00C6BAAC /* NymMixnetTunnel.appex in Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed Foundation Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
D9264E252B3DA062004594A8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
D954C0A62B51C17200C6BAAC /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||||
|
D954C0B82B51C1CA00C6BAAC /* NymMixnetTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NymMixnetTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D954C0BB2B51C1CA00C6BAAC /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||||
|
D954C0BD2B51C1CA00C6BAAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D954C0BE2B51C1CA00C6BAAC /* NymMixnetTunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NymMixnetTunnel.entitlements; sourceTree = "<group>"; };
|
||||||
|
D99A14762B357E9900F2728B /* NymVPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NymVPN.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D99A14792B357E9900F2728B /* NymVPNApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NymVPNApp.swift; sourceTree = "<group>"; };
|
||||||
|
D9F699E32B4C47DE005576A9 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
D9F699E62B4C89F1005576A9 /* NymVPN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NymVPN.entitlements; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
D954C0B52B51C1CA00C6BAAC /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D954C0B92B51C1CA00C6BAAC /* NetworkExtension.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D99A14732B357E9900F2728B /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D968BDCB2B6BC98E003F42E5 /* AppVersionProvider in Frameworks */,
|
||||||
|
D96740232B56A67A001F6891 /* Tunnels in Frameworks */,
|
||||||
|
D9F699E02B4C1862005576A9 /* Settings in Frameworks */,
|
||||||
|
D968BDC92B6AEA9D003F42E5 /* AppSettings in Frameworks */,
|
||||||
|
D99A148C2B35A74200F2728B /* Theme in Frameworks */,
|
||||||
|
D99A148A2B35A73F00F2728B /* Home in Frameworks */,
|
||||||
|
D9CD835D2B6C32D4008DA864 /* Modifiers in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
D954C0BA2B51C1CA00C6BAAC /* NymMixnetTunnel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D954C0BB2B51C1CA00C6BAAC /* PacketTunnelProvider.swift */,
|
||||||
|
D954C0BD2B51C1CA00C6BAAC /* Info.plist */,
|
||||||
|
D954C0BE2B51C1CA00C6BAAC /* NymMixnetTunnel.entitlements */,
|
||||||
|
);
|
||||||
|
path = NymMixnetTunnel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D967402C2B56CA1A001F6891 /* Resources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D9F699E32B4C47DE005576A9 /* Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D99A146D2B357E9900F2728B = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D99A14782B357E9900F2728B /* NymVPN */,
|
||||||
|
D954C0BA2B51C1CA00C6BAAC /* NymMixnetTunnel */,
|
||||||
|
D99A14772B357E9900F2728B /* Products */,
|
||||||
|
D99A14882B35A73F00F2728B /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D99A14772B357E9900F2728B /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D99A14762B357E9900F2728B /* NymVPN.app */,
|
||||||
|
D954C0B82B51C1CA00C6BAAC /* NymMixnetTunnel.appex */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D99A14782B357E9900F2728B /* NymVPN */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D9F699E62B4C89F1005576A9 /* NymVPN.entitlements */,
|
||||||
|
D99A14792B357E9900F2728B /* NymVPNApp.swift */,
|
||||||
|
D9264E252B3DA062004594A8 /* Assets.xcassets */,
|
||||||
|
D967402C2B56CA1A001F6891 /* Resources */,
|
||||||
|
);
|
||||||
|
path = NymVPN;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D99A14882B35A73F00F2728B /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D954C0A62B51C17200C6BAAC /* NetworkExtension.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
D954C0B72B51C1CA00C6BAAC /* NymMixnetTunnel */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = D954C0C22B51C1CA00C6BAAC /* Build configuration list for PBXNativeTarget "NymMixnetTunnel" */;
|
||||||
|
buildPhases = (
|
||||||
|
D954C0B42B51C1CA00C6BAAC /* Sources */,
|
||||||
|
D954C0B52B51C1CA00C6BAAC /* Frameworks */,
|
||||||
|
D954C0B62B51C1CA00C6BAAC /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = NymMixnetTunnel;
|
||||||
|
productName = NymMixnetTunnel;
|
||||||
|
productReference = D954C0B82B51C1CA00C6BAAC /* NymMixnetTunnel.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
|
D99A14752B357E9900F2728B /* NymVPN */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = D99A14842B357E9A00F2728B /* Build configuration list for PBXNativeTarget "NymVPN" */;
|
||||||
|
buildPhases = (
|
||||||
|
D99A14722B357E9900F2728B /* Sources */,
|
||||||
|
D99A14732B357E9900F2728B /* Frameworks */,
|
||||||
|
D99A14742B357E9900F2728B /* Resources */,
|
||||||
|
D99A14872B359C8800F2728B /* Run Swiftlint */,
|
||||||
|
D954C0B32B51C17200C6BAAC /* Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
D954C0C02B51C1CA00C6BAAC /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = NymVPN;
|
||||||
|
packageProductDependencies = (
|
||||||
|
D99A14892B35A73F00F2728B /* Home */,
|
||||||
|
D99A148B2B35A74200F2728B /* Theme */,
|
||||||
|
D9F699DF2B4C1862005576A9 /* Settings */,
|
||||||
|
D96740222B56A67A001F6891 /* Tunnels */,
|
||||||
|
D968BDC82B6AEA9D003F42E5 /* AppSettings */,
|
||||||
|
D968BDCA2B6BC98E003F42E5 /* AppVersionProvider */,
|
||||||
|
D9CD835C2B6C32D4008DA864 /* Modifiers */,
|
||||||
|
);
|
||||||
|
productName = NymVPN;
|
||||||
|
productReference = D99A14762B357E9900F2728B /* NymVPN.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
D99A146E2B357E9900F2728B /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1520;
|
||||||
|
LastUpgradeCheck = 1510;
|
||||||
|
TargetAttributes = {
|
||||||
|
D954C0B72B51C1CA00C6BAAC = {
|
||||||
|
CreatedOnToolsVersion = 15.2;
|
||||||
|
};
|
||||||
|
D99A14752B357E9900F2728B = {
|
||||||
|
CreatedOnToolsVersion = 15.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = D99A14712B357E9900F2728B /* Build configuration list for PBXProject "NymVPN" */;
|
||||||
|
compatibilityVersion = "Xcode 14.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = D99A146D2B357E9900F2728B;
|
||||||
|
productRefGroup = D99A14772B357E9900F2728B /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
D99A14752B357E9900F2728B /* NymVPN */,
|
||||||
|
D954C0B72B51C1CA00C6BAAC /* NymMixnetTunnel */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
D954C0B62B51C1CA00C6BAAC /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D99A14742B357E9900F2728B /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D9264E262B3DA062004594A8 /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
D99A14872B359C8800F2728B /* Run Swiftlint */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Swiftlint";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
D954C0B42B51C1CA00C6BAAC /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D954C0BC2B51C1CA00C6BAAC /* PacketTunnelProvider.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D99A14722B357E9900F2728B /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D99A147A2B357E9900F2728B /* NymVPNApp.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
D954C0C02B51C1CA00C6BAAC /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D954C0B72B51C1CA00C6BAAC /* NymMixnetTunnel */;
|
||||||
|
targetProxy = D954C0BF2B51C1CA00C6BAAC /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
D954C0C32B51C1CA00C6BAAC /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = NymMixnetTunnel/NymMixnetTunnel.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = VW5DZLFHM5;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = NymMixnetTunnel/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NymMixnetTunnel;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "net.nymtech.vpn.network-extension";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D954C0C42B51C1CA00C6BAAC /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = NymMixnetTunnel/NymMixnetTunnel.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = VW5DZLFHM5;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = NymMixnetTunnel/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NymMixnetTunnel;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "net.nymtech.vpn.network-extension";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
D99A14822B357E9A00F2728B /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D99A14832B357E9A00F2728B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
D99A14852B357E9A00F2728B /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = NymVPN/NymVPN.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = VW5DZLFHM5;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NymVPN;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = net.nymtech.vpn;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/NymVPN/Resources/Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D99A14862B357E9A00F2728B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = NymVPN/NymVPN.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = VW5DZLFHM5;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = NymVPN;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = net.nymtech.vpn;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/NymVPN/Resources/Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
D954C0C22B51C1CA00C6BAAC /* Build configuration list for PBXNativeTarget "NymMixnetTunnel" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D954C0C32B51C1CA00C6BAAC /* Debug */,
|
||||||
|
D954C0C42B51C1CA00C6BAAC /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
D99A14712B357E9900F2728B /* Build configuration list for PBXProject "NymVPN" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D99A14822B357E9A00F2728B /* Debug */,
|
||||||
|
D99A14832B357E9A00F2728B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
D99A14842B357E9A00F2728B /* Build configuration list for PBXNativeTarget "NymVPN" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D99A14852B357E9A00F2728B /* Debug */,
|
||||||
|
D99A14862B357E9A00F2728B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
D96740222B56A67A001F6891 /* Tunnels */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Tunnels;
|
||||||
|
};
|
||||||
|
D968BDC82B6AEA9D003F42E5 /* AppSettings */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = AppSettings;
|
||||||
|
};
|
||||||
|
D968BDCA2B6BC98E003F42E5 /* AppVersionProvider */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = AppVersionProvider;
|
||||||
|
};
|
||||||
|
D99A14892B35A73F00F2728B /* Home */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Home;
|
||||||
|
};
|
||||||
|
D99A148B2B35A74200F2728B /* Theme */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Theme;
|
||||||
|
};
|
||||||
|
D9CD835C2B6C32D4008DA864 /* Modifiers */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Modifiers;
|
||||||
|
};
|
||||||
|
D9F699DF2B4C1862005576A9 /* Settings */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Settings;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = D99A146E2B357E9900F2728B /* Project object */;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:NymVPN.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Home">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Services">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Settings">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Theme">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:UIComponents">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict/>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ch.svg",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ch" viewBox="0 0 512 512">
|
||||||
|
<g fill-rule="evenodd" stroke-width="1pt">
|
||||||
|
<path fill="red" d="M0 0h512v512H0z"/>
|
||||||
|
<g fill="#fff">
|
||||||
|
<path d="M96 208h320v96H96z"/>
|
||||||
|
<path d="M208 96h96v320h-96z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 288 B |
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "de.svg",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-de" viewBox="0 0 512 512">
|
||||||
|
<path fill="#ffce00" d="M0 341.3h512V512H0z"/>
|
||||||
|
<path fill="#000001" d="M0 0h512v170.7H0z"/>
|
||||||
|
<path fill="red" d="M0 170.7h512v170.6H0z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 232 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Home
|
||||||
|
import Theme
|
||||||
|
import AppSettings
|
||||||
|
import Tunnels
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct NymVPNApp: App {
|
||||||
|
|
||||||
|
init() {
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
NavigationStack {
|
||||||
|
HomeView(viewModel: HomeViewModel(selectedNetwork: .mixnet))
|
||||||
|
}
|
||||||
|
.environmentObject(AppSettings.shared)
|
||||||
|
.environmentObject(TunnelsManager.shared)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension NymVPNApp {
|
||||||
|
func setup() {
|
||||||
|
ThemeConfiguration.setup()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef Bridging_Header_h
|
||||||
|
#define Bridging_Header_h
|
||||||
|
|
||||||
|
//#import "libwg.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Services",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v16)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "AppSettings",
|
||||||
|
targets: ["AppSettings"]
|
||||||
|
),
|
||||||
|
.library(
|
||||||
|
name: "AppVersionProvider",
|
||||||
|
targets: ["AppVersionProvider"]
|
||||||
|
),
|
||||||
|
.library(
|
||||||
|
name: "Modifiers",
|
||||||
|
targets: ["Modifiers"]
|
||||||
|
),
|
||||||
|
.library(
|
||||||
|
name: "Tunnels",
|
||||||
|
targets: ["Tunnels"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "AppSettings",
|
||||||
|
dependencies: [],
|
||||||
|
path: "Sources/Services/AppSettings"
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "AppVersionProvider",
|
||||||
|
dependencies: [],
|
||||||
|
path: "Sources/Services/AppVersionProvider"
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "Modifiers",
|
||||||
|
dependencies: [
|
||||||
|
"AppSettings"
|
||||||
|
],
|
||||||
|
path: "Sources/Services/Modifiers"
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "Tunnels",
|
||||||
|
dependencies: [],
|
||||||
|
path: "Sources/Services/Tunnels"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public final class AppSettings: ObservableObject {
|
||||||
|
public static let shared = AppSettings()
|
||||||
|
|
||||||
|
@AppStorage("currentAppearance") public var currentTheme: AppSetting.Appearance = .automatic
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct AppSetting {
|
||||||
|
public enum Appearance: Int, CaseIterable {
|
||||||
|
case automatic
|
||||||
|
case light
|
||||||
|
case dark
|
||||||
|
|
||||||
|
public var colorScheme: ColorScheme? {
|
||||||
|
switch self {
|
||||||
|
case .light:
|
||||||
|
return .light
|
||||||
|
case .dark:
|
||||||
|
return .dark
|
||||||
|
case .automatic:
|
||||||
|
return ColorScheme(.unspecified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum AppVersionProvider {
|
||||||
|
public static func appVersion(in bundle: Bundle = .main) -> String {
|
||||||
|
guard let version = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||||||
|
else {
|
||||||
|
fatalError("Missing CFBundleShortVersionString")
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
|
||||||
|
public struct AppearanceUpdate: ViewModifier {
|
||||||
|
@EnvironmentObject private var appSettings: AppSettings
|
||||||
|
|
||||||
|
public func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.preferredColorScheme(appSettings.currentTheme.colorScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension View {
|
||||||
|
func appearanceUpdate() -> some View {
|
||||||
|
modifier(AppearanceUpdate())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
public final class Tunnel: ObservableObject {
|
||||||
|
public var tunnel: NETunnelProviderManager
|
||||||
|
@Published public var isEnabled: Bool
|
||||||
|
|
||||||
|
private var observers = [AnyObject]()
|
||||||
|
|
||||||
|
public init(tunnel: NETunnelProviderManager) {
|
||||||
|
self.tunnel = tunnel
|
||||||
|
isEnabled = tunnel.isEnabled
|
||||||
|
|
||||||
|
let configurationChangeNotification = NotificationCenter.default.addObserver(
|
||||||
|
forName: .NEVPNConfigurationChange,
|
||||||
|
object: tunnel,
|
||||||
|
queue: .main
|
||||||
|
) { [weak self] _ in
|
||||||
|
print("Tunnel isEnabled: \(tunnel.isEnabled)")
|
||||||
|
self?.isEnabled = tunnel.isEnabled
|
||||||
|
}
|
||||||
|
observers.append(configurationChangeNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import NetworkExtension
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
public final class TunnelsManager: ObservableObject {
|
||||||
|
public static let shared = TunnelsManager()
|
||||||
|
|
||||||
|
public var currentTunnel: Tunnel?
|
||||||
|
|
||||||
|
private var observers = [AnyObject]()
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func loadConfigurations() {
|
||||||
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
|
print("1. Loading VPN Configurations")
|
||||||
|
if let error {
|
||||||
|
print("Error: \(String(describing: error))")
|
||||||
|
}
|
||||||
|
|
||||||
|
managers?.forEach {
|
||||||
|
print("Found VPN Configuration")
|
||||||
|
print("\($0)")
|
||||||
|
self?.currentTunnel = Tunnel(tunnel: $0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func test() {
|
||||||
|
loadConfigurations()
|
||||||
|
os_log("2 Starting test")
|
||||||
|
if currentTunnel == nil {
|
||||||
|
let manager = createTestManager()
|
||||||
|
manager.saveToPreferences { error in
|
||||||
|
if error == nil {
|
||||||
|
print("3 Added config Successfully")
|
||||||
|
} else {
|
||||||
|
print("3 Failure to add config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("3 Current tunnel already exists")
|
||||||
|
currentTunnel?.tunnel.isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
print("4 Connecting")
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func connect() {
|
||||||
|
do {
|
||||||
|
let options = [
|
||||||
|
NEVPNConnectionStartOptionUsername: "john",
|
||||||
|
NEVPNConnectionStartOptionPassword: "password"
|
||||||
|
] as [String: NSObject]
|
||||||
|
|
||||||
|
try currentTunnel?.tunnel.connection.startVPNTunnel(options: options)
|
||||||
|
} catch {
|
||||||
|
print("FAILED to connect: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func disconnect() {
|
||||||
|
print("5 Disconnecting")
|
||||||
|
// currentTunnel?.tunnel.connection.stopVPNTunnel()
|
||||||
|
|
||||||
|
NEVPNManager.shared().loadFromPreferences { [weak self] error in
|
||||||
|
if let error {
|
||||||
|
print("Error: \(error)")
|
||||||
|
}
|
||||||
|
self?.currentTunnel?.tunnel.connection.stopVPNTunnel()
|
||||||
|
NEVPNManager.shared().connection.stopVPNTunnel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TunnelsManager {
|
||||||
|
func createTestManager() -> NETunnelProviderManager {
|
||||||
|
let manager = NETunnelProviderManager()
|
||||||
|
manager.localizedDescription = "NymVPN Mixnet"
|
||||||
|
|
||||||
|
let tunnelConfiguration = NETunnelProviderProtocol()
|
||||||
|
tunnelConfiguration.providerBundleIdentifier = "net.nymtech.vpn.network-extension"
|
||||||
|
tunnelConfiguration.serverAddress = "127.0.0.1:4009"
|
||||||
|
tunnelConfiguration.providerConfiguration = [:]
|
||||||
|
|
||||||
|
manager.protocolConfiguration = tunnelConfiguration
|
||||||
|
manager.isEnabled = true
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TunnelsManager {
|
||||||
|
func setup() {
|
||||||
|
registerNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerNotifications() {
|
||||||
|
let statusDidChangeNotification = NotificationCenter.default.addObserver(
|
||||||
|
forName: .NEVPNStatusDidChange,
|
||||||
|
object: nil,
|
||||||
|
queue: .main
|
||||||
|
) { status in
|
||||||
|
print("VPN Status: \(status)")
|
||||||
|
}
|
||||||
|
observers.append(statusDidChangeNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import Services
|
||||||
|
|
||||||
|
final class TunnelsTests: XCTestCase {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Settings",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v16)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Settings",
|
||||||
|
targets: ["Settings"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../UIComponents")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Settings",
|
||||||
|
dependencies: [
|
||||||
|
"UIComponents"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "SettingsTests",
|
||||||
|
dependencies: ["Settings"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
|
||||||
|
struct SettingsFlowCoordinator<Content: View>: View {
|
||||||
|
@ObservedObject var state: SettingsFlowState
|
||||||
|
let content: () -> Content
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
content()
|
||||||
|
.navigationDestination(for: SettingsLink.self, destination: linkDestination)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private func linkDestination(link: SettingsLink) -> some View {
|
||||||
|
switch link {
|
||||||
|
case .theme:
|
||||||
|
AppearanceView(viewModel: AppearanceViewModel(path: $state.path, appSettings: AppSettings.shared))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public class SettingsFlowState: ObservableObject {
|
||||||
|
@Published var presentedItem: SettingsLink?
|
||||||
|
|
||||||
|
@Binding var path: NavigationPath
|
||||||
|
|
||||||
|
public init(path: Binding<NavigationPath>) {
|
||||||
|
_path = path
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum SettingsLink: Hashable, Identifiable {
|
||||||
|
case theme
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
String(describing: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
import Modifiers
|
||||||
|
import UIComponents
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
public struct SettingsView: View {
|
||||||
|
@StateObject private var viewModel: SettingsViewModel
|
||||||
|
|
||||||
|
public init(viewModel: SettingsViewModel) {
|
||||||
|
_viewModel = StateObject(wrappedValue: viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
SettingsFlowCoordinator(state: viewModel, content: content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SettingsView {
|
||||||
|
@ViewBuilder
|
||||||
|
func content() -> some View {
|
||||||
|
VStack {
|
||||||
|
navbar()
|
||||||
|
settingsList()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.appearanceUpdate()
|
||||||
|
.navigationBarBackButtonHidden(true)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.ignoresSafeArea(edges: [.bottom])
|
||||||
|
.background {
|
||||||
|
NymColor.background
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func navbar() -> some View {
|
||||||
|
CustomNavBar(
|
||||||
|
title: viewModel.settingsTitle,
|
||||||
|
leftButton: CustomNavBarButton(type: .back, action: { viewModel.navigateHome() })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func settingsList() -> some View {
|
||||||
|
SettingsList(
|
||||||
|
viewModel:
|
||||||
|
SettingsListViewModel(
|
||||||
|
sections: viewModel.sections,
|
||||||
|
appVersion: viewModel.appVersion()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import Foundation
|
||||||
|
import AppVersionProvider
|
||||||
|
import UIComponents
|
||||||
|
|
||||||
|
public class SettingsViewModel: SettingsFlowState {
|
||||||
|
let settingsTitle = "settings".localizedString
|
||||||
|
|
||||||
|
var sections: [SettingsSection] {
|
||||||
|
[
|
||||||
|
connectionSection(),
|
||||||
|
themeSection(),
|
||||||
|
logsSection(),
|
||||||
|
feedbackSection(),
|
||||||
|
legalSection()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigateHome() {
|
||||||
|
path = .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func appVersion() -> String {
|
||||||
|
AppVersionProvider.appVersion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SettingsViewModel {
|
||||||
|
func navigateToTheme() {
|
||||||
|
path.append(SettingsLink.theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SettingsViewModel {
|
||||||
|
func connectionSection() -> SettingsSection {
|
||||||
|
.connection(
|
||||||
|
viewModels: [
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "autoConnectTitle".localizedString,
|
||||||
|
subtitle: "autoConnectSubtitle".localizedString,
|
||||||
|
imageName: "autoConnect",
|
||||||
|
action: {}
|
||||||
|
),
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .toggle,
|
||||||
|
title: "entryLocationTitle".localizedString,
|
||||||
|
subtitle: "entryLocationSubtitle".localizedString,
|
||||||
|
imageName: "entryHop",
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func themeSection() -> SettingsSection {
|
||||||
|
.theme(
|
||||||
|
viewModels: [
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "displayTheme".localizedString,
|
||||||
|
imageName: "displayTheme",
|
||||||
|
action: { [weak self] in
|
||||||
|
self?.navigateToTheme()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logsSection() -> SettingsSection {
|
||||||
|
.logs(
|
||||||
|
viewModels: [
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "logs".localizedString,
|
||||||
|
imageName: "logs",
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedbackSection() -> SettingsSection {
|
||||||
|
.feedback(
|
||||||
|
viewModels: [
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "feedback".localizedString,
|
||||||
|
imageName: "feedback",
|
||||||
|
action: {}
|
||||||
|
),
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "support".localizedString,
|
||||||
|
imageName: "support",
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func legalSection() -> SettingsSection {
|
||||||
|
.legal(
|
||||||
|
viewModels: [
|
||||||
|
SettingsListItemViewModel(
|
||||||
|
accessory: .arrow,
|
||||||
|
title: "legal".localizedString,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
import Modifiers
|
||||||
|
import Theme
|
||||||
|
import UIComponents
|
||||||
|
|
||||||
|
public struct AppearanceView: View {
|
||||||
|
private let viewModel: AppearanceViewModel
|
||||||
|
|
||||||
|
public init(viewModel: AppearanceViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack {
|
||||||
|
navbar()
|
||||||
|
themeOptions()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.appearanceUpdate()
|
||||||
|
.navigationBarBackButtonHidden(true)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.ignoresSafeArea(edges: [.bottom])
|
||||||
|
.background {
|
||||||
|
NymColor.background
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AppearanceView {
|
||||||
|
@ViewBuilder
|
||||||
|
func navbar() -> some View {
|
||||||
|
CustomNavBar(
|
||||||
|
title: viewModel.title,
|
||||||
|
leftButton: CustomNavBarButton(type: .back, action: { viewModel.navigateBack() })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func themeOptions() -> some View {
|
||||||
|
ForEach(viewModel.themes, id: \.self) { theme in
|
||||||
|
SettingButton(
|
||||||
|
viewModel:
|
||||||
|
SettingButtonViewModel(
|
||||||
|
title: viewModel.themeTitle(for: theme),
|
||||||
|
subtitle: viewModel.themeSubtitle(for: theme),
|
||||||
|
isSelected: viewModel.isSelected(for: theme)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
viewModel.setCurrentTheme(with: theme)
|
||||||
|
}
|
||||||
|
.padding(EdgeInsets(top: 24, leading: 16, bottom: 0, trailing: 16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AppSettings
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
public struct AppearanceViewModel {
|
||||||
|
private let appSettings: AppSettings
|
||||||
|
|
||||||
|
let title = "displayTheme".localizedString
|
||||||
|
|
||||||
|
@Binding var path: NavigationPath
|
||||||
|
|
||||||
|
var themes: [AppSetting.Appearance] {
|
||||||
|
AppSetting.Appearance.allCases
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentTheme: AppSetting.Appearance {
|
||||||
|
appSettings.currentTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(path: Binding<NavigationPath>, appSettings: AppSettings) {
|
||||||
|
_path = path
|
||||||
|
self.appSettings = appSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCurrentTheme(with theme: AppSetting.Appearance) {
|
||||||
|
appSettings.currentTheme = theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppearanceViewModel {
|
||||||
|
func themeTitle(for theme: AppSetting.Appearance) -> String {
|
||||||
|
switch theme {
|
||||||
|
case .light:
|
||||||
|
return "lightThemeTitle".localizedString
|
||||||
|
case .dark:
|
||||||
|
return "darkThemeTitle".localizedString
|
||||||
|
case .automatic:
|
||||||
|
return "automaticThemeTitle".localizedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func themeSubtitle(for theme: AppSetting.Appearance) -> String? {
|
||||||
|
switch theme {
|
||||||
|
case .light, .dark:
|
||||||
|
return nil
|
||||||
|
case .automatic:
|
||||||
|
return "automaticThemeSubtitle".localizedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSelected(for theme: AppSetting.Appearance) -> Bool {
|
||||||
|
appSettings.currentTheme == theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppearanceViewModel {
|
||||||
|
func navigateBack() {
|
||||||
|
path.removeLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import Settings
|
||||||
|
|
||||||
|
final class SettingsTests: XCTestCase {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Theme",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v16)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Theme",
|
||||||
|
targets: ["Theme"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Theme",
|
||||||
|
resources: [
|
||||||
|
.copy("Resources/Fonts/Lato-Bold.ttf"),
|
||||||
|
.copy("Resources/Fonts/Lato-Regular.ttf"),
|
||||||
|
.copy("Resources/Fonts/Lato-SemiBold.ttf"),
|
||||||
|
.copy("Resources/Fonts/Lato-Medium.ttf"),
|
||||||
|
.process("Resources/Colors.xcassets"),
|
||||||
|
.process("Resources/Localizable.xcstrings")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "ThemeTests",
|
||||||
|
dependencies: ["Theme"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct NymColor {
|
||||||
|
// MARK: - Navigation bar -
|
||||||
|
public static let navigationBarBackground = Color(.navigationBarBackground)
|
||||||
|
public static let navigationBarSettingsGear = Color(.navigationBarSettingsGear)
|
||||||
|
|
||||||
|
// MARK: - Status button -
|
||||||
|
public static let statusButtonBackground = Color(.statusButtonBackground)
|
||||||
|
public static let sysOnSurfaceWhite = Color(.sysOnSurfaceWhite)
|
||||||
|
public static let statusInfoText = Color(.statusInfoText)
|
||||||
|
public static let statusTimer = Color(.statusTimer)
|
||||||
|
|
||||||
|
public static let countrySelectionSelectedBackground = Color(.countrySelectionSelectedBackground)
|
||||||
|
|
||||||
|
// MARK: - Network button -
|
||||||
|
public static let networkButtonCircle = Color(.networkButtonCircle)
|
||||||
|
|
||||||
|
// MARK: - Connect -
|
||||||
|
public static let connectTitle = Color(.connectTitle)
|
||||||
|
|
||||||
|
// MARK: - Background
|
||||||
|
public static let background = Color(.background)
|
||||||
|
|
||||||
|
// MARK: - Sys -
|
||||||
|
public static let sysOnSecondary = Color(.sysOnSecondary)
|
||||||
|
public static let sysOnSurface = Color(.sysOnSurface)
|
||||||
|
public static let sysOutline = Color(.sysOutline)
|
||||||
|
|
||||||
|
// MARK: - Key -
|
||||||
|
public static let confirm = Color(.confirm)
|
||||||
|
public static let statusGreen = Color(.statusGreen)
|
||||||
|
|
||||||
|
// MARK: - Primary -
|
||||||
|
public static let primaryOrange = Color(.primaryOrange)
|
||||||
|
|
||||||
|
// MARK: - Settings -
|
||||||
|
public static let settingsSeparator = Color(.settingsSeparator)
|
||||||
|
public static let settingsVersion = Color(.settingsVersion)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Bundle {
|
||||||
|
func localizedString(forKey key: String) -> String {
|
||||||
|
localizedString(forKey: key, value: nil, table: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
init(_ name: String) {
|
||||||
|
guard let namedColor = UIColor(named: name, in: Bundle.module, compatibleWith: nil)
|
||||||
|
else {
|
||||||
|
fatalError("Could not load color from Theme module")
|
||||||
|
}
|
||||||
|
self.init(namedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public extension String {
|
||||||
|
var localizedString: String {
|
||||||
|
Bundle.module.localizedString(forKey: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public enum NymFont {
|
||||||
|
case lato(size: CGFloat, weight: LatoWeight)
|
||||||
|
|
||||||
|
public var font: Font {
|
||||||
|
switch self {
|
||||||
|
case let .lato(size, weight):
|
||||||
|
Font.custom("Lato-\(weight.rawValue)", size: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Weights -
|
||||||
|
|
||||||
|
extension NymFont {
|
||||||
|
public enum LatoWeight: String, CaseIterable {
|
||||||
|
case regular = "Regular"
|
||||||
|
case bold = "Bold"
|
||||||
|
case semibold = "SemiBold"
|
||||||
|
case medium = "Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Register fonts -
|
||||||
|
|
||||||
|
extension NymFont {
|
||||||
|
public static func register() {
|
||||||
|
for latoWeight in NymFont.LatoWeight.allCases {
|
||||||
|
let fontName = "Lato-\(latoWeight.rawValue)"
|
||||||
|
guard let fontURL = Bundle.module.url(forResource: fontName, withExtension: "ttf") else { continue }
|
||||||
|
CTFontManagerRegisterFontsForURL(fontURL as CFURL, .process, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct NymTextStyle {
|
||||||
|
let nymFont: NymFont
|
||||||
|
let lineSpacing: CGFloat
|
||||||
|
let kerning: CGFloat
|
||||||
|
|
||||||
|
init(nymFont: NymFont, lineSpacing: CGFloat = 0, kerning: CGFloat = 0) {
|
||||||
|
self.nymFont = nymFont
|
||||||
|
self.lineSpacing = lineSpacing
|
||||||
|
self.kerning = kerning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Styles -
|
||||||
|
extension NymTextStyle {
|
||||||
|
// MARK: - Title -
|
||||||
|
public struct Title {
|
||||||
|
public struct Large {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 22, weight: .regular))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Medium {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 16, weight: .semibold), kerning: 0.15)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Label -
|
||||||
|
public struct Label {
|
||||||
|
public struct Huge {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 18, weight: .bold))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Large {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 14, weight: .bold), kerning: 0.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Small {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 11, weight: .medium), kerning: 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body -
|
||||||
|
public struct Body {
|
||||||
|
public struct Large {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 16, weight: .semibold), kerning: 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Medium {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 14, weight: .regular), kerning: 0.25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Small {
|
||||||
|
public static var primary: NymTextStyle {
|
||||||
|
NymTextStyle(nymFont: .lato(size: 12, weight: .regular), kerning: 0.4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct NymTextStyleModifier: ViewModifier {
|
||||||
|
public let textStyle: NymTextStyle
|
||||||
|
|
||||||
|
public init(textStyle: NymTextStyle) {
|
||||||
|
self.textStyle = textStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
public func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.font(textStyle.nymFont.font)
|
||||||
|
.kerning(textStyle.kerning)
|
||||||
|
.lineSpacing(textStyle.lineSpacing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension View {
|
||||||
|
func textStyle(_ textStyle: NymTextStyle) -> some View {
|
||||||
|
modifier(NymTextStyleModifier(textStyle: textStyle))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "97",
|
||||||
|
"green" : "199",
|
||||||
|
"red" : "43"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.100",
|
||||||
|
"blue" : "93",
|
||||||
|
"green" : "196",
|
||||||
|
"red" : "71"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.080",
|
||||||
|
"blue" : "0x71",
|
||||||
|
"green" : "0x5B",
|
||||||
|
"red" : "0x61"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.160",
|
||||||
|
"blue" : "0x33",
|
||||||
|
"green" : "0x30",
|
||||||
|
"red" : "0x30"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "113",
|
||||||
|
"green" : "101",
|
||||||
|
"red" : "105"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "153",
|
||||||
|
"green" : "143",
|
||||||
|
"red" : "147"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "31",
|
||||||
|
"green" : "28",
|
||||||
|
"red" : "28"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "255",
|
||||||
|
"green" : "250",
|
||||||
|
"red" : "254"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.639",
|
||||||
|
"green" : "0.639",
|
||||||
|
"red" : "0.639"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.349",
|
||||||
|
"green" : "0.329",
|
||||||
|
"red" : "0.337"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "31",
|
||||||
|
"green" : "27",
|
||||||
|
"red" : "28"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "229",
|
||||||
|
"green" : "225",
|
||||||
|
"red" : "230"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "31",
|
||||||
|
"green" : "27",
|
||||||
|
"red" : "28"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "126",
|
||||||
|
"green" : "116",
|
||||||
|
"red" : "121"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "153",
|
||||||
|
"green" : "143",
|
||||||
|
"red" : "147"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.965",
|
||||||
|
"green" : "0.957",
|
||||||
|
"red" : "0.949"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "31",
|
||||||
|
"green" : "27",
|
||||||
|
"red" : "28"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "255",
|
||||||
|
"green" : "255",
|
||||||
|
"red" : "255"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "31",
|
||||||
|
"green" : "27",
|
||||||
|
"red" : "28"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.120",
|
||||||
|
"blue" : "0x4F",
|
||||||
|
"green" : "0x45",
|
||||||
|
"red" : "0x49"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "0.160",
|
||||||
|
"blue" : "0xD0",
|
||||||
|
"green" : "0xC4",
|
||||||
|
"red" : "0xCA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.192",
|
||||||
|
"green" : "0.157",
|
||||||
|
"red" : "0.165"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.122",
|
||||||
|
"green" : "0.106",
|
||||||
|
"red" : "0.110"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.812",
|
||||||
|
"green" : "0.769",
|
||||||
|
"red" : "0.788"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "126",
|
||||||
|
"green" : "116",
|
||||||
|
"red" : "121"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "208",
|
||||||
|
"green" : "196",
|
||||||
|
"red" : "202"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "78",
|
||||||
|
"green" : "110",
|
||||||
|
"red" : "251"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "229",
|
||||||
|
"green" : "225",
|
||||||
|
"red" : "230"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "79",
|
||||||
|
"green" : "69",
|
||||||
|
"red" : "73"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "126",
|
||||||
|
"green" : "116",
|
||||||
|
"red" : "121"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "113",
|
||||||
|
"green" : "91",
|
||||||
|
"red" : "98"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,391 @@
|
|||||||
|
{
|
||||||
|
"sourceLanguage" : "en",
|
||||||
|
"strings" : {
|
||||||
|
"2hopWireGuardSubtitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Best for browsing, streaming, sharing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2hopWireGuardTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "2-hop WireGuard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5hopMixnetSubtitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Best for payments, emails, messages"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5hopMixnetTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "5-hop mixnet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoConnectSubtitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Auto connect at app startup"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoConnectTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Auto-connect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"automaticThemeSubtitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Use device theme"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"automaticThemeTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Automatic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connect" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Connect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connected" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connecting" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Connecting..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTo" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Connect to"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"darkThemeTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Dark theme"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disconnected" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Disconnected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disconnecting" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Disconnecting..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"displayTheme" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Display theme"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entryLocationSubtitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Manually select your entry hop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entryLocationTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Entry location selector"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fastest" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Fastest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"feedback" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Feedback"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"firstHop" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "First hop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"firstHopSelection" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "First hop selection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lastHop" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Last hop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lastHopSelection" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Last hop selection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legal" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Legal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lightThemeTitle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Light theme"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logs" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Logs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NymVPN" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "NymVPN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Search"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"searchCountry" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Search country"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selected" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Selected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selectNetwork" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Select network"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"support" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Support"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version" : "1.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct ThemeConfiguration {
|
||||||
|
public static func setup() {
|
||||||
|
NymFont.register()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import Theme
|
||||||
|
|
||||||
|
final class ThemeTests: XCTestCase {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "UIComponents",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v16)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "UIComponents",
|
||||||
|
targets: ["UIComponents"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../Theme")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "UIComponents",
|
||||||
|
dependencies: [
|
||||||
|
"Theme"
|
||||||
|
],
|
||||||
|
resources: [
|
||||||
|
.process("Resources/Assets.xcassets")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "UIComponentsTests",
|
||||||
|
dependencies: ["UIComponents"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
public struct ConnectButton: View {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
HStack {
|
||||||
|
Text("connect".localizedString)
|
||||||
|
.foregroundStyle(NymColor.connectTitle)
|
||||||
|
.textStyle(.Label.Huge.primary)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, minHeight: 56, maxHeight: 56)
|
||||||
|
.background(NymColor.primaryOrange)
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
public struct CountryCellButton: View {
|
||||||
|
private let viewModel: CountryCellButtonViewModel
|
||||||
|
|
||||||
|
public init(viewModel: CountryCellButtonViewModel) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
HStack {
|
||||||
|
flagOrBoltImage()
|
||||||
|
|
||||||
|
Text(viewModel.title)
|
||||||
|
.foregroundStyle(NymColor.sysOnSurface)
|
||||||
|
.textStyle(.Body.Large.primary)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
selectedTitleView()
|
||||||
|
}
|
||||||
|
.contentShape(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
)
|
||||||
|
|
||||||
|
.frame(width: 360, height: 56, alignment: .center)
|
||||||
|
.background(viewModel.backgroundColor)
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension CountryCellButton {
|
||||||
|
@ViewBuilder
|
||||||
|
func boltImage() -> some View {
|
||||||
|
Image(viewModel.boltImageName, bundle: .module)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 24, height: 24)
|
||||||
|
.foregroundStyle(NymColor.sysOnSurface)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func flagOrBoltImage() -> some View {
|
||||||
|
switch viewModel.type {
|
||||||
|
case .fastest:
|
||||||
|
boltImage()
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
case .country:
|
||||||
|
FlagImage(countryCode: viewModel.type.country.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
func selectedTitleView() -> some View {
|
||||||
|
if viewModel.isSelected {
|
||||||
|
Text(viewModel.selectedTitle)
|
||||||
|
.textStyle(.Label.Small.primary)
|
||||||
|
.padding(.trailing, 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Theme
|
||||||
|
|
||||||
|
public struct CountryCellButtonViewModel {
|
||||||
|
public enum CountryCellButtonType {
|
||||||
|
case fastest(country: Country)
|
||||||
|
case country(country: Country)
|
||||||
|
|
||||||
|
var country: Country {
|
||||||
|
switch self {
|
||||||
|
case .fastest(let country):
|
||||||
|
return country
|
||||||
|
case .country(let country):
|
||||||
|
return country
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let boltImageName = "bolt"
|
||||||
|
public let selectedTitle = "selected".localizedString
|
||||||
|
public let type: CountryCellButtonType
|
||||||
|
public let isSelected: Bool
|
||||||
|
|
||||||
|
public init(type: CountryCellButtonType, isSelected: Bool) {
|
||||||
|
self.type = type
|
||||||
|
self.isSelected = isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
public var title: String {
|
||||||
|
switch type {
|
||||||
|
case .fastest(let country):
|
||||||
|
return "fastest".localizedString + " (\(country.name))"
|
||||||
|
case .country(let country):
|
||||||
|
return country.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var backgroundColor: Color {
|
||||||
|
isSelected ? NymColor.countrySelectionSelectedBackground : .clear
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user