From 6cda0a744ea038a6a5cae9745039e4e1c977bd13 Mon Sep 17 00:00:00 2001 From: igrushekradi Date: Mon, 30 Mar 2026 09:18:20 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=B0=D0=BD=D0=B0=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=98.=D0=90.=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D1=91=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...21\202\320\276\320\262\320\230\320\220.md" | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 "\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220.md" diff --git "a/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220.md" "b/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220.md" new file mode 100644 index 0000000..c69ae2a --- /dev/null +++ "b/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220/\320\234\320\260\320\275\320\260\321\202\320\276\320\262\320\230\320\220.md" @@ -0,0 +1,45 @@ +## Отчёт по рефакторингу + +### Выбранный проект + +**Rumble** — open-source iOS-приложение для обхода блокировок на базе VPN-туннеля. + +- **Репозиторий:** https://github.com/RumbleOrg/Rumble +- **Описание:** Приложение создаёт локальный VPN-туннель, который пропускает трафик через утилиту `ciadpi` с настраиваемыми параметрами манипуляции пакетами. Позволяет обходить DPI-фильтрацию (Deep Packet Inspection), используемую интернет-провайдерами для блокировки сайтов и сервисов. +- **Стек технологий:** + - Язык: Objective-C + - UI-фреймворк: UIKit (программный, без Storyboard) + - Системные фреймворки: NetworkExtension (`NETunnelProviderManager`, `NETunnelProviderProtocol`), Foundation + - Сборочная система: Theos (сборка unsigned IPA) + - CI: GitHub Actions + +--- + +### Принципы рефакторинга + +- **Инициализация в конструкторе** — состояние объекта приводится в корректное, полностью инициализированное состояние уже в `init`, а не откладывается до момента загрузки представления. Это устраняет зависимость от порядка обхода вкладок пользователем. +- **Устранение порядковой связанности** — логика не должна зависеть от того, в каком порядке пользователь посетил экраны приложения. +- **Единственный источник истины** — конфигурация по умолчанию определена ровно в одном месте (в `initWithStyle:` класса `RMSettingsViewController`), а не дублируется в нескольких местах. +- **Корректный порядок инициализации зависимостей** — связанные свойства объекта устанавливаются до передачи объекта в другой контейнер. + +--- + +### Описание выполненного рефакторинга + +#### Проблема 1: краш при запуске VPN без посещения вкладки Settings + +**Обнаруженная проблема.** Значения по умолчанию для ключей `Args`, `DNSServer` и `IPv6` регистрировались в `NSUserDefaults` только внутри `RMSettingsViewController.viewDidLoad` — метода, который вызывается лишь тогда, когда пользователь открывает вкладку Settings. При первом запуске приложения, если пользователь сразу нажимал кнопку VPN, не заходя в настройки, `NSUserDefaults` возвращал `nil` для `Args` и `DNSServer`. VPN-расширение (`PacketTunnelProvider`) проверяет типы переданных параметров и при получении `nil` завершало соединение с ошибкой `NEVPNErrorConfigurationInvalid`. + +**Решение.** Инициализация массива `settings` и регистрация значений по умолчанию перенесены из `viewDidLoad` в `initWithStyle:`. Этот метод вызывается в `RMAppDelegate.didFinishLaunchingWithOptions:` при создании экземпляра контроллера — до того, как пользователь может взаимодействовать с интерфейсом. Логика и данные остались в одном файле (`RMSettingsViewController.m`), дублирования не возникло. + +#### Проблема 2: кнопка VPN оставалась неактивной + +**Обнаруженная проблема.** При первичной инициализации VPN-менеджера свойства `providerBundleIdentifier` и `serverAddress` устанавливались на объекте `prot` *после* того, как он был присвоен `mgr.protocolConfiguration`. Из-за этого менеджер получал объект протокола без обязательных полей, не мог корректно сконфигурировать туннель и оставался в неактивном состоянии — кнопка включения VPN не реагировала на нажатие. + +**Решение.** Строки установки `providerBundleIdentifier` и `serverAddress` перемещены выше строки `mgr.protocolConfiguration = prot`, чтобы объект протокола передавался в менеджер уже полностью сконфигурированным. + +--- + +### Pull Request + +https://github.com/RumbleOrg/Rumble/pull/1