Compare commits

..

10 Commits

Author SHA1 Message Date
Rokas Ambrazevicius f194fb9cca Add appearance setting 2024-02-01 22:34:37 +02:00
Rokas Ambrazevicius 36a4f93127 Add settings 2024-02-01 17:04:49 +02:00
Rokas Ambrazevicius aae5adef38 Add app icon 2024-01-18 14:51:25 +02:00
Rokas Ambrazevicius 51754f737c Add country selection view 2024-01-17 16:45:13 +02:00
Rokas Ambrazevicius 85ccd835ab Add hop list view 2024-01-12 16:36:16 +02:00
Rokas Ambrazevicius 8e302bfdde Add Settings package 2024-01-08 14:06:08 +02:00
Rokas Ambrazevicius b609331b8e Add Home View 2024-01-08 13:39:07 +02:00
Rokas Ambrazevicius 59845174e7 Add localizations and status button 2023-12-28 16:40:02 +02:00
Rokas Ambrazevicius 0bdb6a626d Setup packages 2023-12-27 16:13:20 +02:00
Rokas Ambrazevicius 7f0256cc33 Setup iOS project 2023-12-22 13:17:21 +02:00
152 changed files with 4697 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm
NymVPM.xcworkspace/
View File
+149
View File
@@ -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"
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
@@ -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>
+36
View File
@@ -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 {}
+13
View File
@@ -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>
+22
View File
@@ -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

+10
View File
@@ -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>
+29
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
+54
View File
@@ -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 {}
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
+33
View File
@@ -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 {}
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
@@ -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>
+35
View File
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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 {}
+8
View File
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
+36
View File
@@ -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"]
)
]
)
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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