From 95beafa25b55b5f1b3e66ef324e6a2aef5e420a3 Mon Sep 17 00:00:00 2001 From: baegteun Date: Tue, 23 Apr 2024 23:22:48 +0900 Subject: [PATCH 1/6] :sparkles: :: [#496] LogHistoryStorage --- .../LogManager/Sources/AnalyticsLogManager.swift | 2 ++ .../Sources/LogHistory/LogHistoryStorage.swift | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift diff --git a/Projects/Modules/LogManager/Sources/AnalyticsLogManager.swift b/Projects/Modules/LogManager/Sources/AnalyticsLogManager.swift index d2686403e..3a3be3361 100644 --- a/Projects/Modules/LogManager/Sources/AnalyticsLogManager.swift +++ b/Projects/Modules/LogManager/Sources/AnalyticsLogManager.swift @@ -115,6 +115,8 @@ public extension LogManager { ) { #if RELEASE Analytics.logEvent(log.name, parameters: log.params) + #elseif DEBUG + LogHistoryStorage.shared.appendHistory(log: log) #endif let message = """ \(log.name) logged diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift new file mode 100644 index 000000000..3b0194ed9 --- /dev/null +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift @@ -0,0 +1,14 @@ +#if DEBUG +import Foundation + +final class LogHistoryStorage { + static let shared = LogHistoryStorage() + + private(set) var logHistory: [any AnalyticsLogType] = [] + + func appendHistory(log: any AnalyticsLogType) { + logHistory.append(log) + } +} + +#endif From f5e44b9ee20f08c1122f951ff6ff5782169e2a64 Mon Sep 17 00:00:00 2001 From: baegteun Date: Tue, 23 Apr 2024 23:23:17 +0900 Subject: [PATCH 2/6] :lipstick: :: [#496] LogHistoryViewController --- .../Sources/LogHistory/LogHistoryCell.swift | 68 ++++++++++++++++ .../LogHistory/LogHistorySectionItem.swift | 17 ++++ .../LogHistory/LogHistoryStorage.swift | 14 ++-- .../LogHistory/LogHistoryViewController.swift | 78 +++++++++++++++++++ 4 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 Projects/Modules/LogManager/Sources/LogHistory/LogHistoryCell.swift create mode 100644 Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift create mode 100644 Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryCell.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryCell.swift new file mode 100644 index 000000000..f82f456e6 --- /dev/null +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryCell.swift @@ -0,0 +1,68 @@ +#if DEBUG + import UIKit + + final class LogHistoryCell: UITableViewCell { + static let reuseIdentifier = String(describing: LogHistoryCell.self) + + private let logStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.spacing = 8 + stackView.alignment = .leading + stackView.translatesAutoresizingMaskIntoConstraints = false + return stackView + }() + + private let logTitleLabel: UILabel = { + let label = UILabel() + label.font = .systemFont(ofSize: 20) + label.numberOfLines = 0 + return label + }() + + private let logParametersLabel: UILabel = { + let label = UILabel() + label.font = .systemFont(ofSize: 16) + label.textColor = .gray + label.numberOfLines = 0 + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + addView() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(log: any AnalyticsLogType) { + logTitleLabel.text = log.name + logParametersLabel.text = log.params + .filter { !($0.key == "date" || $0.key == "timestamp") } + .map { "- \($0) : \($1)" } + .joined(separator: "\n") + } + } + + private extension LogHistoryCell { + func addView() { + contentView.addSubview(logStackView) + logStackView.addArrangedSubview(logTitleLabel) + logStackView.addArrangedSubview(logParametersLabel) + } + + func setLayout() { + NSLayoutConstraint.activate([ + logStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), + logStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + logStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + logStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8) + ]) + } + } + +#endif diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift new file mode 100644 index 000000000..9c36ef5a7 --- /dev/null +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift @@ -0,0 +1,17 @@ +#if DEBUG + import Foundation + + struct LogHistorySectionItem: Hashable, Equatable { + let index: Int + let log: any AnalyticsLogType + + func hash(into hasher: inout Hasher) { + hasher.combine(index) + hasher.combine(log.name) + } + + static func == (lhs: LogHistorySectionItem, rhs: LogHistorySectionItem) -> Bool { + lhs.index == rhs.index && lhs.log.name == rhs.log.name + } + } +#endif diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift index 3b0194ed9..4889264a4 100644 --- a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift @@ -1,14 +1,14 @@ #if DEBUG -import Foundation + import Foundation -final class LogHistoryStorage { - static let shared = LogHistoryStorage() + final class LogHistoryStorage { + static let shared = LogHistoryStorage() - private(set) var logHistory: [any AnalyticsLogType] = [] + private(set) var logHistory: [any AnalyticsLogType] = [] - func appendHistory(log: any AnalyticsLogType) { - logHistory.append(log) + func appendHistory(log: any AnalyticsLogType) { + logHistory.append(log) + } } -} #endif diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift new file mode 100644 index 000000000..05d30a53c --- /dev/null +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift @@ -0,0 +1,78 @@ +#if DEBUG + import UIKit + + public final class LogHistoryViewController: UIViewController { + private let logHistoryTableView: UITableView = { + let tableView = UITableView() + tableView.separatorInset = .init(top: 8, left: 0, bottom: 8, right: 0) + tableView.register(LogHistoryCell.self, forCellReuseIdentifier: LogHistoryCell.reuseIdentifier) + return tableView + }() + + private lazy var logHistoryTableViewDiffableDataSource = UITableViewDiffableDataSource< + Int, + LogHistorySectionItem + >( + tableView: logHistoryTableView + ) { tableView, indexPath, itemIdentifier in + guard let cell = tableView.dequeueReusableCell( + withIdentifier: LogHistoryCell.reuseIdentifier, + for: indexPath + ) as? LogHistoryCell + else { + return UITableViewCell() + } + cell.configure(log: itemIdentifier.log) + return cell + } + + public init() { + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func viewDidLoad() { + super.viewDidLoad() + addView() + setLayout() + view.backgroundColor = .white + + let logs = LogHistoryStorage.shared.logHistory + .enumerated() + .map { LogHistorySectionItem(index: $0.offset, log: $0.element) } + + var snapshot = logHistoryTableViewDiffableDataSource.snapshot() + snapshot.appendSections([0]) + snapshot.appendItems(logs, toSection: 0) + + logHistoryTableViewDiffableDataSource.apply(snapshot, animatingDifferences: true) + } + } + + private extension LogHistoryViewController { + func addView() { + view.addSubview(logHistoryTableView) + logHistoryTableView.translatesAutoresizingMaskIntoConstraints = false + } + + func setLayout() { + NSLayoutConstraint.activate([ + logHistoryTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), + logHistoryTableView.leadingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.leadingAnchor, + constant: 16 + ), + logHistoryTableView.trailingAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.trailingAnchor, + constant: -16 + ), + logHistoryTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -16) + ]) + } + } + +#endif From 057f5e260e2f0baa46885687c6271b8876cfeef1 Mon Sep 17 00:00:00 2001 From: baegteun Date: Tue, 23 Apr 2024 23:24:04 +0900 Subject: [PATCH 3/6] =?UTF-8?q?:sparkles:=20::=20[#496]=20DEBUG=20?= =?UTF-8?q?=EC=95=B1=EC=97=90=EC=84=9C=20Shake=20=EA=B0=90=EC=A7=80=20?= =?UTF-8?q?=EC=8B=9C=20LogHistoryViewController=20present?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/Application/AppDelegate.swift | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Projects/App/Sources/Application/AppDelegate.swift b/Projects/App/Sources/Application/AppDelegate.swift index e2b756e7a..88ea3a69c 100644 --- a/Projects/App/Sources/Application/AppDelegate.swift +++ b/Projects/App/Sources/Application/AppDelegate.swift @@ -103,3 +103,38 @@ private extension AppDelegate { ]) } } + +#if DEBUG + extension UIWindow { + override open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + super.motionEnded(motion, with: event) + switch motion { + case .motionShake: + guard let topViewController = if #available(iOS 15.0, *) { + UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .filter { $0.activationState == .foregroundActive } + .first? + .keyWindow? + .rootViewController + } else { + UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .filter { $0.activationState == .foregroundActive } + .first? + .windows + .first(where: \.isKeyWindow)? + .rootViewController + } else { break } + if let nav = topViewController as? UINavigationController { + nav.visibleViewController?.present(LogHistoryViewController(), animated: true) + } else { + topViewController.present(LogHistoryViewController(), animated: true) + } + + default: + break + } + } + } +#endif From a5eaef6fac49df7c536eb02bb80096432773383d Mon Sep 17 00:00:00 2001 From: baegteun Date: Tue, 23 Apr 2024 23:34:41 +0900 Subject: [PATCH 4/6] =?UTF-8?q?:bug:=20::=20[#496]=20Swift=20expression=20?= =?UTF-8?q?=EB=AC=B8=EB=B2=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/App/Sources/Application/AppDelegate.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Projects/App/Sources/Application/AppDelegate.swift b/Projects/App/Sources/Application/AppDelegate.swift index 88ea3a69c..f46f5afee 100644 --- a/Projects/App/Sources/Application/AppDelegate.swift +++ b/Projects/App/Sources/Application/AppDelegate.swift @@ -110,22 +110,24 @@ private extension AppDelegate { super.motionEnded(motion, with: event) switch motion { case .motionShake: - guard let topViewController = if #available(iOS 15.0, *) { - UIApplication.shared.connectedScenes + let topViewController: UIViewController? + if #available(iOS 15.0, *) { + topViewController = UIApplication.shared.connectedScenes .compactMap { $0 as? UIWindowScene } .filter { $0.activationState == .foregroundActive } .first? .keyWindow? .rootViewController } else { - UIApplication.shared.connectedScenes + topViewController = UIApplication.shared.connectedScenes .compactMap { $0 as? UIWindowScene } .filter { $0.activationState == .foregroundActive } .first? .windows .first(where: \.isKeyWindow)? .rootViewController - } else { break } + } + guard let topViewController else { break } if let nav = topViewController as? UINavigationController { nav.visibleViewController?.present(LogHistoryViewController(), animated: true) } else { From 574189367261321b627dc28f1a246fe9d03c476d Mon Sep 17 00:00:00 2001 From: baegteun Date: Tue, 23 Apr 2024 23:52:43 +0900 Subject: [PATCH 5/6] =?UTF-8?q?:lipstick:=20::=20[#496]=20LogHistoryViewCo?= =?UTF-8?q?ntroller=20/=20Navigation=20=ED=83=80=EC=9D=B4=ED=8B=80=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/App/Sources/Application/AppDelegate.swift | 5 +++-- .../Sources/LogHistory/LogHistoryViewController.swift | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Projects/App/Sources/Application/AppDelegate.swift b/Projects/App/Sources/Application/AppDelegate.swift index f46f5afee..11e19a6fa 100644 --- a/Projects/App/Sources/Application/AppDelegate.swift +++ b/Projects/App/Sources/Application/AppDelegate.swift @@ -128,10 +128,11 @@ private extension AppDelegate { .rootViewController } guard let topViewController else { break } + let logHistoryViewController = UINavigationController(rootViewController: LogHistoryViewController()) if let nav = topViewController as? UINavigationController { - nav.visibleViewController?.present(LogHistoryViewController(), animated: true) + nav.visibleViewController?.present(logHistoryViewController, animated: true) } else { - topViewController.present(LogHistoryViewController(), animated: true) + topViewController.present(logHistoryViewController, animated: true) } default: diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift index 05d30a53c..8e1b6dddf 100644 --- a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryViewController.swift @@ -50,6 +50,8 @@ snapshot.appendItems(logs, toSection: 0) logHistoryTableViewDiffableDataSource.apply(snapshot, animatingDifferences: true) + + navigationItem.title = "애널리틱스 히스토리" } } From 933ae99e06ac0d2b879600ba5ed07ad7fac41bc2 Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 24 Apr 2024 13:40:46 +0900 Subject: [PATCH 6/6] =?UTF-8?q?:goal=5Fnet:=20::=20[#496]=20LogHistoryView?= =?UTF-8?q?Controller=20=EC=A4=91=EB=B3=B5=20present=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/App/Sources/Application/AppDelegate.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Projects/App/Sources/Application/AppDelegate.swift b/Projects/App/Sources/Application/AppDelegate.swift index 11e19a6fa..2fbb1d412 100644 --- a/Projects/App/Sources/Application/AppDelegate.swift +++ b/Projects/App/Sources/Application/AppDelegate.swift @@ -129,9 +129,10 @@ private extension AppDelegate { } guard let topViewController else { break } let logHistoryViewController = UINavigationController(rootViewController: LogHistoryViewController()) - if let nav = topViewController as? UINavigationController { + if let nav = topViewController as? UINavigationController, + !(nav.visibleViewController is LogHistoryViewController) { nav.visibleViewController?.present(logHistoryViewController, animated: true) - } else { + } else if !(topViewController is LogHistoryViewController) { topViewController.present(logHistoryViewController, animated: true) }