From 25b9faea375657f9ad5d73ca8c05d8ac044c9b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D1=82=D1=91=D0=BC=D0=BA=D0=B8=D0=BD=20=D0=90?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Mon, 16 Feb 2026 10:36:26 +0300 Subject: [PATCH 1/3] Add menuItems and sectionScreens --- DebugScreen.xcodeproj/project.pbxproj | 88 +++++++++++++++ .../Common/Models/Common/MainTableBlock.swift | 4 + .../Common/Protocols/Content/MenuItem.swift | 15 +++ .../Protocols/Content/SectionScreen.swift | 13 +++ .../Cells/PlainTextCell/PlainTextCell.swift | 1 - .../Flows/DebugScreenCoordinator.swift | 12 ++ .../Presenter/MainModuleOutput.swift | 2 + .../Presenter/MainModulePresenter.swift | 5 + .../MainScreen/View/Adapter/MainAdapter.swift | 21 +++- .../MainScreen/View/MainViewController.swift | 3 + .../MainScreen/View/MainViewOutput.swift | 1 + .../SectionScreenModuleConfigurator.swift | 29 +++++ .../Presenter/SectionScreenModuleInput.swift | 8 ++ .../Presenter/SectionScreenModuleOutput.swift | 17 +++ .../Presenter/SectionScreenPresenter.swift | 65 +++++++++++ .../View/SectionScreenViewController.swift | 82 ++++++++++++++ .../View/SectionScreenViewController.xib | 45 ++++++++ .../View/SectionScreenViewInput.swift | 10 ++ .../View/SectionScreenViewOutput.swift | 14 +++ .../DebugScreenExample/App/AppDelegate.swift | 4 + .../Builders/MenuItemsSectionBuilder.swift | 60 ++++++++++ .../SectionScreensSectionBuilder.swift | 104 ++++++++++++++++++ .../Models/DebugScreenMenuItem.swift | 26 +++++ .../Models/DebugScreenSectionScreen.swift | 24 ++++ .../Library/Resources/Strings/Strings.swift | 51 +++++++++ .../Strings/en.lproj/Localizable.strings | 26 +++++ .../Strings/ru.lproj/Localizable.strings | 26 +++++ 27 files changed, 753 insertions(+), 3 deletions(-) create mode 100644 DebugScreen/Common/Protocols/Content/MenuItem.swift create mode 100644 DebugScreen/Common/Protocols/Content/SectionScreen.swift create mode 100644 DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift create mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift create mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift create mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift create mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift create mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib create mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift create mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift create mode 100644 Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/MenuItemsSectionBuilder.swift create mode 100644 Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift create mode 100644 Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenMenuItem.swift create mode 100644 Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift diff --git a/DebugScreen.xcodeproj/project.pbxproj b/DebugScreen.xcodeproj/project.pbxproj index 6c3b244..7d84889 100644 --- a/DebugScreen.xcodeproj/project.pbxproj +++ b/DebugScreen.xcodeproj/project.pbxproj @@ -7,6 +7,16 @@ objects = { /* Begin PBXBuildFile section */ + 400C6A2B2F3F67C500678284 /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2A2F3F67C500678284 /* MenuItem.swift */; }; + 400C6A2D2F3F69A700678284 /* SectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2C2F3F69A700678284 /* SectionScreen.swift */; }; + 400C6A302F3F6D9700678284 /* SectionScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */; }; + 400C6A412F3F800000678284 /* SectionScreenModuleConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */; }; + 400C6A432F3F800100678284 /* SectionScreenModuleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */; }; + 400C6A452F3F800200678284 /* SectionScreenModuleOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */; }; + 400C6A472F3F800300678284 /* SectionScreenPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */; }; + 400C6A492F3F800400678284 /* SectionScreenViewInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */; }; + 400C6A4B2F3F800500678284 /* SectionScreenViewOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */; }; + 400C6A4D2F3F800600678284 /* SectionScreenViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */; }; 9054BA4A2A5C6CCA0009D70D /* BaseNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9054BA492A5C6CCA0009D70D /* BaseNavigationController.swift */; }; 90985F5C2A5C58980066790A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2E91F9A2A4C2308000E3E45 /* LaunchScreen.storyboard */; }; A4301A65285A462F006B77C0 /* DebugScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A43019E6285A3137006B77C0 /* DebugScreenTests.swift */; }; @@ -105,6 +115,10 @@ B2CA74E32A1744F300E91B24 /* FileViewerViewOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CA74E22A1744F300E91B24 /* FileViewerViewOutput.swift */; }; B2CA74E82A175D7700E91B24 /* FileViewerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CA74E62A175D7700E91B24 /* FileViewerViewController.swift */; }; B2CA74E92A175D7700E91B24 /* FileViewerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B2CA74E72A175D7700E91B24 /* FileViewerViewController.xib */; }; + 400C6A322F3F700200678284 /* DebugScreenMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */; }; + 400C6A342F3F700400678284 /* DebugScreenSectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */; }; + 400C6A362F3F700600678284 /* MenuItemsSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */; }; + 400C6A382F3F700800678284 /* SectionScreensSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */; }; B2E91F9D2A4C2308000E3E45 /* UserDefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F702A4C2304000E3E45 /* UserDefaultsKeys.swift */; }; B2E91F9E2A4C2308000E3E45 /* UserDefaultsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F712A4C2304000E3E45 /* UserDefaultsService.swift */; }; B2E91F9F2A4C2308000E3E45 /* DestinationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F722A4C2304000E3E45 /* DestinationViewController.swift */; }; @@ -174,6 +188,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 400C6A2A2F3F67C500678284 /* MenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = ""; }; + 400C6A2C2F3F69A700678284 /* SectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreen.swift; sourceTree = ""; }; + 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewController.swift; sourceTree = ""; }; + 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleConfigurator.swift; sourceTree = ""; }; + 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleInput.swift; sourceTree = ""; }; + 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleOutput.swift; sourceTree = ""; }; + 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenPresenter.swift; sourceTree = ""; }; + 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewInput.swift; sourceTree = ""; }; + 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewOutput.swift; sourceTree = ""; }; + 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionScreenViewController.xib; sourceTree = ""; }; 9054BA492A5C6CCA0009D70D /* BaseNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNavigationController.swift; sourceTree = ""; }; A43019E6285A3137006B77C0 /* DebugScreenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugScreenTests.swift; sourceTree = ""; }; A47BDB38286A1249002326B8 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; @@ -275,6 +299,10 @@ B2CA74E62A175D7700E91B24 /* FileViewerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileViewerViewController.swift; sourceTree = ""; }; B2CA74E72A175D7700E91B24 /* FileViewerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FileViewerViewController.xib; sourceTree = ""; }; B2E91F582A4C21E9000E3E45 /* DebugScreenExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DebugScreenExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenMenuItem.swift; sourceTree = ""; }; + 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenSectionScreen.swift; sourceTree = ""; }; + 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemsSectionBuilder.swift; sourceTree = ""; }; + 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreensSectionBuilder.swift; sourceTree = ""; }; B2E91F702A4C2304000E3E45 /* UserDefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsKeys.swift; sourceTree = ""; }; B2E91F712A4C2304000E3E45 /* UserDefaultsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsService.swift; sourceTree = ""; }; B2E91F722A4C2304000E3E45 /* DestinationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DestinationViewController.swift; sourceTree = ""; }; @@ -349,6 +377,45 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 400C6A2E2F3F6D6E00678284 /* SectionScreen */ = { + isa = PBXGroup; + children = ( + 400C6A4E2F3F800700678284 /* Configurator */, + 400C6A4F2F3F800800678284 /* Presenter */, + 400C6A502F3F800900678284 /* View */, + ); + path = SectionScreen; + sourceTree = ""; + }; + 400C6A4E2F3F800700678284 /* Configurator */ = { + isa = PBXGroup; + children = ( + 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */, + ); + path = Configurator; + sourceTree = ""; + }; + 400C6A4F2F3F800800678284 /* Presenter */ = { + isa = PBXGroup; + children = ( + 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */, + 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */, + 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */, + ); + path = Presenter; + sourceTree = ""; + }; + 400C6A502F3F800900678284 /* View */ = { + isa = PBXGroup; + children = ( + 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */, + 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */, + 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */, + 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */, + ); + path = View; + sourceTree = ""; + }; A43019E5285A3137006B77C0 /* DebugScreenTests */ = { isa = PBXGroup; children = ( @@ -504,6 +571,7 @@ isa = PBXGroup; children = ( A4BDF4B828512FAA00651C61 /* DebugScreenCoordinator.swift */, + 400C6A2E2F3F6D6E00678284 /* SectionScreen */, B2610E1F2A16D90600509FA1 /* FileViewer */, B2B1D43C2A52DB3200CC5A11 /* InfoTable */, A4BDF4A028512FAA00651C61 /* MainScreen */, @@ -710,6 +778,8 @@ B28F97E92A271FC00089C032 /* CopiedText.swift */, B28F97E72A271FAF0089C032 /* FeatureToggle.swift */, B28F97EB2A271FF30089C032 /* SelectionList.swift */, + 400C6A2A2F3F67C500678284 /* MenuItem.swift */, + 400C6A2C2F3F69A700678284 /* SectionScreen.swift */, ); path = Content; sourceTree = ""; @@ -956,6 +1026,8 @@ B2E91F772A4C2305000E3E45 /* ActionsSectionBuilder.swift */, B2E91F792A4C2305000E3E45 /* CopiedTextSectionBuilder.swift */, B2B1D4502A52FC3000CC5A11 /* InfoTablesSectionBuilder.swift */, + 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */, + 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */, B2E91F7A2A4C2305000E3E45 /* ServerSelectionSectionBuilder.swift */, B2E91F782A4C2305000E3E45 /* TogglesSectionBuilder.swift */, ); @@ -969,6 +1041,8 @@ B2E91F7D2A4C2305000E3E45 /* CopiedTextItem.swift */, B2E91F7E2A4C2305000E3E45 /* DebugScreenActionList.swift */, B2E91F7F2A4C2305000E3E45 /* DebugScreenAction.swift */, + 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */, + 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */, B2E91F802A4C2305000E3E45 /* ServersSelectionList.swift */, ); path = Models; @@ -1232,6 +1306,7 @@ A47BDB45286A1249002326B8 /* Assets.xcassets in Resources */, B28F97F42A2728560089C032 /* ButtonCell.xib in Resources */, A4BDF4C328512FAA00651C61 /* MainViewController.xib in Resources */, + 400C6A4D2F3F800600678284 /* SectionScreenViewController.xib in Resources */, B2CA74E92A175D7700E91B24 /* FileViewerViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1354,6 +1429,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 400C6A302F3F6D9700678284 /* SectionScreenViewController.swift in Sources */, + 400C6A412F3F800000678284 /* SectionScreenModuleConfigurator.swift in Sources */, + 400C6A432F3F800100678284 /* SectionScreenModuleInput.swift in Sources */, + 400C6A452F3F800200678284 /* SectionScreenModuleOutput.swift in Sources */, + 400C6A472F3F800300678284 /* SectionScreenPresenter.swift in Sources */, + 400C6A492F3F800400678284 /* SectionScreenViewInput.swift in Sources */, + 400C6A4B2F3F800500678284 /* SectionScreenViewOutput.swift in Sources */, B2CA74E12A1744E300E91B24 /* FileViewerViewInput.swift in Sources */, B23477792A540D5C00F788A8 /* InfoTableSection.swift in Sources */, B28F97F92A27313E0089C032 /* UIImage.swift in Sources */, @@ -1362,6 +1444,7 @@ A4BDF4C528512FAA00651C61 /* MainViewController.swift in Sources */, B2610E242A16DCC300509FA1 /* SimpleAlertModuleConfigurator.swift in Sources */, B28F97E42A271F980089C032 /* Action.swift in Sources */, + 400C6A2D2F3F69A700678284 /* SectionScreen.swift in Sources */, 9054BA4A2A5C6CCA0009D70D /* BaseNavigationController.swift in Sources */, B2B1D4442A52DB7F00CC5A11 /* InfoTableModuleInput.swift in Sources */, B237FF652A2913DF0095FA33 /* LoggerSectionComponent.swift in Sources */, @@ -1375,6 +1458,7 @@ A4BDF4C728512FAA00651C61 /* MainModuleConfigurator.swift in Sources */, B2C65D122A4571E0004DC80D /* UITableViewCell.swift in Sources */, A4BDF49328512F7700651C61 /* Array.swift in Sources */, + 400C6A2B2F3F67C500678284 /* MenuItem.swift in Sources */, B2B1D4492A52DBA400CC5A11 /* InfoTableViewInput.swift in Sources */, A4BDF49428512F7700651C61 /* NSObject.swift in Sources */, A4D337562863A7AA002328F2 /* TableSection.swift in Sources */, @@ -1472,6 +1556,10 @@ B2E91FA42A4C2309000E3E45 /* UserDefaultsFeatureToggle.swift in Sources */, B2E91FB22A4C2309000E3E45 /* UIImage.swift in Sources */, B2E91FA72A4C2309000E3E45 /* DebugScreenAction.swift in Sources */, + 400C6A322F3F700200678284 /* DebugScreenMenuItem.swift in Sources */, + 400C6A342F3F700400678284 /* DebugScreenSectionScreen.swift in Sources */, + 400C6A362F3F700600678284 /* MenuItemsSectionBuilder.swift in Sources */, + 400C6A382F3F700800678284 /* SectionScreensSectionBuilder.swift in Sources */, B2E91FB12A4C2309000E3E45 /* UIButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/DebugScreen/Common/Models/Common/MainTableBlock.swift b/DebugScreen/Common/Models/Common/MainTableBlock.swift index 79c2488..39aa9e6 100644 --- a/DebugScreen/Common/Models/Common/MainTableBlock.swift +++ b/DebugScreen/Common/Models/Common/MainTableBlock.swift @@ -25,4 +25,8 @@ public enum MainTableBlock { case selectionList(model: SelectionList) /// Block with toggle. case toggle(model: FeatureToggle) + /// Block with a custom menu item that performs an action on tap. + case menuItem(model: MenuItem) + /// Block that opens a nested section screen with its own table sections. + case sectionScreen(model: SectionScreen) } diff --git a/DebugScreen/Common/Protocols/Content/MenuItem.swift b/DebugScreen/Common/Protocols/Content/MenuItem.swift new file mode 100644 index 0000000..1a27d09 --- /dev/null +++ b/DebugScreen/Common/Protocols/Content/MenuItem.swift @@ -0,0 +1,15 @@ +// +// MenuItem.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +public protocol MenuItem { + /// Item title text (supports multiline). + var title: String { get } + /// Whether to show a disclosure indicator arrow. + var showsDisclosure: Bool { get } + /// Action performed on tap. + var block: (() -> Void)? { get } +} diff --git a/DebugScreen/Common/Protocols/Content/SectionScreen.swift b/DebugScreen/Common/Protocols/Content/SectionScreen.swift new file mode 100644 index 0000000..774539b --- /dev/null +++ b/DebugScreen/Common/Protocols/Content/SectionScreen.swift @@ -0,0 +1,13 @@ +// +// SectionScreen.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +public protocol SectionScreen { + /// Screen title displayed in the navigation bar. + var title: String { get } + /// Sections — same ``TableSection`` items as on the main screen. + var sections: [TableSection] { get } +} diff --git a/DebugScreen/Common/ReusableLayer/Cells/PlainTextCell/PlainTextCell.swift b/DebugScreen/Common/ReusableLayer/Cells/PlainTextCell/PlainTextCell.swift index a61a406..d1ec6c4 100644 --- a/DebugScreen/Common/ReusableLayer/Cells/PlainTextCell/PlainTextCell.swift +++ b/DebugScreen/Common/ReusableLayer/Cells/PlainTextCell/PlainTextCell.swift @@ -38,7 +38,6 @@ private extension PlainTextCell { } func configureContainer() { - accessoryType = .disclosureIndicator backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor } diff --git a/DebugScreen/Flows/DebugScreenCoordinator.swift b/DebugScreen/Flows/DebugScreenCoordinator.swift index aac23da..fb12233 100644 --- a/DebugScreen/Flows/DebugScreenCoordinator.swift +++ b/DebugScreen/Flows/DebugScreenCoordinator.swift @@ -60,6 +60,9 @@ private extension DebugScreenCoordinator { output.didModuleDismissed = { [weak self] in self?.completionHandler?() } + output.onSectionScreenShow = { [weak self] model in + self?.showSectionScreen(with: model) + } router.present(view) } @@ -102,6 +105,15 @@ private extension DebugScreenCoordinator { router.present(view) } + func showSectionScreen(with model: SectionScreen) { + let (view, output) = SectionScreenModuleConfigurator().configure(with: model) + output.onActionListShow = { [weak self] model in self?.showActionList(model: model) } + output.onSectionScreenShow = { [weak self] model in self?.showSectionScreen(with: model) } + output.onInfoTableShow = { [weak self] model in self?.showInfoTable(with: model) } + output.onAlertShow = { [weak self] model in self?.showAlert(with: model) } + router.push(view) + } + func configureActionSheetItem(with model: Action) -> UIAlertAction { let style: UIAlertAction.Style = model.style == .destructive ? .destructive : .default let action = UIAlertAction(title: model.title, style: style) { _ in diff --git a/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift b/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift index 54f8dbb..7d1eaf2 100644 --- a/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift +++ b/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift @@ -18,4 +18,6 @@ protocol MainModuleOutput: AnyObject { var didModuleClosed: (() -> Void)? { get set } /// Call after module deinit from memory var didModuleDismissed: (() -> Void)? { get set } + /// Called when a section screen needs to be shown + var onSectionScreenShow: ((SectionScreen) -> Void)? { get set } } diff --git a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift index 6058a93..9550db3 100644 --- a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift +++ b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift @@ -17,6 +17,7 @@ final class MainModulePresenter: MainModuleOutput { var onInfoTableShow: ((InfoTableModel) -> Void)? var didModuleClosed: (() -> Void)? var didModuleDismissed: (() -> Void)? + var onSectionScreenShow: ((SectionScreen) -> Void)? // MARK: - Properties @@ -60,4 +61,8 @@ extension MainModulePresenter: MainViewOutput { didModuleClosed?() } + func didTapSectionScreen(model: SectionScreen) { + onSectionScreenShow?(model) + } + } diff --git a/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift b/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift index ee8df21..9f88fe6 100644 --- a/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift +++ b/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift @@ -20,6 +20,7 @@ final class MainAdapter: NSObject { var onOpenActionList: ((ActionList) -> Void)? var onSelectableTextTap: ((CopiedText) -> Void)? var onOpenInfoTable: ((InfoTableModel) -> Void)? + var onOpenSectionScreen: ((SectionScreen) -> Void)? // MARK: - Private Properties @@ -81,11 +82,23 @@ extension MainAdapter: UITableViewDataSource { case .toggle(let model): return configureSwitcherCell(tableView, indexPath: indexPath, model: model) case .copiedText(let model): - return configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) + let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) + cell.accessoryType = .disclosureIndicator + return cell case .infoTable(let model): - return configurePlainTextCell(tableView, indexPath: indexPath, title: model.header) + let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.header) + cell.accessoryType = .disclosureIndicator + return cell case .selectionList(let model): return configureSelectorCell(tableView, indexPath: indexPath, model: model) + case .menuItem(let model): + let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) + cell.accessoryType = model.showsDisclosure ? .disclosureIndicator : .none + return cell + case .sectionScreen(let model): + let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) + cell.accessoryType = .disclosureIndicator + return cell } } @@ -129,6 +142,10 @@ extension MainAdapter: UITableViewDelegate { onSelectableTextTap?(model) case .infoTable(let model): onOpenInfoTable?(model) + case .menuItem(let model): + model.block?() + case .sectionScreen(let model): + onOpenSectionScreen?(model) default: return } diff --git a/DebugScreen/Flows/MainScreen/View/MainViewController.swift b/DebugScreen/Flows/MainScreen/View/MainViewController.swift index e0f53e4..e5b8611 100644 --- a/DebugScreen/Flows/MainScreen/View/MainViewController.swift +++ b/DebugScreen/Flows/MainScreen/View/MainViewController.swift @@ -78,6 +78,9 @@ private extension MainViewController { adapter?.onSelectableTextTap = { [weak self] model in self?.output?.didTapSelectableText(model: model) } + adapter?.onOpenSectionScreen = { [weak self] model in + self?.output?.didTapSectionScreen(model: model) + } } func configureCloseButton() { diff --git a/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift b/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift index 1321088..6c00ff6 100644 --- a/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift +++ b/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift @@ -13,4 +13,5 @@ protocol MainViewOutput { func didTapInfoTable(model: InfoTableModel) func didTapSelectableText(model: CopiedText) func didTapCloseButton() + func didTapSectionScreen(model: SectionScreen) } diff --git a/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift b/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift new file mode 100644 index 0000000..99522a4 --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift @@ -0,0 +1,29 @@ +// +// SectionScreenModuleConfigurator.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import UIKit + +typealias SectionScreenModuleComponents = ( + view: UIViewController, + output: SectionScreenModuleOutput +) + +final class SectionScreenModuleConfigurator { + + // MARK: - Methods + + func configure(with model: SectionScreen) -> SectionScreenModuleComponents { + let view = UIViewController.instantiate(ofType: SectionScreenViewController.self) + let presenter = SectionScreenPresenter(model: model) + + presenter.view = view + view.output = presenter + + return (view, presenter) + } + +} diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift new file mode 100644 index 0000000..ba440a5 --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift @@ -0,0 +1,8 @@ +// +// SectionScreenModuleInput.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +protocol SectionScreenModuleInput: AnyObject { } diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift new file mode 100644 index 0000000..7884d5b --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift @@ -0,0 +1,17 @@ +// +// SectionScreenModuleOutput.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +protocol SectionScreenModuleOutput: AnyObject { + /// Called when an action list needs to be shown + var onActionListShow: ((ActionList) -> Void)? { get set } + /// Called when an alert needs to be shown + var onAlertShow: ((AlertModel) -> Void)? { get set } + /// Called when an info table screen needs to be shown + var onInfoTableShow: ((InfoTableModel) -> Void)? { get set } + /// Called when a nested section screen needs to be shown + var onSectionScreenShow: ((SectionScreen) -> Void)? { get set } +} diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift new file mode 100644 index 0000000..9d3da4f --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift @@ -0,0 +1,65 @@ +// +// SectionScreenPresenter.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import UIKit + +final class SectionScreenPresenter: SectionScreenModuleOutput { + + // MARK: - SectionScreenModuleOutput + + var onActionListShow: ((ActionList) -> Void)? + var onAlertShow: ((AlertModel) -> Void)? + var onInfoTableShow: ((InfoTableModel) -> Void)? + var onSectionScreenShow: ((SectionScreen) -> Void)? + + // MARK: - Properties + + weak var view: SectionScreenViewInput? + + // MARK: - Private Properties + + private let model: SectionScreen + + // MARK: - Initialization + + init(model: SectionScreen) { + self.model = model + } + +} + +// MARK: - SectionScreenModuleInput + +extension SectionScreenPresenter: SectionScreenModuleInput { } + +// MARK: - SectionScreenViewOutput + +extension SectionScreenPresenter: SectionScreenViewOutput { + + func viewLoaded() { + view?.setupInitialState(with: model.title, sections: model.sections) + } + + func didTapActionList(model: ActionList) { + onActionListShow?(model) + } + + func didTapInfoTable(model: InfoTableModel) { + onInfoTableShow?(model) + } + + func didTapSelectableText(model: CopiedText) { + UIPasteboard.general.string = model.value + onAlertShow?(.init(message: L10n.MainPresenter.CopyTextAction.complete)) + debugPrint("✅ \(model.title) copied to clipboard") + } + + func didTapSectionScreen(model: SectionScreen) { + onSectionScreenShow?(model) + } + +} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift new file mode 100644 index 0000000..57a17ff --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift @@ -0,0 +1,82 @@ +// +// SectionScreenViewController.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import UIKit + +final class SectionScreenViewController: UIViewController { + + // MARK: - @IBOutlets + + @IBOutlet private weak var tableView: UITableView! + + // MARK: - Properties + + var output: SectionScreenViewOutput? + + // MARK: - Private Properties + + private var adapter: MainAdapter? + + // MARK: - UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + output?.viewLoaded() + } + +} + +// MARK: - SectionScreenViewInput + +extension SectionScreenViewController: SectionScreenViewInput { + + func setupInitialState(with title: String, sections: [TableSection]) { + configureAppearance(with: title) + adapter?.fill(with: sections) + } + +} + +// MARK: - Appearance + +private extension SectionScreenViewController { + + func configureAppearance(with title: String) { + view.backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor + configureNavigationBar(with: title) + configureTableView() + configureAdapter() + } + + func configureNavigationBar(with title: String) { + navigationItem.title = title + } + + func configureTableView() { + tableView.backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor + tableView.contentInsetAdjustmentBehavior = .never + tableView.separatorStyle = .none + tableView.contentInset = .init(top: 0, left: 0, bottom: 32, right: 0) + } + + func configureAdapter() { + adapter = MainAdapter(tableView: tableView) + adapter?.onOpenActionList = { [weak self] model in + self?.output?.didTapActionList(model: model) + } + adapter?.onOpenInfoTable = { [weak self] model in + self?.output?.didTapInfoTable(model: model) + } + adapter?.onSelectableTextTap = { [weak self] model in + self?.output?.didTapSelectableText(model: model) + } + adapter?.onOpenSectionScreen = { [weak self] model in + self?.output?.didTapSectionScreen(model: model) + } + } + +} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib new file mode 100644 index 0000000..ee4ac60 --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift new file mode 100644 index 0000000..33ed83b --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift @@ -0,0 +1,10 @@ +// +// SectionScreenViewInput.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +protocol SectionScreenViewInput: AnyObject { + func setupInitialState(with title: String, sections: [TableSection]) +} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift new file mode 100644 index 0000000..349d68d --- /dev/null +++ b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift @@ -0,0 +1,14 @@ +// +// SectionScreenViewOutput.swift +// DebugScreen +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +protocol SectionScreenViewOutput { + func viewLoaded() + func didTapActionList(model: ActionList) + func didTapInfoTable(model: InfoTableModel) + func didTapSelectableText(model: CopiedText) + func didTapSectionScreen(model: SectionScreen) +} diff --git a/Example/DebugScreenExample/App/AppDelegate.swift b/Example/DebugScreenExample/App/AppDelegate.swift index e6ad83c..3c86e4b 100644 --- a/Example/DebugScreenExample/App/AppDelegate.swift +++ b/Example/DebugScreenExample/App/AppDelegate.swift @@ -56,6 +56,8 @@ private extension AppDelegate { let togglesSection = TogglesSectionBuilder().build() let copiedTextSection = CopiedTextSectionBuilder().build() let infoTablesSection = InfoTablesSectionBuilder().build() + let menuItemsSection = MenuItemsSectionBuilder().build() + let sectionScreensSection = SectionScreensSectionBuilder().build() /// You can build logger section from ready-to-use components /// or create it manually like other sections by implementing `SectionBuilder` protocol @@ -69,6 +71,8 @@ private extension AppDelegate { DebugScreenConfiguration.shared.sections = [serverSelectionSection, actionsSection, togglesSection, + menuItemsSection, + sectionScreensSection, copiedTextSection, infoTablesSection, loggerSection] diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/MenuItemsSectionBuilder.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/MenuItemsSectionBuilder.swift new file mode 100644 index 0000000..b3b1b7e --- /dev/null +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/MenuItemsSectionBuilder.swift @@ -0,0 +1,60 @@ +// +// MenuItemsSectionBuilder.swift +// Example +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import DebugScreen + +final class MenuItemsSectionBuilder: SectionBuilder { + + // MARK: - Methods + + func build() -> TableSection { + let blocks = configureMenuItemBlocks() + + return .init(title: L10n.MenuItems.header, blocks: blocks) + } + +} + +// MARK: - Private Methods + +private extension MenuItemsSectionBuilder { + + func configureMenuItemBlocks() -> [MainTableBlock] { + let simpleItem = DebugScreenMenuItem( + title: L10n.MenuItems.simpleTitle + ) { + debugPrint("✅ Simple menu item tapped") + } + + let disclosureItem = DebugScreenMenuItem( + title: L10n.MenuItems.disclosureTitle, + showsDisclosure: true + ) { + let view = DestinationViewController() + DebugScreenPresenterService.shared.showCustomScreen(view) + } + + let multilineItem = DebugScreenMenuItem( + title: L10n.MenuItems.multilineTitle, + showsDisclosure: true + ) { + debugPrint("✅ Multiline menu item tapped") + } + + let noActionItem = DebugScreenMenuItem( + title: L10n.MenuItems.noActionTitle + ) + + return [ + .menuItem(model: simpleItem), + .menuItem(model: disclosureItem), + .menuItem(model: multilineItem), + .menuItem(model: noActionItem) + ] + } + +} diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift new file mode 100644 index 0000000..faa8d82 --- /dev/null +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift @@ -0,0 +1,104 @@ +// +// SectionScreensSectionBuilder.swift +// Example +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import DebugScreen + +final class SectionScreensSectionBuilder: SectionBuilder { + + // MARK: - Methods + + func build() -> TableSection { + let blocks = configureSectionScreenBlocks() + + return .init(title: L10n.SectionScreens.header, blocks: blocks) + } + +} + +// MARK: - Private Methods + +private extension SectionScreensSectionBuilder { + + func configureSectionScreenBlocks() -> [MainTableBlock] { + let settingsScreen = configureSettingsScreen() + let nestedScreen = configureNestedScreen() + + return [ + .sectionScreen(model: settingsScreen), + .sectionScreen(model: nestedScreen) + ] + } + + /// Simple section screen with toggles and copied text items inside. + func configureSettingsScreen() -> DebugScreenSectionScreen { + let toggle1 = UserDefaultsFeatureToggle( + title: L10n.SectionScreens.Settings.firstToggleTitle, + key: UserDefaultsKeys.isNeedShowTouches + ) + let toggle2 = UserDefaultsFeatureToggle( + title: L10n.SectionScreens.Settings.secondToggleTitle, + key: UserDefaultsKeys.isNeedUseSSH + ) + let togglesSection = TableSection( + title: L10n.SectionScreens.Settings.togglesSectionTitle, + blocks: [.toggle(model: toggle1), .toggle(model: toggle2)] + ) + + let copiedTextItem = CopiedTextItem( + title: L10n.SectionScreens.Settings.copiedTextTitle, + value: "example_api_key_12345" + ) + let copiedTextSection = TableSection( + title: L10n.SectionScreens.Settings.copiedTextSectionTitle, + blocks: [.copiedText(model: copiedTextItem)] + ) + + return .init( + title: L10n.SectionScreens.settingsTitle, + sections: [togglesSection, copiedTextSection] + ) + } + + /// Nested section screen that contains another section screen inside (recursive nesting demo). + func configureNestedScreen() -> DebugScreenSectionScreen { + // Deepest level – a screen with simple menu items + let deepMenuItem = DebugScreenMenuItem( + title: L10n.SectionScreens.Nested.deepMenuItemTitle + ) { + debugPrint("✅ Deep nested menu item tapped") + } + let deepInfoTable: InfoTableModel = .appInfoTable() + let deepScreen = DebugScreenSectionScreen( + title: L10n.SectionScreens.Nested.deepScreenTitle, + sections: [ + .init(title: L10n.SectionScreens.Nested.deepMenuSectionTitle, + blocks: [.menuItem(model: deepMenuItem)]), + .init(title: L10n.SectionScreens.Nested.deepInfoSectionTitle, + blocks: [.infoTable(model: deepInfoTable)]) + ] + ) + + // Middle level – contains a link to the deepest screen and its own content + let middleMenuItem = DebugScreenMenuItem( + title: L10n.SectionScreens.Nested.middleMenuItemTitle, + showsDisclosure: true + ) { + debugPrint("✅ Middle nested menu item tapped") + } + + return .init( + title: L10n.SectionScreens.nestedTitle, + sections: [ + .init(title: L10n.SectionScreens.Nested.middleMenuSectionTitle, + blocks: [.menuItem(model: middleMenuItem)]), + .init(title: L10n.SectionScreens.Nested.nestedScreenSectionTitle, + blocks: [.sectionScreen(model: deepScreen)]) + ] + ) + } + +} diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenMenuItem.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenMenuItem.swift new file mode 100644 index 0000000..ff0c492 --- /dev/null +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenMenuItem.swift @@ -0,0 +1,26 @@ +// +// DebugScreenMenuItem.swift +// Example +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import DebugScreen + +final class DebugScreenMenuItem: MenuItem { + + // MARK: - Properties + + let title: String + let showsDisclosure: Bool + let block: (() -> Void)? + + // MARK: - Initialization + + init(title: String, showsDisclosure: Bool = false, block: (() -> Void)? = nil) { + self.title = title + self.showsDisclosure = showsDisclosure + self.block = block + } + +} diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift new file mode 100644 index 0000000..bf8c10e --- /dev/null +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift @@ -0,0 +1,24 @@ +// +// DebugScreenSectionScreen.swift +// Example +// +// Created by Aleksandr Potemkin on 13.02.2026. +// + +import DebugScreen + +final class DebugScreenSectionScreen: SectionScreen { + + // MARK: - Properties + + let title: String + let sections: [TableSection] + + // MARK: - Initialization + + init(title: String, sections: [TableSection]) { + self.title = title + self.sections = sections + } + +} diff --git a/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift b/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift index c14eaee..2d4ef32 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift +++ b/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift @@ -71,6 +71,57 @@ internal enum L10n { internal static let header = L10n.tr("Localizable", "InfoTable.header") } + internal enum MenuItems { + /// Открыть экран + internal static let disclosureTitle = L10n.tr("Localizable", "MenuItems.disclosureTitle") + /// Пункты меню + internal static let header = L10n.tr("Localizable", "MenuItems.header") + /// Многострочный пункт меню + /// с очень длинным описанием, которое занимает несколько строк + internal static let multilineTitle = L10n.tr("Localizable", "MenuItems.multilineTitle") + /// Информационный пункт (без действия) + internal static let noActionTitle = L10n.tr("Localizable", "MenuItems.noActionTitle") + /// Простое действие + internal static let simpleTitle = L10n.tr("Localizable", "MenuItems.simpleTitle") + } + + internal enum SectionScreens { + /// Вложенные экраны + internal static let header = L10n.tr("Localizable", "SectionScreens.header") + /// Вложенность экранов + internal static let nestedTitle = L10n.tr("Localizable", "SectionScreens.nestedTitle") + /// Настройки + internal static let settingsTitle = L10n.tr("Localizable", "SectionScreens.settingsTitle") + internal enum Nested { + /// Информация + internal static let deepInfoSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepInfoSectionTitle") + /// Действие глубокого уровня + internal static let deepMenuItemTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepMenuItemTitle") + /// Действия + internal static let deepMenuSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepMenuSectionTitle") + /// Глубокий уровень + internal static let deepScreenTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepScreenTitle") + /// Действие среднего уровня + internal static let middleMenuItemTitle = L10n.tr("Localizable", "SectionScreens.Nested.middleMenuItemTitle") + /// Действия + internal static let middleMenuSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.middleMenuSectionTitle") + /// Глубже + internal static let nestedScreenSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.nestedScreenSectionTitle") + } + internal enum Settings { + /// API ключи + internal static let copiedTextSectionTitle = L10n.tr("Localizable", "SectionScreens.Settings.copiedTextSectionTitle") + /// API ключ + internal static let copiedTextTitle = L10n.tr("Localizable", "SectionScreens.Settings.copiedTextTitle") + /// Показывать касания + internal static let firstToggleTitle = L10n.tr("Localizable", "SectionScreens.Settings.firstToggleTitle") + /// Использовать SSH + internal static let secondToggleTitle = L10n.tr("Localizable", "SectionScreens.Settings.secondToggleTitle") + /// Переключатели + internal static let togglesSectionTitle = L10n.tr("Localizable", "SectionScreens.Settings.togglesSectionTitle") + } + } + internal enum ServerSelection { /// Продовый internal static let firstServerTitle = L10n.tr("Localizable", "ServerSelection.firstServerTitle") diff --git a/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings b/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings index 63d20e4..ca773c8 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings +++ b/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings @@ -47,6 +47,32 @@ "InfoTable.AboutDevice" = "Device Info"; "InfoTable.Custom" = "Custom Data"; +// MARK: - MenuItems + +"MenuItems.header" = "Menu Items"; +"MenuItems.simpleTitle" = "Simple action"; +"MenuItems.disclosureTitle" = "Open Screen"; +"MenuItems.multilineTitle" = "This is a multiline menu item\nwith a very long description that spans multiple lines"; +"MenuItems.noActionTitle" = "Informational item (no action)"; + +// MARK: - SectionScreens + +"SectionScreens.header" = "Section Screens"; +"SectionScreens.settingsTitle" = "Settings"; +"SectionScreens.nestedTitle" = "Nested Screens"; +"SectionScreens.Settings.togglesSectionTitle" = "Toggles"; +"SectionScreens.Settings.firstToggleTitle" = "Show touches"; +"SectionScreens.Settings.secondToggleTitle" = "Use SSH"; +"SectionScreens.Settings.copiedTextSectionTitle" = "API Keys"; +"SectionScreens.Settings.copiedTextTitle" = "API Key"; +"SectionScreens.Nested.middleMenuSectionTitle" = "Actions"; +"SectionScreens.Nested.middleMenuItemTitle" = "Middle level action"; +"SectionScreens.Nested.nestedScreenSectionTitle" = "Go Deeper"; +"SectionScreens.Nested.deepScreenTitle" = "Deep Level"; +"SectionScreens.Nested.deepMenuSectionTitle" = "Deep Actions"; +"SectionScreens.Nested.deepMenuItemTitle" = "Deep nested action"; +"SectionScreens.Nested.deepInfoSectionTitle" = "Info"; + // MARK: - Destination Controller "DestinationController.title" = "Destination controller"; diff --git a/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings b/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings index 4924b45..25baf83 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings +++ b/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings @@ -47,6 +47,32 @@ "InfoTable.AboutDevice" = "Информация об устройстве"; "InfoTable.Custom" = "Произвольные данные"; +// MARK: - MenuItems + +"MenuItems.header" = "Пункты меню"; +"MenuItems.simpleTitle" = "Простое действие"; +"MenuItems.disclosureTitle" = "Открыть экран"; +"MenuItems.multilineTitle" = "Многострочный пункт меню\nс очень длинным описанием, которое занимает несколько строк"; +"MenuItems.noActionTitle" = "Информационный пункт (без действия)"; + +// MARK: - SectionScreens + +"SectionScreens.header" = "Вложенные экраны"; +"SectionScreens.settingsTitle" = "Настройки"; +"SectionScreens.nestedTitle" = "Вложенность экранов"; +"SectionScreens.Settings.togglesSectionTitle" = "Переключатели"; +"SectionScreens.Settings.firstToggleTitle" = "Показывать касания"; +"SectionScreens.Settings.secondToggleTitle" = "Использовать SSH"; +"SectionScreens.Settings.copiedTextSectionTitle" = "API ключи"; +"SectionScreens.Settings.copiedTextTitle" = "API ключ"; +"SectionScreens.Nested.middleMenuSectionTitle" = "Действия"; +"SectionScreens.Nested.middleMenuItemTitle" = "Действие среднего уровня"; +"SectionScreens.Nested.nestedScreenSectionTitle" = "Глубже"; +"SectionScreens.Nested.deepScreenTitle" = "Глубокий уровень"; +"SectionScreens.Nested.deepMenuSectionTitle" = "Действия"; +"SectionScreens.Nested.deepMenuItemTitle" = "Действие глубокого уровня"; +"SectionScreens.Nested.deepInfoSectionTitle" = "Информация"; + // MARK: - Destination Controller "DestinationController.title" = "Новый экран"; From 4f21e25e8177901b2afc1dce4ad081be9969d755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D1=82=D1=91=D0=BC=D0=BA=D0=B8=D0=BD=20=D0=90?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Wed, 18 Feb 2026 15:22:08 +0300 Subject: [PATCH 2/3] Remove SectionScreen module, change sectionScreen name to nestedScreen --- DebugScreen.xcodeproj/project.pbxproj | 100 ++++-------------- .../Common/Models/Common/MainTableBlock.swift | 2 +- ...SectionScreen.swift => NestedScreen.swift} | 4 +- .../Flows/DebugScreenCoordinator.swift | 41 ++++--- .../Configurator/MainModuleConfigurator.swift | 28 +++-- .../Configurator/MainScreenType.swift | 13 +++ .../Presenter/MainModuleOutput.swift | 4 +- .../Presenter/MainModulePresenter.swift | 31 ++++-- .../MainScreen/View/Adapter/MainAdapter.swift | 8 +- .../MainScreen/View/MainViewController.swift | 16 +-- .../Flows/MainScreen/View/MainViewInput.swift | 2 +- .../MainScreen/View/MainViewOutput.swift | 2 +- .../SectionScreenModuleConfigurator.swift | 29 ----- .../Presenter/SectionScreenModuleInput.swift | 8 -- .../Presenter/SectionScreenModuleOutput.swift | 17 --- .../Presenter/SectionScreenPresenter.swift | 65 ------------ .../View/SectionScreenViewController.swift | 82 -------------- .../View/SectionScreenViewController.xib | 45 -------- .../View/SectionScreenViewInput.swift | 10 -- .../View/SectionScreenViewOutput.swift | 14 --- .../DebugScreenExample/App/AppDelegate.swift | 4 +- ...wift => NestedScreensSectionBuilder.swift} | 53 +++++----- ...en.swift => DebugScreenNestedScreen.swift} | 4 +- .../Library/Resources/Strings/Strings.swift | 32 +++--- .../Strings/en.lproj/Localizable.strings | 34 +++--- .../Strings/ru.lproj/Localizable.strings | 34 +++--- 26 files changed, 203 insertions(+), 479 deletions(-) rename DebugScreen/Common/Protocols/Content/{SectionScreen.swift => NestedScreen.swift} (83%) create mode 100644 DebugScreen/Flows/MainScreen/Configurator/MainScreenType.swift delete mode 100644 DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift delete mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift delete mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift delete mode 100644 DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift delete mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift delete mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib delete mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift delete mode 100644 DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift rename Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/{SectionScreensSectionBuilder.swift => NestedScreensSectionBuilder.swift} (53%) rename Example/DebugScreenExample/App/DebugScreenConfiguration/Models/{DebugScreenSectionScreen.swift => DebugScreenNestedScreen.swift} (78%) diff --git a/DebugScreen.xcodeproj/project.pbxproj b/DebugScreen.xcodeproj/project.pbxproj index 7d84889..2de39b9 100644 --- a/DebugScreen.xcodeproj/project.pbxproj +++ b/DebugScreen.xcodeproj/project.pbxproj @@ -8,17 +8,14 @@ /* Begin PBXBuildFile section */ 400C6A2B2F3F67C500678284 /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2A2F3F67C500678284 /* MenuItem.swift */; }; - 400C6A2D2F3F69A700678284 /* SectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2C2F3F69A700678284 /* SectionScreen.swift */; }; - 400C6A302F3F6D9700678284 /* SectionScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */; }; - 400C6A412F3F800000678284 /* SectionScreenModuleConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */; }; - 400C6A432F3F800100678284 /* SectionScreenModuleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */; }; - 400C6A452F3F800200678284 /* SectionScreenModuleOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */; }; - 400C6A472F3F800300678284 /* SectionScreenPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */; }; - 400C6A492F3F800400678284 /* SectionScreenViewInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */; }; - 400C6A4B2F3F800500678284 /* SectionScreenViewOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */; }; - 400C6A4D2F3F800600678284 /* SectionScreenViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */; }; + 400C6A2D2F3F69A700678284 /* NestedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A2C2F3F69A700678284 /* NestedScreen.swift */; }; + 400C6A322F3F700200678284 /* DebugScreenMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */; }; + 400C6A342F3F700400678284 /* DebugScreenNestedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A332F3F700300678284 /* DebugScreenNestedScreen.swift */; }; + 400C6A362F3F700600678284 /* MenuItemsSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */; }; + 400C6A382F3F700800678284 /* NestedScreensSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A372F3F700700678284 /* NestedScreensSectionBuilder.swift */; }; 9054BA4A2A5C6CCA0009D70D /* BaseNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9054BA492A5C6CCA0009D70D /* BaseNavigationController.swift */; }; 90985F5C2A5C58980066790A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2E91F9A2A4C2308000E3E45 /* LaunchScreen.storyboard */; }; + 90C0C14C2F45CC4300D3C438 /* MainScreenType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C0C14B2F45CC3D00D3C438 /* MainScreenType.swift */; }; A4301A65285A462F006B77C0 /* DebugScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A43019E6285A3137006B77C0 /* DebugScreenTests.swift */; }; A47BDB42286A1249002326B8 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A47BDB38286A1249002326B8 /* Colors.xcassets */; }; A47BDB43286A1249002326B8 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47BDB39286A1249002326B8 /* Colors.swift */; }; @@ -115,10 +112,6 @@ B2CA74E32A1744F300E91B24 /* FileViewerViewOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CA74E22A1744F300E91B24 /* FileViewerViewOutput.swift */; }; B2CA74E82A175D7700E91B24 /* FileViewerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CA74E62A175D7700E91B24 /* FileViewerViewController.swift */; }; B2CA74E92A175D7700E91B24 /* FileViewerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B2CA74E72A175D7700E91B24 /* FileViewerViewController.xib */; }; - 400C6A322F3F700200678284 /* DebugScreenMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */; }; - 400C6A342F3F700400678284 /* DebugScreenSectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */; }; - 400C6A362F3F700600678284 /* MenuItemsSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */; }; - 400C6A382F3F700800678284 /* SectionScreensSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */; }; B2E91F9D2A4C2308000E3E45 /* UserDefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F702A4C2304000E3E45 /* UserDefaultsKeys.swift */; }; B2E91F9E2A4C2308000E3E45 /* UserDefaultsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F712A4C2304000E3E45 /* UserDefaultsService.swift */; }; B2E91F9F2A4C2308000E3E45 /* DestinationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E91F722A4C2304000E3E45 /* DestinationViewController.swift */; }; @@ -189,16 +182,13 @@ /* Begin PBXFileReference section */ 400C6A2A2F3F67C500678284 /* MenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = ""; }; - 400C6A2C2F3F69A700678284 /* SectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreen.swift; sourceTree = ""; }; - 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewController.swift; sourceTree = ""; }; - 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleConfigurator.swift; sourceTree = ""; }; - 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleInput.swift; sourceTree = ""; }; - 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenModuleOutput.swift; sourceTree = ""; }; - 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenPresenter.swift; sourceTree = ""; }; - 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewInput.swift; sourceTree = ""; }; - 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreenViewOutput.swift; sourceTree = ""; }; - 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionScreenViewController.xib; sourceTree = ""; }; + 400C6A2C2F3F69A700678284 /* NestedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedScreen.swift; sourceTree = ""; }; + 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenMenuItem.swift; sourceTree = ""; }; + 400C6A332F3F700300678284 /* DebugScreenNestedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenNestedScreen.swift; sourceTree = ""; }; + 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemsSectionBuilder.swift; sourceTree = ""; }; + 400C6A372F3F700700678284 /* NestedScreensSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedScreensSectionBuilder.swift; sourceTree = ""; }; 9054BA492A5C6CCA0009D70D /* BaseNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNavigationController.swift; sourceTree = ""; }; + 90C0C14B2F45CC3D00D3C438 /* MainScreenType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreenType.swift; sourceTree = ""; }; A43019E6285A3137006B77C0 /* DebugScreenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugScreenTests.swift; sourceTree = ""; }; A47BDB38286A1249002326B8 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; A47BDB39286A1249002326B8 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; @@ -299,10 +289,6 @@ B2CA74E62A175D7700E91B24 /* FileViewerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileViewerViewController.swift; sourceTree = ""; }; B2CA74E72A175D7700E91B24 /* FileViewerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FileViewerViewController.xib; sourceTree = ""; }; B2E91F582A4C21E9000E3E45 /* DebugScreenExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DebugScreenExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenMenuItem.swift; sourceTree = ""; }; - 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreenSectionScreen.swift; sourceTree = ""; }; - 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemsSectionBuilder.swift; sourceTree = ""; }; - 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionScreensSectionBuilder.swift; sourceTree = ""; }; B2E91F702A4C2304000E3E45 /* UserDefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsKeys.swift; sourceTree = ""; }; B2E91F712A4C2304000E3E45 /* UserDefaultsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsService.swift; sourceTree = ""; }; B2E91F722A4C2304000E3E45 /* DestinationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DestinationViewController.swift; sourceTree = ""; }; @@ -377,45 +363,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 400C6A2E2F3F6D6E00678284 /* SectionScreen */ = { - isa = PBXGroup; - children = ( - 400C6A4E2F3F800700678284 /* Configurator */, - 400C6A4F2F3F800800678284 /* Presenter */, - 400C6A502F3F800900678284 /* View */, - ); - path = SectionScreen; - sourceTree = ""; - }; - 400C6A4E2F3F800700678284 /* Configurator */ = { - isa = PBXGroup; - children = ( - 400C6A402F3F800000678284 /* SectionScreenModuleConfigurator.swift */, - ); - path = Configurator; - sourceTree = ""; - }; - 400C6A4F2F3F800800678284 /* Presenter */ = { - isa = PBXGroup; - children = ( - 400C6A422F3F800100678284 /* SectionScreenModuleInput.swift */, - 400C6A442F3F800200678284 /* SectionScreenModuleOutput.swift */, - 400C6A462F3F800300678284 /* SectionScreenPresenter.swift */, - ); - path = Presenter; - sourceTree = ""; - }; - 400C6A502F3F800900678284 /* View */ = { - isa = PBXGroup; - children = ( - 400C6A2F2F3F6D9700678284 /* SectionScreenViewController.swift */, - 400C6A4C2F3F800600678284 /* SectionScreenViewController.xib */, - 400C6A482F3F800400678284 /* SectionScreenViewInput.swift */, - 400C6A4A2F3F800500678284 /* SectionScreenViewOutput.swift */, - ); - path = View; - sourceTree = ""; - }; A43019E5285A3137006B77C0 /* DebugScreenTests */ = { isa = PBXGroup; children = ( @@ -571,7 +518,6 @@ isa = PBXGroup; children = ( A4BDF4B828512FAA00651C61 /* DebugScreenCoordinator.swift */, - 400C6A2E2F3F6D6E00678284 /* SectionScreen */, B2610E1F2A16D90600509FA1 /* FileViewer */, B2B1D43C2A52DB3200CC5A11 /* InfoTable */, A4BDF4A028512FAA00651C61 /* MainScreen */, @@ -633,6 +579,7 @@ A4BDF4B628512FAA00651C61 /* Configurator */ = { isa = PBXGroup; children = ( + 90C0C14B2F45CC3D00D3C438 /* MainScreenType.swift */, A4BDF4B728512FAA00651C61 /* MainModuleConfigurator.swift */, ); path = Configurator; @@ -779,7 +726,7 @@ B28F97E72A271FAF0089C032 /* FeatureToggle.swift */, B28F97EB2A271FF30089C032 /* SelectionList.swift */, 400C6A2A2F3F67C500678284 /* MenuItem.swift */, - 400C6A2C2F3F69A700678284 /* SectionScreen.swift */, + 400C6A2C2F3F69A700678284 /* NestedScreen.swift */, ); path = Content; sourceTree = ""; @@ -1027,7 +974,7 @@ B2E91F792A4C2305000E3E45 /* CopiedTextSectionBuilder.swift */, B2B1D4502A52FC3000CC5A11 /* InfoTablesSectionBuilder.swift */, 400C6A352F3F700500678284 /* MenuItemsSectionBuilder.swift */, - 400C6A372F3F700700678284 /* SectionScreensSectionBuilder.swift */, + 400C6A372F3F700700678284 /* NestedScreensSectionBuilder.swift */, B2E91F7A2A4C2305000E3E45 /* ServerSelectionSectionBuilder.swift */, B2E91F782A4C2305000E3E45 /* TogglesSectionBuilder.swift */, ); @@ -1042,7 +989,7 @@ B2E91F7E2A4C2305000E3E45 /* DebugScreenActionList.swift */, B2E91F7F2A4C2305000E3E45 /* DebugScreenAction.swift */, 400C6A312F3F700100678284 /* DebugScreenMenuItem.swift */, - 400C6A332F3F700300678284 /* DebugScreenSectionScreen.swift */, + 400C6A332F3F700300678284 /* DebugScreenNestedScreen.swift */, B2E91F802A4C2305000E3E45 /* ServersSelectionList.swift */, ); path = Models; @@ -1306,7 +1253,6 @@ A47BDB45286A1249002326B8 /* Assets.xcassets in Resources */, B28F97F42A2728560089C032 /* ButtonCell.xib in Resources */, A4BDF4C328512FAA00651C61 /* MainViewController.xib in Resources */, - 400C6A4D2F3F800600678284 /* SectionScreenViewController.xib in Resources */, B2CA74E92A175D7700E91B24 /* FileViewerViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1429,22 +1375,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 400C6A302F3F6D9700678284 /* SectionScreenViewController.swift in Sources */, - 400C6A412F3F800000678284 /* SectionScreenModuleConfigurator.swift in Sources */, - 400C6A432F3F800100678284 /* SectionScreenModuleInput.swift in Sources */, - 400C6A452F3F800200678284 /* SectionScreenModuleOutput.swift in Sources */, - 400C6A472F3F800300678284 /* SectionScreenPresenter.swift in Sources */, - 400C6A492F3F800400678284 /* SectionScreenViewInput.swift in Sources */, - 400C6A4B2F3F800500678284 /* SectionScreenViewOutput.swift in Sources */, B2CA74E12A1744E300E91B24 /* FileViewerViewInput.swift in Sources */, B23477792A540D5C00F788A8 /* InfoTableSection.swift in Sources */, B28F97F92A27313E0089C032 /* UIImage.swift in Sources */, A47BDB43286A1249002326B8 /* Colors.swift in Sources */, A4BDF49828512F7700651C61 /* UIApplication.swift in Sources */, + 90C0C14C2F45CC4300D3C438 /* MainScreenType.swift in Sources */, A4BDF4C528512FAA00651C61 /* MainViewController.swift in Sources */, B2610E242A16DCC300509FA1 /* SimpleAlertModuleConfigurator.swift in Sources */, B28F97E42A271F980089C032 /* Action.swift in Sources */, - 400C6A2D2F3F69A700678284 /* SectionScreen.swift in Sources */, + 400C6A2D2F3F69A700678284 /* NestedScreen.swift in Sources */, 9054BA4A2A5C6CCA0009D70D /* BaseNavigationController.swift in Sources */, B2B1D4442A52DB7F00CC5A11 /* InfoTableModuleInput.swift in Sources */, B237FF652A2913DF0095FA33 /* LoggerSectionComponent.swift in Sources */, @@ -1557,9 +1497,9 @@ B2E91FB22A4C2309000E3E45 /* UIImage.swift in Sources */, B2E91FA72A4C2309000E3E45 /* DebugScreenAction.swift in Sources */, 400C6A322F3F700200678284 /* DebugScreenMenuItem.swift in Sources */, - 400C6A342F3F700400678284 /* DebugScreenSectionScreen.swift in Sources */, + 400C6A342F3F700400678284 /* DebugScreenNestedScreen.swift in Sources */, 400C6A362F3F700600678284 /* MenuItemsSectionBuilder.swift in Sources */, - 400C6A382F3F700800678284 /* SectionScreensSectionBuilder.swift in Sources */, + 400C6A382F3F700800678284 /* NestedScreensSectionBuilder.swift in Sources */, B2E91FB12A4C2309000E3E45 /* UIButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/DebugScreen/Common/Models/Common/MainTableBlock.swift b/DebugScreen/Common/Models/Common/MainTableBlock.swift index 39aa9e6..440ccc5 100644 --- a/DebugScreen/Common/Models/Common/MainTableBlock.swift +++ b/DebugScreen/Common/Models/Common/MainTableBlock.swift @@ -28,5 +28,5 @@ public enum MainTableBlock { /// Block with a custom menu item that performs an action on tap. case menuItem(model: MenuItem) /// Block that opens a nested section screen with its own table sections. - case sectionScreen(model: SectionScreen) + case nestedScreen(model: NestedScreen) } diff --git a/DebugScreen/Common/Protocols/Content/SectionScreen.swift b/DebugScreen/Common/Protocols/Content/NestedScreen.swift similarity index 83% rename from DebugScreen/Common/Protocols/Content/SectionScreen.swift rename to DebugScreen/Common/Protocols/Content/NestedScreen.swift index 774539b..02b5c26 100644 --- a/DebugScreen/Common/Protocols/Content/SectionScreen.swift +++ b/DebugScreen/Common/Protocols/Content/NestedScreen.swift @@ -1,11 +1,11 @@ // -// SectionScreen.swift +// NestedScreen.swift // DebugScreen // // Created by Aleksandr Potemkin on 13.02.2026. // -public protocol SectionScreen { +public protocol NestedScreen { /// Screen title displayed in the navigation bar. var title: String { get } /// Sections — same ``TableSection`` items as on the main screen. diff --git a/DebugScreen/Flows/DebugScreenCoordinator.swift b/DebugScreen/Flows/DebugScreenCoordinator.swift index fb12233..6d244a9 100644 --- a/DebugScreen/Flows/DebugScreenCoordinator.swift +++ b/DebugScreen/Flows/DebugScreenCoordinator.swift @@ -21,7 +21,7 @@ final class DebugScreenCoordinator: BaseCoordinator { // MARK: - Methods override func start() { - showMainScreen() + showMainScreen(type: .root) } func open(module: ModuleType) { @@ -43,8 +43,15 @@ final class DebugScreenCoordinator: BaseCoordinator { private extension DebugScreenCoordinator { - func showMainScreen() { - let (view, output) = MainModuleConfigurator().configure() + func showMainScreen(type: MainScreenType) { + let (view, output): MainModuleComponents + switch type { + case .root: + (view, output) = MainModuleConfigurator().configureMain() + case .nested(let model): + (view, output) = MainModuleConfigurator().configureNestedModule(model: model) + } + output.onActionListShow = { [weak self] model in self?.showActionList(model: model) } @@ -54,16 +61,25 @@ private extension DebugScreenCoordinator { output.onInfoTableShow = { [weak self] model in self?.showInfoTable(with: model) } + output.onNestedScreenShow = { [weak self] model in + self?.showMainScreen(type: .nested(model: model)) + } output.didModuleClosed = { [weak self] in self?.router.dismissModule() } - output.didModuleDismissed = { [weak self] in - self?.completionHandler?() + + if case .root = type { + output.didModuleDismissed = { [weak self] in + self?.completionHandler?() + } } - output.onSectionScreenShow = { [weak self] model in - self?.showSectionScreen(with: model) + + switch type { + case .root: + router.present(view) + case .nested(_): + router.push(view) } - router.present(view) } func showActionList(model: ActionList) { @@ -105,15 +121,6 @@ private extension DebugScreenCoordinator { router.present(view) } - func showSectionScreen(with model: SectionScreen) { - let (view, output) = SectionScreenModuleConfigurator().configure(with: model) - output.onActionListShow = { [weak self] model in self?.showActionList(model: model) } - output.onSectionScreenShow = { [weak self] model in self?.showSectionScreen(with: model) } - output.onInfoTableShow = { [weak self] model in self?.showInfoTable(with: model) } - output.onAlertShow = { [weak self] model in self?.showAlert(with: model) } - router.push(view) - } - func configureActionSheetItem(with model: Action) -> UIAlertAction { let style: UIAlertAction.Style = model.style == .destructive ? .destructive : .default let action = UIAlertAction(title: model.title, style: style) { _ in diff --git a/DebugScreen/Flows/MainScreen/Configurator/MainModuleConfigurator.swift b/DebugScreen/Flows/MainScreen/Configurator/MainModuleConfigurator.swift index 3236790..6af2a91 100644 --- a/DebugScreen/Flows/MainScreen/Configurator/MainModuleConfigurator.swift +++ b/DebugScreen/Flows/MainScreen/Configurator/MainModuleConfigurator.swift @@ -13,17 +13,33 @@ final class MainModuleConfigurator { // MARK: - Methods - func configure() -> MainModuleComponents { - let viewController = UIViewController.instantiate(ofType: MainViewController.self) - let presenter = MainModulePresenter() + func configureMain() -> MainModuleComponents { + let view = UIViewController.instantiate(ofType: MainViewController.self) + let presenter = MainModulePresenter( + title: L10n.MainViewController.debugTitle, + sections: DebugScreenConfiguration.shared.sections + ) - presenter.view = viewController - viewController.output = presenter + presenter.view = view + view.output = presenter - let navController = BaseNavigationController(rootViewController: viewController) + let navController = BaseNavigationController(rootViewController: view) navController.modalPresentationStyle = .overFullScreen return (navController, presenter) } + func configureNestedModule(model: NestedScreen) -> MainModuleComponents { + let view = UIViewController.instantiate(ofType: MainViewController.self) + let presenter = MainModulePresenter( + title: model.title, + sections: model.sections + ) + + presenter.view = view + view.output = presenter + + return (view, presenter) + } + } diff --git a/DebugScreen/Flows/MainScreen/Configurator/MainScreenType.swift b/DebugScreen/Flows/MainScreen/Configurator/MainScreenType.swift new file mode 100644 index 0000000..815edeb --- /dev/null +++ b/DebugScreen/Flows/MainScreen/Configurator/MainScreenType.swift @@ -0,0 +1,13 @@ +// +// MainScreenType.swift +// DebugScreen +// +// Created by chausov on 18.02.2026. +// + +enum MainScreenType { + /// Main debug screen + case root + /// Nested debug screen + case nested(model: NestedScreen) +} diff --git a/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift b/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift index 7d1eaf2..895e3ae 100644 --- a/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift +++ b/DebugScreen/Flows/MainScreen/Presenter/MainModuleOutput.swift @@ -14,10 +14,10 @@ protocol MainModuleOutput: AnyObject { var onAlertShow: ((AlertModel) -> Void)? { get set } /// Call then show information table screen var onInfoTableShow: ((InfoTableModel) -> Void)? { get set } + /// Called when a section screen needs to be shown + var onNestedScreenShow: ((NestedScreen) -> Void)? { get set } /// Call then close module var didModuleClosed: (() -> Void)? { get set } /// Call after module deinit from memory var didModuleDismissed: (() -> Void)? { get set } - /// Called when a section screen needs to be shown - var onSectionScreenShow: ((SectionScreen) -> Void)? { get set } } diff --git a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift index 9550db3..d2ae89b 100644 --- a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift +++ b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift @@ -15,14 +15,26 @@ final class MainModulePresenter: MainModuleOutput { var onActionListShow: ((ActionList) -> Void)? var onAlertShow: ((AlertModel) -> Void)? var onInfoTableShow: ((InfoTableModel) -> Void)? + var onNestedScreenShow: ((NestedScreen) -> Void)? var didModuleClosed: (() -> Void)? var didModuleDismissed: (() -> Void)? - var onSectionScreenShow: ((SectionScreen) -> Void)? // MARK: - Properties weak var view: MainViewInput? + // MARK: - Private Properties + + private let title: String + private let sections: [TableSection] + + // MARK: - Initialization + + init(title: String, sections: [TableSection]) { + self.title = title + self.sections = sections + } + // MARK: - Deinitialization deinit { @@ -40,7 +52,14 @@ extension MainModulePresenter: MainModuleInput { } extension MainModulePresenter: MainViewOutput { func viewLoaded() { - view?.setupInitialState(sections: DebugScreenConfiguration.shared.sections) + view?.setupInitialState( + title: title, + sections: sections + ) +// view?.setupInitialState( +// title: L10n.MainViewController.debugTitle, +// sections: DebugScreenConfiguration.shared.sections +// ) } func didTapActionList(model: ActionList) { @@ -57,12 +76,12 @@ extension MainModulePresenter: MainViewOutput { debugPrint("✅ \(model.title) copied to clipboard") } - func didTapCloseButton() { - didModuleClosed?() + func didTapNestedScreen(model: NestedScreen) { + onNestedScreenShow?(model) } - func didTapSectionScreen(model: SectionScreen) { - onSectionScreenShow?(model) + func didTapCloseButton() { + didModuleClosed?() } } diff --git a/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift b/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift index 9f88fe6..8c423f2 100644 --- a/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift +++ b/DebugScreen/Flows/MainScreen/View/Adapter/MainAdapter.swift @@ -20,7 +20,7 @@ final class MainAdapter: NSObject { var onOpenActionList: ((ActionList) -> Void)? var onSelectableTextTap: ((CopiedText) -> Void)? var onOpenInfoTable: ((InfoTableModel) -> Void)? - var onOpenSectionScreen: ((SectionScreen) -> Void)? + var onOpenNestedScreen: ((NestedScreen) -> Void)? // MARK: - Private Properties @@ -95,7 +95,7 @@ extension MainAdapter: UITableViewDataSource { let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) cell.accessoryType = model.showsDisclosure ? .disclosureIndicator : .none return cell - case .sectionScreen(let model): + case .nestedScreen(let model): let cell = configurePlainTextCell(tableView, indexPath: indexPath, title: model.title) cell.accessoryType = .disclosureIndicator return cell @@ -144,8 +144,8 @@ extension MainAdapter: UITableViewDelegate { onOpenInfoTable?(model) case .menuItem(let model): model.block?() - case .sectionScreen(let model): - onOpenSectionScreen?(model) + case .nestedScreen(let model): + onOpenNestedScreen?(model) default: return } diff --git a/DebugScreen/Flows/MainScreen/View/MainViewController.swift b/DebugScreen/Flows/MainScreen/View/MainViewController.swift index e5b8611..dda1e67 100644 --- a/DebugScreen/Flows/MainScreen/View/MainViewController.swift +++ b/DebugScreen/Flows/MainScreen/View/MainViewController.swift @@ -34,8 +34,8 @@ final class MainViewController: UIViewController { extension MainViewController: MainViewInput { - func setupInitialState(sections: [TableSection]) { - configureAppearance() + func setupInitialState(title: String, sections: [TableSection]) { + configureAppearance(title: title) adapter?.fill(with: sections) } @@ -45,15 +45,15 @@ extension MainViewController: MainViewInput { private extension MainViewController { - func configureAppearance() { + func configureAppearance(title: String) { view.backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor - configureNavigationBar() + configureNavigationBar(title: title) configureTableView() configureAdapter() } - func configureNavigationBar() { - navigationItem.title = L10n.MainViewController.debugTitle + func configureNavigationBar(title: String) { + navigationItem.title = title configureCloseButton() } @@ -78,8 +78,8 @@ private extension MainViewController { adapter?.onSelectableTextTap = { [weak self] model in self?.output?.didTapSelectableText(model: model) } - adapter?.onOpenSectionScreen = { [weak self] model in - self?.output?.didTapSectionScreen(model: model) + adapter?.onOpenNestedScreen = { [weak self] model in + self?.output?.didTapNestedScreen(model: model) } } diff --git a/DebugScreen/Flows/MainScreen/View/MainViewInput.swift b/DebugScreen/Flows/MainScreen/View/MainViewInput.swift index 81d51bb..c9ab392 100644 --- a/DebugScreen/Flows/MainScreen/View/MainViewInput.swift +++ b/DebugScreen/Flows/MainScreen/View/MainViewInput.swift @@ -8,5 +8,5 @@ import Foundation protocol MainViewInput: AnyObject { - func setupInitialState(sections: [TableSection]) + func setupInitialState(title: String, sections: [TableSection]) } diff --git a/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift b/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift index 6c00ff6..0f8afd5 100644 --- a/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift +++ b/DebugScreen/Flows/MainScreen/View/MainViewOutput.swift @@ -12,6 +12,6 @@ protocol MainViewOutput { func didTapActionList(model: ActionList) func didTapInfoTable(model: InfoTableModel) func didTapSelectableText(model: CopiedText) + func didTapNestedScreen(model: NestedScreen) func didTapCloseButton() - func didTapSectionScreen(model: SectionScreen) } diff --git a/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift b/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift deleted file mode 100644 index 99522a4..0000000 --- a/DebugScreen/Flows/SectionScreen/Configurator/SectionScreenModuleConfigurator.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// SectionScreenModuleConfigurator.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -import UIKit - -typealias SectionScreenModuleComponents = ( - view: UIViewController, - output: SectionScreenModuleOutput -) - -final class SectionScreenModuleConfigurator { - - // MARK: - Methods - - func configure(with model: SectionScreen) -> SectionScreenModuleComponents { - let view = UIViewController.instantiate(ofType: SectionScreenViewController.self) - let presenter = SectionScreenPresenter(model: model) - - presenter.view = view - view.output = presenter - - return (view, presenter) - } - -} diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift deleted file mode 100644 index ba440a5..0000000 --- a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleInput.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// SectionScreenModuleInput.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -protocol SectionScreenModuleInput: AnyObject { } diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift deleted file mode 100644 index 7884d5b..0000000 --- a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenModuleOutput.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// SectionScreenModuleOutput.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -protocol SectionScreenModuleOutput: AnyObject { - /// Called when an action list needs to be shown - var onActionListShow: ((ActionList) -> Void)? { get set } - /// Called when an alert needs to be shown - var onAlertShow: ((AlertModel) -> Void)? { get set } - /// Called when an info table screen needs to be shown - var onInfoTableShow: ((InfoTableModel) -> Void)? { get set } - /// Called when a nested section screen needs to be shown - var onSectionScreenShow: ((SectionScreen) -> Void)? { get set } -} diff --git a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift b/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift deleted file mode 100644 index 9d3da4f..0000000 --- a/DebugScreen/Flows/SectionScreen/Presenter/SectionScreenPresenter.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// SectionScreenPresenter.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -import UIKit - -final class SectionScreenPresenter: SectionScreenModuleOutput { - - // MARK: - SectionScreenModuleOutput - - var onActionListShow: ((ActionList) -> Void)? - var onAlertShow: ((AlertModel) -> Void)? - var onInfoTableShow: ((InfoTableModel) -> Void)? - var onSectionScreenShow: ((SectionScreen) -> Void)? - - // MARK: - Properties - - weak var view: SectionScreenViewInput? - - // MARK: - Private Properties - - private let model: SectionScreen - - // MARK: - Initialization - - init(model: SectionScreen) { - self.model = model - } - -} - -// MARK: - SectionScreenModuleInput - -extension SectionScreenPresenter: SectionScreenModuleInput { } - -// MARK: - SectionScreenViewOutput - -extension SectionScreenPresenter: SectionScreenViewOutput { - - func viewLoaded() { - view?.setupInitialState(with: model.title, sections: model.sections) - } - - func didTapActionList(model: ActionList) { - onActionListShow?(model) - } - - func didTapInfoTable(model: InfoTableModel) { - onInfoTableShow?(model) - } - - func didTapSelectableText(model: CopiedText) { - UIPasteboard.general.string = model.value - onAlertShow?(.init(message: L10n.MainPresenter.CopyTextAction.complete)) - debugPrint("✅ \(model.title) copied to clipboard") - } - - func didTapSectionScreen(model: SectionScreen) { - onSectionScreenShow?(model) - } - -} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift deleted file mode 100644 index 57a17ff..0000000 --- a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// SectionScreenViewController.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -import UIKit - -final class SectionScreenViewController: UIViewController { - - // MARK: - @IBOutlets - - @IBOutlet private weak var tableView: UITableView! - - // MARK: - Properties - - var output: SectionScreenViewOutput? - - // MARK: - Private Properties - - private var adapter: MainAdapter? - - // MARK: - UIViewController - - override func viewDidLoad() { - super.viewDidLoad() - output?.viewLoaded() - } - -} - -// MARK: - SectionScreenViewInput - -extension SectionScreenViewController: SectionScreenViewInput { - - func setupInitialState(with title: String, sections: [TableSection]) { - configureAppearance(with: title) - adapter?.fill(with: sections) - } - -} - -// MARK: - Appearance - -private extension SectionScreenViewController { - - func configureAppearance(with title: String) { - view.backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor - configureNavigationBar(with: title) - configureTableView() - configureAdapter() - } - - func configureNavigationBar(with title: String) { - navigationItem.title = title - } - - func configureTableView() { - tableView.backgroundColor = DebugScreenConfiguration.shared.colorScheme.backgroundColor - tableView.contentInsetAdjustmentBehavior = .never - tableView.separatorStyle = .none - tableView.contentInset = .init(top: 0, left: 0, bottom: 32, right: 0) - } - - func configureAdapter() { - adapter = MainAdapter(tableView: tableView) - adapter?.onOpenActionList = { [weak self] model in - self?.output?.didTapActionList(model: model) - } - adapter?.onOpenInfoTable = { [weak self] model in - self?.output?.didTapInfoTable(model: model) - } - adapter?.onSelectableTextTap = { [weak self] model in - self?.output?.didTapSelectableText(model: model) - } - adapter?.onOpenSectionScreen = { [weak self] model in - self?.output?.didTapSectionScreen(model: model) - } - } - -} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib deleted file mode 100644 index ee4ac60..0000000 --- a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewController.xib +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift deleted file mode 100644 index 33ed83b..0000000 --- a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewInput.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// SectionScreenViewInput.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -protocol SectionScreenViewInput: AnyObject { - func setupInitialState(with title: String, sections: [TableSection]) -} diff --git a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift b/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift deleted file mode 100644 index 349d68d..0000000 --- a/DebugScreen/Flows/SectionScreen/View/SectionScreenViewOutput.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// SectionScreenViewOutput.swift -// DebugScreen -// -// Created by Aleksandr Potemkin on 13.02.2026. -// - -protocol SectionScreenViewOutput { - func viewLoaded() - func didTapActionList(model: ActionList) - func didTapInfoTable(model: InfoTableModel) - func didTapSelectableText(model: CopiedText) - func didTapSectionScreen(model: SectionScreen) -} diff --git a/Example/DebugScreenExample/App/AppDelegate.swift b/Example/DebugScreenExample/App/AppDelegate.swift index 3c86e4b..de95904 100644 --- a/Example/DebugScreenExample/App/AppDelegate.swift +++ b/Example/DebugScreenExample/App/AppDelegate.swift @@ -57,7 +57,7 @@ private extension AppDelegate { let copiedTextSection = CopiedTextSectionBuilder().build() let infoTablesSection = InfoTablesSectionBuilder().build() let menuItemsSection = MenuItemsSectionBuilder().build() - let sectionScreensSection = SectionScreensSectionBuilder().build() + let nestedScreensSection = NestedScreensSectionBuilder().build() /// You can build logger section from ready-to-use components /// or create it manually like other sections by implementing `SectionBuilder` protocol @@ -72,7 +72,7 @@ private extension AppDelegate { actionsSection, togglesSection, menuItemsSection, - sectionScreensSection, + nestedScreensSection, copiedTextSection, infoTablesSection, loggerSection] diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/NestedScreensSectionBuilder.swift similarity index 53% rename from Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift rename to Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/NestedScreensSectionBuilder.swift index faa8d82..57202aa 100644 --- a/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/SectionScreensSectionBuilder.swift +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Builders/NestedScreensSectionBuilder.swift @@ -1,5 +1,5 @@ // -// SectionScreensSectionBuilder.swift +// NestedScreensSectionBuilder.swift // Example // // Created by Aleksandr Potemkin on 13.02.2026. @@ -7,96 +7,95 @@ import DebugScreen -final class SectionScreensSectionBuilder: SectionBuilder { +final class NestedScreensSectionBuilder: SectionBuilder { // MARK: - Methods func build() -> TableSection { - let blocks = configureSectionScreenBlocks() + let blocks = configureNestedScreenBlocks() - return .init(title: L10n.SectionScreens.header, blocks: blocks) + return .init(title: L10n.NestedScreens.header, blocks: blocks) } } // MARK: - Private Methods -private extension SectionScreensSectionBuilder { +private extension NestedScreensSectionBuilder { - func configureSectionScreenBlocks() -> [MainTableBlock] { + func configureNestedScreenBlocks() -> [MainTableBlock] { let settingsScreen = configureSettingsScreen() let nestedScreen = configureNestedScreen() return [ - .sectionScreen(model: settingsScreen), - .sectionScreen(model: nestedScreen) + .nestedScreen(model: settingsScreen), + .nestedScreen(model: nestedScreen) ] } /// Simple section screen with toggles and copied text items inside. - func configureSettingsScreen() -> DebugScreenSectionScreen { + func configureSettingsScreen() -> DebugScreenNestedScreen { let toggle1 = UserDefaultsFeatureToggle( - title: L10n.SectionScreens.Settings.firstToggleTitle, + title: L10n.NestedScreens.Settings.firstToggleTitle, key: UserDefaultsKeys.isNeedShowTouches ) let toggle2 = UserDefaultsFeatureToggle( - title: L10n.SectionScreens.Settings.secondToggleTitle, + title: L10n.NestedScreens.Settings.secondToggleTitle, key: UserDefaultsKeys.isNeedUseSSH ) let togglesSection = TableSection( - title: L10n.SectionScreens.Settings.togglesSectionTitle, + title: L10n.NestedScreens.Settings.togglesSectionTitle, blocks: [.toggle(model: toggle1), .toggle(model: toggle2)] ) let copiedTextItem = CopiedTextItem( - title: L10n.SectionScreens.Settings.copiedTextTitle, + title: L10n.NestedScreens.Settings.copiedTextTitle, value: "example_api_key_12345" ) let copiedTextSection = TableSection( - title: L10n.SectionScreens.Settings.copiedTextSectionTitle, + title: L10n.NestedScreens.Settings.copiedTextSectionTitle, blocks: [.copiedText(model: copiedTextItem)] ) return .init( - title: L10n.SectionScreens.settingsTitle, + title: L10n.NestedScreens.settingsTitle, sections: [togglesSection, copiedTextSection] ) } /// Nested section screen that contains another section screen inside (recursive nesting demo). - func configureNestedScreen() -> DebugScreenSectionScreen { + func configureNestedScreen() -> DebugScreenNestedScreen { // Deepest level – a screen with simple menu items let deepMenuItem = DebugScreenMenuItem( - title: L10n.SectionScreens.Nested.deepMenuItemTitle + title: L10n.NestedScreens.Nested.deepMenuItemTitle ) { debugPrint("✅ Deep nested menu item tapped") } let deepInfoTable: InfoTableModel = .appInfoTable() - let deepScreen = DebugScreenSectionScreen( - title: L10n.SectionScreens.Nested.deepScreenTitle, + let deepScreen = DebugScreenNestedScreen( + title: L10n.NestedScreens.Nested.deepScreenTitle, sections: [ - .init(title: L10n.SectionScreens.Nested.deepMenuSectionTitle, + .init(title: L10n.NestedScreens.Nested.deepMenuSectionTitle, blocks: [.menuItem(model: deepMenuItem)]), - .init(title: L10n.SectionScreens.Nested.deepInfoSectionTitle, + .init(title: L10n.NestedScreens.Nested.deepInfoSectionTitle, blocks: [.infoTable(model: deepInfoTable)]) ] ) // Middle level – contains a link to the deepest screen and its own content let middleMenuItem = DebugScreenMenuItem( - title: L10n.SectionScreens.Nested.middleMenuItemTitle, - showsDisclosure: true + title: L10n.NestedScreens.Nested.middleMenuItemTitle, ) { debugPrint("✅ Middle nested menu item tapped") } return .init( - title: L10n.SectionScreens.nestedTitle, + title: L10n.NestedScreens.nestedTitle, sections: [ - .init(title: L10n.SectionScreens.Nested.middleMenuSectionTitle, + .init(title: L10n.NestedScreens.Nested.middleMenuSectionTitle, blocks: [.menuItem(model: middleMenuItem)]), - .init(title: L10n.SectionScreens.Nested.nestedScreenSectionTitle, - blocks: [.sectionScreen(model: deepScreen)]) + .init(title: L10n.NestedScreens.Nested.nestedScreenSectionTitle, + blocks: [.nestedScreen(model: deepScreen)]) ] ) } diff --git a/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenNestedScreen.swift similarity index 78% rename from Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift rename to Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenNestedScreen.swift index bf8c10e..5d6a58f 100644 --- a/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenSectionScreen.swift +++ b/Example/DebugScreenExample/App/DebugScreenConfiguration/Models/DebugScreenNestedScreen.swift @@ -1,5 +1,5 @@ // -// DebugScreenSectionScreen.swift +// DebugScreenNestedScreen.swift // Example // // Created by Aleksandr Potemkin on 13.02.2026. @@ -7,7 +7,7 @@ import DebugScreen -final class DebugScreenSectionScreen: SectionScreen { +final class DebugScreenNestedScreen: NestedScreen { // MARK: - Properties diff --git a/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift b/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift index 2d4ef32..74dd00d 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift +++ b/Example/DebugScreenExample/Library/Resources/Strings/Strings.swift @@ -85,40 +85,40 @@ internal enum L10n { internal static let simpleTitle = L10n.tr("Localizable", "MenuItems.simpleTitle") } - internal enum SectionScreens { + internal enum NestedScreens { /// Вложенные экраны - internal static let header = L10n.tr("Localizable", "SectionScreens.header") + internal static let header = L10n.tr("Localizable", "NestedScreens.header") /// Вложенность экранов - internal static let nestedTitle = L10n.tr("Localizable", "SectionScreens.nestedTitle") + internal static let nestedTitle = L10n.tr("Localizable", "NestedScreens.nestedTitle") /// Настройки - internal static let settingsTitle = L10n.tr("Localizable", "SectionScreens.settingsTitle") + internal static let settingsTitle = L10n.tr("Localizable", "NestedScreens.settingsTitle") internal enum Nested { /// Информация - internal static let deepInfoSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepInfoSectionTitle") + internal static let deepInfoSectionTitle = L10n.tr("Localizable", "NestedScreens.Nested.deepInfoSectionTitle") /// Действие глубокого уровня - internal static let deepMenuItemTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepMenuItemTitle") + internal static let deepMenuItemTitle = L10n.tr("Localizable", "NestedScreens.Nested.deepMenuItemTitle") /// Действия - internal static let deepMenuSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepMenuSectionTitle") + internal static let deepMenuSectionTitle = L10n.tr("Localizable", "NestedScreens.Nested.deepMenuSectionTitle") /// Глубокий уровень - internal static let deepScreenTitle = L10n.tr("Localizable", "SectionScreens.Nested.deepScreenTitle") + internal static let deepScreenTitle = L10n.tr("Localizable", "NestedScreens.Nested.deepScreenTitle") /// Действие среднего уровня - internal static let middleMenuItemTitle = L10n.tr("Localizable", "SectionScreens.Nested.middleMenuItemTitle") + internal static let middleMenuItemTitle = L10n.tr("Localizable", "NestedScreens.Nested.middleMenuItemTitle") /// Действия - internal static let middleMenuSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.middleMenuSectionTitle") + internal static let middleMenuSectionTitle = L10n.tr("Localizable", "NestedScreens.Nested.middleMenuSectionTitle") /// Глубже - internal static let nestedScreenSectionTitle = L10n.tr("Localizable", "SectionScreens.Nested.nestedScreenSectionTitle") + internal static let nestedScreenSectionTitle = L10n.tr("Localizable", "NestedScreens.Nested.nestedScreenSectionTitle") } internal enum Settings { /// API ключи - internal static let copiedTextSectionTitle = L10n.tr("Localizable", "SectionScreens.Settings.copiedTextSectionTitle") + internal static let copiedTextSectionTitle = L10n.tr("Localizable", "NestedScreens.Settings.copiedTextSectionTitle") /// API ключ - internal static let copiedTextTitle = L10n.tr("Localizable", "SectionScreens.Settings.copiedTextTitle") + internal static let copiedTextTitle = L10n.tr("Localizable", "NestedScreens.Settings.copiedTextTitle") /// Показывать касания - internal static let firstToggleTitle = L10n.tr("Localizable", "SectionScreens.Settings.firstToggleTitle") + internal static let firstToggleTitle = L10n.tr("Localizable", "NestedScreens.Settings.firstToggleTitle") /// Использовать SSH - internal static let secondToggleTitle = L10n.tr("Localizable", "SectionScreens.Settings.secondToggleTitle") + internal static let secondToggleTitle = L10n.tr("Localizable", "NestedScreens.Settings.secondToggleTitle") /// Переключатели - internal static let togglesSectionTitle = L10n.tr("Localizable", "SectionScreens.Settings.togglesSectionTitle") + internal static let togglesSectionTitle = L10n.tr("Localizable", "NestedScreens.Settings.togglesSectionTitle") } } diff --git a/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings b/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings index ca773c8..856a2d1 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings +++ b/Example/DebugScreenExample/Library/Resources/Strings/en.lproj/Localizable.strings @@ -55,23 +55,23 @@ "MenuItems.multilineTitle" = "This is a multiline menu item\nwith a very long description that spans multiple lines"; "MenuItems.noActionTitle" = "Informational item (no action)"; -// MARK: - SectionScreens - -"SectionScreens.header" = "Section Screens"; -"SectionScreens.settingsTitle" = "Settings"; -"SectionScreens.nestedTitle" = "Nested Screens"; -"SectionScreens.Settings.togglesSectionTitle" = "Toggles"; -"SectionScreens.Settings.firstToggleTitle" = "Show touches"; -"SectionScreens.Settings.secondToggleTitle" = "Use SSH"; -"SectionScreens.Settings.copiedTextSectionTitle" = "API Keys"; -"SectionScreens.Settings.copiedTextTitle" = "API Key"; -"SectionScreens.Nested.middleMenuSectionTitle" = "Actions"; -"SectionScreens.Nested.middleMenuItemTitle" = "Middle level action"; -"SectionScreens.Nested.nestedScreenSectionTitle" = "Go Deeper"; -"SectionScreens.Nested.deepScreenTitle" = "Deep Level"; -"SectionScreens.Nested.deepMenuSectionTitle" = "Deep Actions"; -"SectionScreens.Nested.deepMenuItemTitle" = "Deep nested action"; -"SectionScreens.Nested.deepInfoSectionTitle" = "Info"; +// MARK: - NestedScreens + +"NestedScreens.header" = "Section Screens"; +"NestedScreens.settingsTitle" = "Settings"; +"NestedScreens.nestedTitle" = "Nested Screens"; +"NestedScreens.Settings.togglesSectionTitle" = "Toggles"; +"NestedScreens.Settings.firstToggleTitle" = "Show touches"; +"NestedScreens.Settings.secondToggleTitle" = "Use SSH"; +"NestedScreens.Settings.copiedTextSectionTitle" = "API Keys"; +"NestedScreens.Settings.copiedTextTitle" = "API Key"; +"NestedScreens.Nested.middleMenuSectionTitle" = "Actions"; +"NestedScreens.Nested.middleMenuItemTitle" = "Middle level action"; +"NestedScreens.Nested.nestedScreenSectionTitle" = "Go Deeper"; +"NestedScreens.Nested.deepScreenTitle" = "Deep Level"; +"NestedScreens.Nested.deepMenuSectionTitle" = "Deep Actions"; +"NestedScreens.Nested.deepMenuItemTitle" = "Deep nested action"; +"NestedScreens.Nested.deepInfoSectionTitle" = "Info"; // MARK: - Destination Controller diff --git a/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings b/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings index 25baf83..b7363da 100644 --- a/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings +++ b/Example/DebugScreenExample/Library/Resources/Strings/ru.lproj/Localizable.strings @@ -55,23 +55,23 @@ "MenuItems.multilineTitle" = "Многострочный пункт меню\nс очень длинным описанием, которое занимает несколько строк"; "MenuItems.noActionTitle" = "Информационный пункт (без действия)"; -// MARK: - SectionScreens - -"SectionScreens.header" = "Вложенные экраны"; -"SectionScreens.settingsTitle" = "Настройки"; -"SectionScreens.nestedTitle" = "Вложенность экранов"; -"SectionScreens.Settings.togglesSectionTitle" = "Переключатели"; -"SectionScreens.Settings.firstToggleTitle" = "Показывать касания"; -"SectionScreens.Settings.secondToggleTitle" = "Использовать SSH"; -"SectionScreens.Settings.copiedTextSectionTitle" = "API ключи"; -"SectionScreens.Settings.copiedTextTitle" = "API ключ"; -"SectionScreens.Nested.middleMenuSectionTitle" = "Действия"; -"SectionScreens.Nested.middleMenuItemTitle" = "Действие среднего уровня"; -"SectionScreens.Nested.nestedScreenSectionTitle" = "Глубже"; -"SectionScreens.Nested.deepScreenTitle" = "Глубокий уровень"; -"SectionScreens.Nested.deepMenuSectionTitle" = "Действия"; -"SectionScreens.Nested.deepMenuItemTitle" = "Действие глубокого уровня"; -"SectionScreens.Nested.deepInfoSectionTitle" = "Информация"; +// MARK: - NestedScreens + +"NestedScreens.header" = "Вложенные экраны"; +"NestedScreens.settingsTitle" = "Настройки"; +"NestedScreens.nestedTitle" = "Вложенность экранов"; +"NestedScreens.Settings.togglesSectionTitle" = "Переключатели"; +"NestedScreens.Settings.firstToggleTitle" = "Показывать касания"; +"NestedScreens.Settings.secondToggleTitle" = "Использовать SSH"; +"NestedScreens.Settings.copiedTextSectionTitle" = "API ключи"; +"NestedScreens.Settings.copiedTextTitle" = "API ключ"; +"NestedScreens.Nested.middleMenuSectionTitle" = "Действия"; +"NestedScreens.Nested.middleMenuItemTitle" = "Действие среднего уровня"; +"NestedScreens.Nested.nestedScreenSectionTitle" = "Глубже"; +"NestedScreens.Nested.deepScreenTitle" = "Глубокий уровень"; +"NestedScreens.Nested.deepMenuSectionTitle" = "Действия"; +"NestedScreens.Nested.deepMenuItemTitle" = "Действие глубокого уровня"; +"NestedScreens.Nested.deepInfoSectionTitle" = "Информация"; // MARK: - Destination Controller From 7d1fe0dbd040d6099bda32e5709994bd4b57cc0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D1=82=D1=91=D0=BC=D0=BA=D0=B8=D0=BD=20=D0=90?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Wed, 18 Feb 2026 15:51:32 +0300 Subject: [PATCH 3/3] Remove old setupInitialState from MainModulePresenter --- .../Flows/MainScreen/Presenter/MainModulePresenter.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift index d2ae89b..9ee0cd6 100644 --- a/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift +++ b/DebugScreen/Flows/MainScreen/Presenter/MainModulePresenter.swift @@ -56,10 +56,6 @@ extension MainModulePresenter: MainViewOutput { title: title, sections: sections ) -// view?.setupInitialState( -// title: L10n.MainViewController.debugTitle, -// sections: DebugScreenConfiguration.shared.sections -// ) } func didTapActionList(model: ActionList) {