/
sberpay
/
AgeGate-iOS
Обзор
Документация
Войти
/
sberpay
/
AgeGate-iOS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
develop
AgeGateExample/Presentation/ConfigModule/ConfigPresenter.swift
292 строки
11 KB
SergeyGladkiy
[0.2.0] БДСЧ сценарий
30 июл 2026, 21:57
30 июл 2026, 21:57
cbcdfcb
Код
Авторство
О чём код?
// // ConfigPresenter.swift // SPay // // Created by Alexander Ipatov on 08.11.2022. // import UIKit import AgeGateSDKDEBUG protocol ConfigPresenterProtocol { func viewDidLoad() func cell(for indexPath: IndexPath, in tableView: UITableView) -> UITableViewCell func didSelectRow(at indexPath: IndexPath) func title(for section: Int) -> String? func footer(for section: Int) -> String? func numberOfRows(in section: Int) -> Int var sectionCount: Int { get } } final class ConfigPresenter: ConfigPresenterProtocol { weak var view: (ConfigVCProtocol & UIViewController)? private var values = ConfigValues() private let sections = SectionData.allCases var sectionCount: Int { sections.count } func viewDidLoad() { applyToSDK() } func numberOfRows(in section: Int) -> Int { cells(for: section).count } func title(for section: Int) -> String? { SectionData(rawValue: section)?.title } func footer(for section: Int) -> String? { switch SectionData(rawValue: section) { case .standalone: return "Сценарии, вызываемые отдельной ручкой, вне основного потока." default: return nil } } private func cells(for section: Int) -> [CellType] { switch SectionData(rawValue: section) { case .standalone: return [.runKeyGeneration] case .actions: return [.showLogs, .clearLogs, .clearSession, .resetDefaults] case .next: return [.next] case .none: return [] } } // MARK: - Ячейки func cell(for indexPath: IndexPath, in tableView: UITableView) -> UITableViewCell { let type = cells(for: indexPath.section)[indexPath.row] switch type { case .scenario: let cell: ListCell = tableView.dequeue(for: indexPath) cell.config(title: "Ветка клиента", value: values.scenario.rawValue) return cell case .stepDelay: let cell: TextViewCell = tableView.dequeue(for: indexPath) cell.config(title: "Задержка шага, сек", value: String(values.stepDelay), keyboard: .decimalPad) { [weak self] text in self?.values.stepDelay = Double(text.replacingOccurrences(of: ",", with: ".")) ?? 0.4 self?.applyToSDK() } return cell case .environment: let cell: SegmentedControlCell = tableView.dequeue(for: indexPath) let items = EnvironmentMode.allCases cell.config(title: "Среда", items: items.map(\.rawValue), selected: items.firstIndex(of: values.environment) ?? 0) { [weak self] index in self?.values.environment = items[index] self?.applyToSDK() } return cell case .partnerId: let cell: TextViewCell = tableView.dequeue(for: indexPath) cell.config(title: "Идентификатор партнёра", value: values.partnerId) { [weak self] text in self?.values.partnerId = text } return cell case .logging: let cell: SwitchCell = tableView.dequeue(for: indexPath) cell.config(title: "Писать логи в файл", value: values.logging) { [weak self] isOn in self?.values.logging = isOn self?.applyToSDK() } return cell case .secureLogs: let cell: SwitchCell = tableView.dequeue(for: indexPath) cell.config(title: "Маскировать чувствительное", value: values.secureLogs) { [weak self] isOn in self?.values.secureLogs = isOn self?.applyToSDK() } return cell case .logLevels: let cell: ListCell = tableView.dequeue(for: indexPath) cell.config(title: "Категории логов", value: "\(values.logLevels.count) из \(DebugLogLevel.allCases.count)") return cell case .orderId: let cell: TextViewCell = tableView.dequeue(for: indexPath) cell.config(title: "Идентификатор заказа", value: values.orderId) { [weak self] text in self?.values.orderId = text } return cell case .cost: let cell: TextViewCell = tableView.dequeue(for: indexPath) cell.config(title: "Сумма, ₽", value: String(values.cost), keyboard: .numberPad) { [weak self] text in self?.values.cost = Int(text) ?? 0 } return cell case .restrictedCount: let cell: SegmentedControlCell = tableView.dequeue(for: indexPath) cell.config(title: "Позиций 18+ в корзине", items: ["0", "1", "2"], selected: min(values.restrictedCount, 2)) { [weak self] index in self?.values.restrictedCount = index } return cell case .displayMode: let cell: SegmentedControlCell = tableView.dequeue(for: indexPath) let items = DisplayMode.allCases cell.config(title: "Режим показа", items: items.map(\.rawValue), selected: items.firstIndex(of: values.displayMode) ?? 0) { [weak self] index in self?.values.displayMode = items[index] } return cell case .showLogs: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Показать логи") { [weak self] in self?.view?.push(AgeGate.debugLogViewController()) } return cell case .clearLogs: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Очистить логи", destructive: true) { [weak self] in AgeGate.debugClearLogs() self?.view?.showAlert(title: "Логи очищены", message: nil) } return cell case .clearSession: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Сбросить сессию SDK", destructive: true) { [weak self] in AgeGate.logout() self?.view?.showAlert(title: "Сессия сброшена", message: nil) } return cell case .resetDefaults: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Сбросить все настройки", destructive: true) { [weak self] in CellType.allCases.forEach { UserDefaults.standard.removeObject(forKey: $0.rawValue) } self?.values = ConfigValues() self?.applyToSDK() self?.view?.reload() } return cell case .runKeyGeneration: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Генерация ключей (БДСЧ)") { [weak self] in self?.runKeyGeneration() } return cell case .next: let cell: ButtonCell = tableView.dequeue(for: indexPath) cell.config(title: "Далее") { [weak self] in self?.goForward() } return cell } } // MARK: - Выбор func didSelectRow(at indexPath: IndexPath) { let type = cells(for: indexPath.section)[indexPath.row] switch type { case .scenario: let items = AgeGate.DebugScenario.allCases view?.showSelectableAlert(title: "Ветка клиента", items: items.map(\.title)) { [weak self] index in self?.values.scenario = items[index] self?.applyToSDK() self?.view?.reloadSection(indexPath.section) } case .logLevels: let all = DebugLogLevel.allCases /// Быстрый переключатель: либо все категории, либо только сценарий и переходы. let isAll = values.logLevels.count == all.count values.logLevels = isAll ? [.flow, .appSwitch, .error] : all applyToSDK() view?.reloadSection(indexPath.section) default: break } } // MARK: - Отдельные сценарии private func runKeyGeneration() { applyToSDK() AgeGate.startKeyGeneration(from: nil) { [weak self] result in switch result { case .created: self?.view?.showAlert(title: "Ключ создан", message: nil) case .cancelled: self?.view?.showAlert(title: "Отменено", message: nil) case .failed(let error): self?.view?.showAlert(title: "Ошибка", message: "\(error)") } } } // MARK: - Переход к корзине /// Настройки применяются к SDK и уезжают в корзину значениями, /// а не общим состоянием — так видно, с чем именно запускали сценарий. private func goForward() { applyToSDK() view?.push(CartAssembly().createModule(values: values)) } // MARK: - Применение /// Прокидывает настройки в SDK. Вызывается при каждом изменении, /// чтобы не приходилось перезапускать приложение. private func applyToSDK() { AgeGate.setup(partnerId: values.partnerId, environment: values.environment.sdkValue, logging: values.logging) AgeGate.debugSetScenario(values.scenario) AgeGate.debugSetStepDelay(values.stepDelay) AgeGate.debugSetLogLevels(values.logLevels) AgeGate.debugSetSecureLogs(values.secureLogs) } } private extension EnvironmentMode { var sdkValue: AGTEnvironment { switch self { case .mock: return .mock case .sandbox: return .sandbox case .production: return .production } } } extension UITableView { func dequeue<T: UITableViewCell>(for indexPath: IndexPath) -> T { guard let cell = dequeueReusableCell(withIdentifier: String(describing: T.self), for: indexPath) as? T else { fatalError("не зарегистрирована ячейка \(T.self)") } return cell } }