From ab343927bf9a181fd1e7e429bd4d4747bddbb18c Mon Sep 17 00:00:00 2001 From: Matus Klasovity Date: Tue, 21 May 2024 16:55:23 +0200 Subject: [PATCH 1/4] feat: Debugman Loging --- .../Application/Info.plist | 6 + .../Providers/AppDebugModeProvider.swift | 7 +- .../Providers/ConnectionsManager.swift | 94 ++++++++++++++ .../ConnectionsSettingsView.swift | 117 ++++++++++++++++++ .../Utils/StandardOutputService.swift | 2 + Sources/AppDebugMode/Views/AppDebugView.swift | 10 +- 6 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 Sources/AppDebugMode/Providers/ConnectionsManager.swift create mode 100644 Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift diff --git a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist index a802b65..6bf4ac0 100644 --- a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist +++ b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist @@ -2,6 +2,12 @@ + NSBonjourServices + + _LogDog._tcp + + NSLocalNetworkUsageDescription + Test CFBundleDocumentTypes diff --git a/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift b/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift index d515e21..e0e6168 100644 --- a/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift +++ b/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift @@ -20,6 +20,7 @@ public final class AppDebugModeProvider { // MARK: - Internal - Variables + internal let connectionManager = ConnectionsManager() internal var servers: [ApiServer] = [] internal var serversCollections: [ApiServerCollection] = [] internal var onServerChange: (() -> Void)? @@ -94,8 +95,10 @@ public extension AppDebugModeProvider { serversCollections: AppDebugModeProvider.shared.serversCollections, customControls: AnyView( AppDebugModeProvider.shared.customControls), customControlsViewIsVisible: AppDebugModeProvider.shared.customControlsViewIsVisible - ).eraseToUIViewController() - + ) + .environmentObject(connectionManager) + .eraseToUIViewController() + let navigationController = UINavigationController(rootViewController: viewController) navigationController.navigationBar.configureSolidAppearance() return navigationController diff --git a/Sources/AppDebugMode/Providers/ConnectionsManager.swift b/Sources/AppDebugMode/Providers/ConnectionsManager.swift new file mode 100644 index 0000000..8f41f08 --- /dev/null +++ b/Sources/AppDebugMode/Providers/ConnectionsManager.swift @@ -0,0 +1,94 @@ +// +// ConnectionsManager.swift +// +// +// Created by Matus Klasovity on 21/05/2024. +// + +import Foundation +import MultipeerConnectivity + +final class ConnectionsManager: NSObject, ObservableObject { + + let session: MCSession + let browser: MCNearbyServiceBrowser + + @Published var connectedPeers: [MCPeerID] = [] + + override init() { + let appName = Bundle.main.infoDictionary?[kCFBundleNameKey as String] as? String + let peer = MCPeerID( + displayName: UIDevice.current.name + " - " + (appName ?? "") + ) + + browser = MCNearbyServiceBrowser( + peer: peer, + serviceType: "LogDog" + ) + session = MCSession( + peer: peer, + securityIdentity: nil, + encryptionPreference: .none + ) + super.init() + + session.delegate = self + } + + func cancelConnection(to peer: MCPeerID) { + session.cancelConnectPeer(peer) + } + + func sendToAllPeers(message: String) { + guard !session.connectedPeers.isEmpty else { return } + + let data = Data(message.utf8) + do { + try session.send(data, toPeers: session.connectedPeers, with: .reliable) + } catch { + print("error sending data to peers: \(error)") + } + } + +} + + +extension ConnectionsManager: MCSessionDelegate { + + func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { + switch state { + case .notConnected: + print("not connected to peer: \(peerID.displayName)") + + case .connecting: + print("connecting to peer: \(peerID.displayName)") + + case .connected: + print("connected to peer: \(peerID.displayName)") + + @unknown default: + print("unknown state: \(state)") + } + + DispatchQueue.main.async { [weak self] in + self?.connectedPeers = session.connectedPeers + } + } + + func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { + print("received data from peer: \(peerID)") + } + + func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { + print("received stream from peer: \(peerID)") + } + + func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { + print("started receiving resource from peer: \(peerID)") + } + + func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: (any Error)?) { + print("finished receiving resource from peer: \(peerID)") + } + +} diff --git a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift new file mode 100644 index 0000000..e754a5d --- /dev/null +++ b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift @@ -0,0 +1,117 @@ +// +// ConnectionsSettingsView.swift +// +// +// Created by Matus Klasovity on 21/05/2024. +// + +import SwiftUI +import MultipeerConnectivity + +struct ConnectionsSettingsView: View { + + @State var isBrowserPresented: Bool = false + @EnvironmentObject var connectionsManager: ConnectionsManager + + var body: some View { + List { + connectedPeersSection() + actionsSection() + } + .navigationTitle("Connections") + .listStyle(.insetGrouped) + .listBackgroundColor(AppDebugColors.backgroundPrimary, for: .insetGrouped) + .sheet(isPresented: $isBrowserPresented) { + BrowserRepresentableViewController( + browser: connectionsManager.browser, + session: connectionsManager.session + ) + } + } + +} + +// MARK: - Components + +private extension ConnectionsSettingsView { + + // MARK: - Sections + + func actionsSection() -> some View { + Section { + VStack { + ButtonFilled(text: "Open browser") { + isBrowserPresented = true + } + } + .listRowBackground(AppDebugColors.backgroundSecondary) + } header: { + Text("Actions") + .foregroundColor(AppDebugColors.textSecondary) + } + } + + func connectedPeersSection() -> some View { + Section { + if connectionsManager.connectedPeers.isEmpty { + Text("No connected peers") + .listRowBackground(AppDebugColors.backgroundSecondary) + .foregroundColor(AppDebugColors.textPrimary) + } + + ForEach(connectionsManager.connectedPeers, id: \.displayName) { connectedPeer in + Text(connectedPeer.displayName) + .listRowSeparatorColor(AppDebugColors.primary, for: .insetGrouped) + .listRowBackground(AppDebugColors.backgroundSecondary) + .foregroundColor(AppDebugColors.textPrimary) + } + .onDelete { offset in + for index in offset { + connectionsManager.cancelConnection(to: connectionsManager.connectedPeers[index]) + } + } + } header: { + Text("Connected peers") + .foregroundColor(AppDebugColors.textSecondary) + } + } + +} + + +// MARK: - BrowserRepresentableViewController + +private struct BrowserRepresentableViewController: UIViewControllerRepresentable { + + let browser: MCNearbyServiceBrowser + let session: MCSession + + func makeUIViewController(context: Context) -> UIViewController { + let viewController = MCBrowserViewController( + browser: browser, + session: session + ) + viewController.delegate = context.coordinator + + return viewController + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + final class Coordinator: NSObject, MCBrowserViewControllerDelegate { + + func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) { + browserViewController.dismiss(animated: true) + } + + func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) { + browserViewController.dismiss(animated: true) + } + + } + +} diff --git a/Sources/AppDebugMode/Utils/StandardOutputService.swift b/Sources/AppDebugMode/Utils/StandardOutputService.swift index 523ae63..f1d21f4 100644 --- a/Sources/AppDebugMode/Utils/StandardOutputService.swift +++ b/Sources/AppDebugMode/Utils/StandardOutputService.swift @@ -152,6 +152,8 @@ final public class StandardOutputService: ObservableObject { public func connectCustomLogStreamPublisher(_ stream: AnyPublisher) { stream.sink { logMessage in + // Send message to 🐶 logDog + AppDebugModeProvider.shared.connectionManager.sendToAllPeers(message: logMessage) let log = StandardOutputService.Log(message: logMessage) StandardOutputService.shared.capturedOutput.append(log) } diff --git a/Sources/AppDebugMode/Views/AppDebugView.swift b/Sources/AppDebugMode/Views/AppDebugView.swift index 56ee81e..2cb1ba3 100644 --- a/Sources/AppDebugMode/Views/AppDebugView.swift +++ b/Sources/AppDebugMode/Views/AppDebugView.swift @@ -11,7 +11,8 @@ struct AppDebugView: View { @Environment(\.presentationMode) var presentationMode @Environment(\.hostingControllerHolder) var viewControlleeHolder - + @EnvironmentObject var connectionsManager: ConnectionsManager + // MARK: - Properties private var screens: [Screen] @@ -83,6 +84,11 @@ struct AppDebugView: View { title: "Logs", image: Image(systemName: "list.bullet.rectangle"), destination: AnyView(ConsoleLogsView()) + ), + Screen( + title: "Connections", + image: Image(systemName: "network"), + destination: AnyView(erasing: ConnectionsSettingsView()) ) ]) @@ -138,7 +144,7 @@ private extension AppDebugView { @ViewBuilder func navigationLink(screen: Screen) -> some View { Button { - let viewController = screen.destination.eraseToUIViewController() + let viewController = screen.destination.environmentObject(connectionsManager).eraseToUIViewController() viewControlleeHolder?.controller?.navigationController?.pushViewController(viewController, animated: false) } label: { HStack { From 5a1ae5ab8bd0dc34b07370b5c39599314d7f6025 Mon Sep 17 00:00:00 2001 From: Matus Klasovity Date: Thu, 23 May 2024 09:47:17 +0200 Subject: [PATCH 2/4] feat: Share Log --- .../ConsoleLogDetailView.swift | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Sources/AppDebugMode/Screens/ConsoleLogsVIew/ConsoleLogDetailView.swift b/Sources/AppDebugMode/Screens/ConsoleLogsVIew/ConsoleLogDetailView.swift index 535b8bf..5614220 100644 --- a/Sources/AppDebugMode/Screens/ConsoleLogsVIew/ConsoleLogDetailView.swift +++ b/Sources/AppDebugMode/Screens/ConsoleLogsVIew/ConsoleLogDetailView.swift @@ -26,7 +26,15 @@ class ConsoleLogDetailViewController: UIViewController { button.setTitleColor(UIColor(cgColor: AppDebugColors.primary.opacity(0.5).cgColor!), for: .highlighted) return button }() - + + let shareButton: UIButton = { + var button = UIButton() + button.setTitle("Share", for: .normal) + button.setTitleColor(UIColor(cgColor: AppDebugColors.primary.cgColor!), for: .normal) + button.setTitleColor(UIColor(cgColor: AppDebugColors.primary.opacity(0.5).cgColor!), for: .highlighted) + return button + }() + let trailingNavigationView: UIStackView = { var stackView = UIStackView() stackView.axis = .horizontal @@ -93,8 +101,9 @@ private extension ConsoleLogDetailViewController { detectorButton.addTarget(self, action: #selector(detectorButtonClicked), for: .touchUpInside) copyButton.addTarget(self, action: #selector(copyButtonClicked), for: .touchUpInside) - [detectorButton, copyButton].forEach { trailingNavigationView.addArrangedSubview($0) } - + shareButton.addTarget(self, action: #selector(shareButtonClicked), for: .touchUpInside) + [detectorButton, copyButton, shareButton].forEach { trailingNavigationView.addArrangedSubview($0) } + scrollView.addSubview(textView) } @@ -127,5 +136,11 @@ private extension ConsoleLogDetailViewController { @IBAction func detectorButtonClicked(_ sender: Any?) { textView.dataDetectorTypes = .all } - + + @IBAction func shareButtonClicked(_ sender: Any?) { + let activityViewController = UIActivityViewController(activityItems: [textView.text], applicationActivities: nil) + activityViewController.popoverPresentationController?.sourceView = view + present(activityViewController, animated: true, completion: nil) + } + } From c541fe15b50d93f4e6b8f7162f8b20e77fb88e93 Mon Sep 17 00:00:00 2001 From: Matus Klasovity Date: Tue, 28 May 2024 13:49:24 +0200 Subject: [PATCH 3/4] feat: Proxy Settings --- .../Application/Info.plist | 2 +- .../RequestManager/RequestManager.swift | 16 +++- README.md | 29 ++++++ .../Providers/AppDebugModeProvider.swift | 6 +- .../Providers/ConnectionsManager.swift | 2 +- .../Providers/ProxySettingsProvider.swift | 40 +++++++++ .../ConnectedPeers/ConnectedPeersView.swift | 83 +++++++++++++++++ .../ConnectionsSettingsView.swift | 89 +++---------------- .../ProxySettings/ProxySettingsView.swift | 84 +++++++++++++++++ .../ProxySettingsViewModel.swift | 46 ++++++++++ .../Utils/StandardOutputService.swift | 2 +- 11 files changed, 316 insertions(+), 83 deletions(-) create mode 100644 Sources/AppDebugMode/Providers/ProxySettingsProvider.swift create mode 100644 Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectedPeers/ConnectedPeersView.swift create mode 100644 Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsView.swift create mode 100644 Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsViewModel.swift diff --git a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist index 6bf4ac0..859330c 100644 --- a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist +++ b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Application/Info.plist @@ -4,7 +4,7 @@ NSBonjourServices - _LogDog._tcp + _Debugman._tcp NSLocalNetworkUsageDescription Test diff --git a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Managers/RequestManager/RequestManager.swift b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Managers/RequestManager/RequestManager.swift index 719bc4c..d3257bc 100644 --- a/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Managers/RequestManager/RequestManager.swift +++ b/AppDebugMode-iOS-Sample/AppDebugMode-iOS-Sample/Managers/RequestManager/RequestManager.swift @@ -37,7 +37,21 @@ final class RequestManager: RequestManagerType { #if DEBUG StandardOutputService.shared.connectCustomLogStreamPublisher(monitor.subscribeToMessages()) #endif - session = NetworkSession(baseUrl: baseServer.rawValue, configuration: .init(urlSessionConfiguration: .default, interceptor: nil, serverTrustManager: nil, eventMonitors: [monitor])) + + #if DEBUG + let urlSessionConfig = AppDebugModeProvider.shared.proxySettingsProvider.urlSessionConfiguration + #else + let urlSessionConfig = URLSessionConfiguration.default + #endif + + session = NetworkSession( + baseUrl: baseServer.rawValue, + configuration: NetworkSessionConfiguration( + urlSessionConfiguration: urlSessionConfig, + interceptor: nil, + eventMonitors: [monitor] + ) + ) } func fetchLarge() -> AnyPublisher { diff --git a/README.md b/README.md index c88d4d1..62411a0 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,35 @@ if StandardOutputService.shared.shouldRedirectLogsToAppDebugView { #endif ``` + +## Debugman connection +If you want to enable app to send logs to Debugman you need to add following to your Info.plist file. +```xml +NSBonjourServices + + _Debugman._tcp + +NSLocalNetworkUsageDescription +Test +``` + +To enable proxy for Debugman you need to add AppDebugMode Proxy URL Sessiion Config to your Network session. +```swift +#if DEBUG +let urlSessionConfig = AppDebugModeProvider.shared.proxySettingsProvider.urlSessionConfiguration +#else +let urlSessionConfig = URLSessionConfiguration.default +#endif + +session = NetworkSession( + baseUrl: baseServer.rawValue, + configuration: NetworkSessionConfiguration( + urlSessionConfiguration: urlSessionConfig, + interceptor: nil, + eventMonitors: [monitor] + ) +) +``` ## Activation in App - In app you can activate debug mode by shaking device or in simulator by `CMD + CTRL + Z` diff --git a/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift b/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift index e0e6168..9820374 100644 --- a/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift +++ b/Sources/AppDebugMode/Providers/AppDebugModeProvider.swift @@ -40,7 +40,11 @@ public final class AppDebugModeProvider { public var selectedUserProfile: UserProfile? { UserProfilesProvider.shared.selectedUserProfile } - + + public var proxySettingsProvider: ProxySettingsProvider { + ProxySettingsProvider.shared + } + @available(*, deprecated, renamed: "selectedUserProfilePublisher") public var selectedTestingUserPublisher = UserProfilesProvider.shared.selectedUserProfilePublisher public var selectedUserProfilePublisher = UserProfilesProvider.shared.selectedUserProfilePublisher diff --git a/Sources/AppDebugMode/Providers/ConnectionsManager.swift b/Sources/AppDebugMode/Providers/ConnectionsManager.swift index 8f41f08..adb519f 100644 --- a/Sources/AppDebugMode/Providers/ConnectionsManager.swift +++ b/Sources/AppDebugMode/Providers/ConnectionsManager.swift @@ -23,7 +23,7 @@ final class ConnectionsManager: NSObject, ObservableObject { browser = MCNearbyServiceBrowser( peer: peer, - serviceType: "LogDog" + serviceType: "Debugman" ) session = MCSession( peer: peer, diff --git a/Sources/AppDebugMode/Providers/ProxySettingsProvider.swift b/Sources/AppDebugMode/Providers/ProxySettingsProvider.swift new file mode 100644 index 0000000..2c48d49 --- /dev/null +++ b/Sources/AppDebugMode/Providers/ProxySettingsProvider.swift @@ -0,0 +1,40 @@ +// +// ProxySettingsProvider.swift +// +// +// Created by Matus Klasovity on 28/05/2024. +// + +import Foundation +import GoodPersistence + +public final class ProxySettingsProvider { + + @UserDefaultValue("proxyIpAdress", defaultValue: "") + var proxyIpAdress: String + + @UserDefaultValue("proxyPort", defaultValue: 8080) + var proxyPort: Int + + public static let shared = ProxySettingsProvider() + + public var urlSessionConfiguration: URLSessionConfiguration { + let urlSessionConfig = URLSessionConfiguration.default + urlSessionConfig.connectionProxyDictionary = [AnyHashable: Any]() + urlSessionConfig.connectionProxyDictionary?[kCFNetworkProxiesHTTPEnable as String] = 1 + urlSessionConfig.connectionProxyDictionary?[kCFNetworkProxiesHTTPProxy as String] = proxyIpAdress + urlSessionConfig.connectionProxyDictionary?[kCFNetworkProxiesHTTPPort as String] = proxyPort + urlSessionConfig.connectionProxyDictionary?["HTTPSProxy"] = proxyIpAdress + urlSessionConfig.connectionProxyDictionary?["HTTPSPort"] = proxyPort + + return urlSessionConfig + } + + func updateProxySettings(ipAddress: String, port: Int) { + proxyIpAdress = ipAddress + proxyPort = port + + exit(0) + } + +} diff --git a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectedPeers/ConnectedPeersView.swift b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectedPeers/ConnectedPeersView.swift new file mode 100644 index 0000000..7936a17 --- /dev/null +++ b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectedPeers/ConnectedPeersView.swift @@ -0,0 +1,83 @@ +// +// ConnectedPeersView.swift +// +// +// Created by Matus Klasovity on 28/05/2024. +// + +import SwiftUI +import MultipeerConnectivity + +struct ConnectedPeersView: View { + + @State var isBrowserPresented: Bool = false + @EnvironmentObject var connectionsManager: ConnectionsManager + + var body: some View { + Group { + if connectionsManager.connectedPeers.isEmpty { + Text("No connected peers") + .foregroundColor(AppDebugColors.textPrimary) + } + + ForEach(connectionsManager.connectedPeers, id: \.displayName) { connectedPeer in + Text(connectedPeer.displayName) + + .foregroundColor(AppDebugColors.textPrimary) + } + .onDelete { offset in + for index in offset { + connectionsManager.cancelConnection(to: connectionsManager.connectedPeers[index]) + } + } + + ButtonFilled(text: "Open browser") { + isBrowserPresented = true + } + .sheet(isPresented: $isBrowserPresented) { + BrowserRepresentableViewController( + browser: connectionsManager.browser, + session: connectionsManager.session + ) + } + } + } + +} + +// MARK: - BrowserRepresentableViewController + +private struct BrowserRepresentableViewController: UIViewControllerRepresentable { + + let browser: MCNearbyServiceBrowser + let session: MCSession + + func makeUIViewController(context: Context) -> UIViewController { + let viewController = MCBrowserViewController( + browser: browser, + session: session + ) + viewController.delegate = context.coordinator + + return viewController + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + final class Coordinator: NSObject, MCBrowserViewControllerDelegate { + + func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) { + browserViewController.dismiss(animated: true) + } + + func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) { + browserViewController.dismiss(animated: true) + } + + } + +} diff --git a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift index e754a5d..9149b2c 100644 --- a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift +++ b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ConnectionsSettingsView.swift @@ -6,27 +6,17 @@ // import SwiftUI -import MultipeerConnectivity struct ConnectionsSettingsView: View { - @State var isBrowserPresented: Bool = false - @EnvironmentObject var connectionsManager: ConnectionsManager - var body: some View { List { connectedPeersSection() - actionsSection() + proxySettingsSection() } .navigationTitle("Connections") .listStyle(.insetGrouped) .listBackgroundColor(AppDebugColors.backgroundPrimary, for: .insetGrouped) - .sheet(isPresented: $isBrowserPresented) { - BrowserRepresentableViewController( - browser: connectionsManager.browser, - session: connectionsManager.session - ) - } } } @@ -35,83 +25,26 @@ struct ConnectionsSettingsView: View { private extension ConnectionsSettingsView { - // MARK: - Sections - - func actionsSection() -> some View { + func connectedPeersSection() -> some View { Section { - VStack { - ButtonFilled(text: "Open browser") { - isBrowserPresented = true - } - } - .listRowBackground(AppDebugColors.backgroundSecondary) + ConnectedPeersView() + .listRowSeparatorColor(AppDebugColors.primary, for: .insetGrouped) + .listRowBackground(AppDebugColors.backgroundSecondary) } header: { - Text("Actions") + Text("Connected peers") .foregroundColor(AppDebugColors.textSecondary) } } - func connectedPeersSection() -> some View { + func proxySettingsSection() -> some View { Section { - if connectionsManager.connectedPeers.isEmpty { - Text("No connected peers") - .listRowBackground(AppDebugColors.backgroundSecondary) - .foregroundColor(AppDebugColors.textPrimary) - } - - ForEach(connectionsManager.connectedPeers, id: \.displayName) { connectedPeer in - Text(connectedPeer.displayName) - .listRowSeparatorColor(AppDebugColors.primary, for: .insetGrouped) - .listRowBackground(AppDebugColors.backgroundSecondary) - .foregroundColor(AppDebugColors.textPrimary) - } - .onDelete { offset in - for index in offset { - connectionsManager.cancelConnection(to: connectionsManager.connectedPeers[index]) - } - } + ProxySettingsView() + .listRowBackground(AppDebugColors.backgroundSecondary) + .foregroundColor(AppDebugColors.textPrimary) } header: { - Text("Connected peers") + Text("Proxy settings") .foregroundColor(AppDebugColors.textSecondary) } } } - - -// MARK: - BrowserRepresentableViewController - -private struct BrowserRepresentableViewController: UIViewControllerRepresentable { - - let browser: MCNearbyServiceBrowser - let session: MCSession - - func makeUIViewController(context: Context) -> UIViewController { - let viewController = MCBrowserViewController( - browser: browser, - session: session - ) - viewController.delegate = context.coordinator - - return viewController - } - - func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator() - } - - final class Coordinator: NSObject, MCBrowserViewControllerDelegate { - - func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) { - browserViewController.dismiss(animated: true) - } - - func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) { - browserViewController.dismiss(animated: true) - } - - } - -} diff --git a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsView.swift b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsView.swift new file mode 100644 index 0000000..84497cc --- /dev/null +++ b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsView.swift @@ -0,0 +1,84 @@ +// +// ProxySettingsView.swift +// +// +// Created by Matus Klasovity on 28/05/2024. +// + +import SwiftUI +@_spi(Advanced) import SwiftUIIntrospect + +struct ProxySettingsView: View { + + @ObservedObject private var viewModel = ProxySettingsViewModel() + + var body: some View { + Group { + Text(""" + Set up a proxy server to monitor network traffic. + The proxy server will be used only for the app. + + To set up a proxy server, enter the IP address and port of the proxy server. + You can find the IP address and port in the **Debugman** app. You also **need to install certificates** on your device from debugman app and trust them. + + To install certificates, open the Debugman app, go to the **Proxy Settings** -> **Share certificates**. Then use airDrop to send the certificates to your device and install them from the Settings app (similiar workflow to bitrise certificates installation). Then click the **Open Cert Trust Settings** button and enable the certificates. + """ + ) + .foregroundColor(AppDebugColors.textPrimary) + .padding(.bottom, 8) + + TextField("", text: $viewModel.proxyIpAdress) + .introspect(.textField, on: .iOS(.v14...)) { textField in + textField.attributedPlaceholder = NSAttributedString( + string: "Proxy ip address", + attributes: [ + .foregroundColor: UIColor(AppDebugColors.textSecondary) + ] + ) + } + .disableAutocorrection(true) + .autocapitalization(.none) + .keyboardType(.URL) + .padding() + .overlay( + RoundedRectangle(cornerRadius: 8).stroke(AppDebugColors.textSecondary, lineWidth: 1) + ) + .foregroundColor(AppDebugColors.textPrimary) + + TextField("", text: $viewModel.proxyPort) + .introspect(.textField, on: .iOS(.v14...)) { textField in + textField.attributedPlaceholder = NSAttributedString( + string: "Proxy port", + attributes: [ + .foregroundColor: UIColor(AppDebugColors.textSecondary) + ] + ) + } + .disableAutocorrection(true) + .autocapitalization(.none) + .keyboardType(.numberPad) + .padding() + .overlay( + RoundedRectangle(cornerRadius: 8).stroke(AppDebugColors.textSecondary, lineWidth: 1) + ) + .foregroundColor(AppDebugColors.textPrimary) + + ButtonFilled(text: "Open Cert Trust Settings") { + let url = URL(string: "App-prefs:General&path=About/CERT_TRUST_SETTINGS")! + UIApplication.shared.open(url) + } + + ButtonFilled(text: "Save proxy settings") { + viewModel.saveProxySettings() + } + } + .alert(item: $viewModel.validationError) { validationError in + Alert( + title: Text("Validation error"), + message: Text(validationError.localizedDescription), + dismissButton: .cancel() + ) + } + } + +} diff --git a/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsViewModel.swift b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsViewModel.swift new file mode 100644 index 0000000..05ead02 --- /dev/null +++ b/Sources/AppDebugMode/Screens/ConnectionsSettingsView/ProxySettings/ProxySettingsViewModel.swift @@ -0,0 +1,46 @@ +// +// ProxySettingsViewModel.swift +// +// +// Created by Matus Klasovity on 28/05/2024. +// + +import Foundation + +enum ProxySettingsValidationError: Error, Identifiable { + + case portIsNotANumber + + var id: String { + self.localizedDescription + } + + var localizedDescription: String { + switch self { + case .portIsNotANumber: + return "Port must be a number" + } + } + +} + +final class ProxySettingsViewModel: ObservableObject { + + @Published var proxyIpAdress: String = ProxySettingsProvider.shared.proxyIpAdress + @Published var proxyPort: String = String(ProxySettingsProvider.shared.proxyPort) + @Published var validationError: ProxySettingsValidationError? + +} + +extension ProxySettingsViewModel { + + func saveProxySettings() { + guard let port = Int(proxyPort) else { + validationError = .portIsNotANumber + + return + } + ProxySettingsProvider.shared.updateProxySettings(ipAddress: proxyIpAdress, port: port) + } + +} diff --git a/Sources/AppDebugMode/Utils/StandardOutputService.swift b/Sources/AppDebugMode/Utils/StandardOutputService.swift index f1d21f4..8bdf0c2 100644 --- a/Sources/AppDebugMode/Utils/StandardOutputService.swift +++ b/Sources/AppDebugMode/Utils/StandardOutputService.swift @@ -152,7 +152,7 @@ final public class StandardOutputService: ObservableObject { public func connectCustomLogStreamPublisher(_ stream: AnyPublisher) { stream.sink { logMessage in - // Send message to 🐶 logDog + // Send message to 🪲 Debugman AppDebugModeProvider.shared.connectionManager.sendToAllPeers(message: logMessage) let log = StandardOutputService.Log(message: logMessage) StandardOutputService.shared.capturedOutput.append(log) From 2a1b2c3779ecde214259b5524a889d2968b37f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0a=C5=A1ala?= <31418257+plajdo@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:46:55 +0200 Subject: [PATCH 4/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62411a0..4252f2c 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ If you want to enable app to send logs to Debugman you need to add following to _Debugman._tcp NSLocalNetworkUsageDescription -Test +Proxy and debugger connection when running in DEBUG mode ``` To enable proxy for Debugman you need to add AppDebugMode Proxy URL Sessiion Config to your Network session.