diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKit.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKit.xcscheme new file mode 100644 index 0000000..222c885 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKit.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKitTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKitTests.xcscheme new file mode 100644 index 0000000..7d90eee --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/ComposableLogsSheetKitTests.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.resolved b/Package.resolved index cd1cea7..77e5e4c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,23 @@ { "pins" : [ + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "aa3e575929f2bcc5bad012bd2575eae716cbcdf7", + "version" : "0.8.0" + } + }, + { + "identity" : "logssheetkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/riiid/LogsSheetKit", + "state" : { + "revision" : "5bb7cc405d718e86f9362e6a335230e2e295801b", + "version" : "0.9.0" + } + }, { "identity" : "spindicator", "kind" : "remoteSourceControl", @@ -8,6 +26,60 @@ "revision" : "43600857729d9efff046dc6673e6f94e095882c7", "version" : "1.6.4" } + }, + { + "identity" : "swift-case-paths", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-case-paths", + "state" : { + "revision" : "7346701ea29da0a85d4403cf3d7a589a58ae3dee", + "version" : "0.9.2" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", + "version" : "1.0.3" + } + }, + { + "identity" : "swift-composable-architecture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-composable-architecture", + "state" : { + "revision" : "9ea8c763061287052a68d5e6723fed45e898b7d9", + "version" : "0.40.2" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "819d9d370cd721c9d87671e29d947279292e4541", + "version" : "0.6.0" + } + }, + { + "identity" : "swift-identified-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-identified-collections", + "state" : { + "revision" : "bfb0d43e75a15b6dfac770bf33479e8393884a36", + "version" : "0.4.1" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "30314f1ece684dd60679d598a9b89107557b67d9", + "version" : "0.4.1" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 11c9b37..17cd3ab 100644 --- a/Package.swift +++ b/Package.swift @@ -3,26 +3,31 @@ import PackageDescription let package = Package( - name: "LogsSheetKit", + name: "ComposableLogsSheetKit", platforms: [ .iOS(.v14) ], products: [ .library( - name: "LogsSheetKit", - targets: ["LogsSheetKit"]), + name: "ComposableLogsSheetKit", + targets: ["ComposableLogsSheetKit"]), ], dependencies: [ - .package(url: "https://github.com/ivanvorobei/SPIndicator", .upToNextMajor(from: "1.6.0")) + .package(url: "https://github.com/pointfreeco/swift-composable-architecture", .upToNextMajor(from: "0.40.0")), + .package(url: "https://github.com/riiid/LogsSheetKit", .upToNextMajor(from: "0.9.0")) ], targets: [ .target( - name: "LogsSheetKit", + name: "ComposableLogsSheetKit", dependencies: [ - "SPIndicator" + "LogsSheetKit", + .product(name: "ComposableArchitecture", package: "swift-composable-architecture") ]), .testTarget( - name: "LogsSheetKitTests", - dependencies: ["LogsSheetKit"]), + name: "ComposableLogsSheetKitTests", + dependencies: [ + "ComposableLogsSheetKit", + "LogsSheetKit" + ]), ] ) diff --git a/Sources/ComposableLogsSheetKit/Extensions/TCA/Reducer+Log.swift b/Sources/ComposableLogsSheetKit/Extensions/TCA/Reducer+Log.swift new file mode 100644 index 0000000..627025f --- /dev/null +++ b/Sources/ComposableLogsSheetKit/Extensions/TCA/Reducer+Log.swift @@ -0,0 +1,40 @@ +// +// Reducer+Log.swift +// +// +// Created by Yessen Yermukhanbet on 2022/10/05. +// Copyright © 2022 Riiid Inc. All rights reserved. +// + +import Foundation +import ComposableArchitecture + +extension Reducer where State: LoggableState { + /// Saves each action run through the reducer as the log message if debug build configuration is on + /// - Parameters: + /// - isDebug: is debug configuration on + public func log(isDebug: Bool) -> Self { + .init { state, action, environment in + if isDebug { + state.logs.append(.init(message: "\(action)")) + } + return self.run(&state, action, environment) + } + } + + /// Saves the custom log message if debug build configuration is on + /// - Parameters: + /// - isDebug: is debug configuration on + /// - message: custom log message + public func log( + isDebug: Bool, + with message: @escaping ((Action) -> String) + ) -> Self { + .init { state, action, environment in + if isDebug { + state.logs.append(.init(message: message(action))) + } + return self.run(&state, action, environment) + } + } +} diff --git a/Sources/ComposableLogsSheetKit/LoggableState.swift b/Sources/ComposableLogsSheetKit/LoggableState.swift new file mode 100644 index 0000000..e2a66f4 --- /dev/null +++ b/Sources/ComposableLogsSheetKit/LoggableState.swift @@ -0,0 +1,30 @@ +// +// File.swift +// +// +// Created by Yessen Yermukhanbet on 2022/10/05. +// Copyright © 2022 Riiid Inc. All rights reserved. +// + +import Foundation +import LogsSheetKit + +public protocol LoggableState: Equatable { + var logs: [ActionLog] { get set } +} + +extension LoggableState { + var logs: [ActionLog] { + get { + return GlobalLogs.shared.logs + } + set { + GlobalLogs.shared.logs = newValue + } + } +} + +struct GlobalLogs { + static var shared: GlobalLogs = GlobalLogs() + var logs: [ActionLog] = [] +} diff --git a/Sources/LogsSheetKit/ActionLog.swift b/Sources/LogsSheetKit/ActionLog.swift deleted file mode 100644 index 88d51c9..0000000 --- a/Sources/LogsSheetKit/ActionLog.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ActionLog.swift -// -// -// Created by Yessen Yermukhanbet on 2022/10/04. -// Copyright © 2022 Riiid Inc. All rights reserved. -// - -import Foundation - -public struct ActionLog: Equatable, Identifiable { - public var id: UUID = .init() - public var message: String - public var timeStamp: Date = Date() - - public init(message: String) { - self.message = message - } -} diff --git a/Sources/LogsSheetKit/LogsSheet.swift b/Sources/LogsSheetKit/LogsSheet.swift deleted file mode 100644 index eeb82c4..0000000 --- a/Sources/LogsSheetKit/LogsSheet.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// LogsSheet.swift -// -// Created by Jiyeon Song on 2022/06/17. -// Copyright © 2022 Riiid Inc. All rights reserved. -// - -import SPIndicator -import SwiftUI - -public struct LogsSheet: View { - @Environment(\.presentationMode) private var presentationMode: Binding - - private let logs: [ActionLog] - private let clearAction: () -> Void - - @State private var isAscending: Bool = false - @State private var showCopiedIndicator: Bool = false - @State private var searchText: String = "" - - public init(logs: [ActionLog], clearAction: @escaping () -> Void) { - self.logs = logs - self.clearAction = clearAction - } - - private var filteredLogs: [ActionLog] { - let sortedLogs: [ActionLog] = logs - .sorted(by: { - isAscending - ? $0.timeStamp < $1.timeStamp - : $0.timeStamp > $1.timeStamp - }) - return searchText.isEmpty - ? sortedLogs - : sortedLogs.filter { $0.message.localizedCaseInsensitiveContains(searchText) } - } - - public var body: some View { - NavigationView { - List { - ForEach(filteredLogs) { log in - Section(header: Text(log.timeStamp, formatter: dateFormatter)) { - ScrollView(.horizontal, showsIndicators: false) { - Button( - action: { - UIPasteboard.general.string = dateFormatter.string(from: log.timeStamp) + "\n" + log.message - showCopiedIndicator = true - }, - label: { - Text(log.message) - .font(Font.system(size: 12, design: .monospaced)) - .foregroundColor(.primary) - .multilineTextAlignment(.leading) - .padding() - } - ) - } - .listRowInsets(.init()) - } - } - } - .SPIndicator( - isPresent: $showCopiedIndicator, - title: "Copied to clipboard", - duration: 3.0 - ) - .listStyle(.grouped) - .navigationBarTitle("Logs for Debug") - .modified { - if #available(iOS 15.0, *) { - $0.searchable(text: $searchText) - } else { - $0 - } - } - .toolbar { - ToolbarItemGroup(placement: .bottomBar) { - Button( - action: { - UIPasteboard.general.string = logs - .map { dateFormatter.string(from: $0.timeStamp) + "\n" + $0.message } - .joined(separator: "\n") - showCopiedIndicator = true - }, - label: { - Text("Copy All") - } - ) - Spacer() - Button( - action: clearAction, - label: { - Text("Clear") - } - ) - } - ToolbarItem(placement: .automatic) { - Button( - action: { isAscending.toggle() }, - label: { - Image(systemName: isAscending ? "arrow.up.arrow.down.circle.fill" : "arrow.up.arrow.down.circle") - .renderingMode(.template) - .foregroundColor(.secondary) - } - ) - } - ToolbarItem(placement: .destructiveAction) { - Button( - action: { presentationMode.wrappedValue.dismiss() }, - label: { - Image(systemName: "xmark.circle.fill") - .renderingMode(.template) - .foregroundColor(.secondary) - } - ) - } - } - } - } -} - -private let dateFormatter: ISO8601DateFormatter = { - let formatter: ISO8601DateFormatter = .init() - formatter.timeZone = TimeZone.autoupdatingCurrent - return formatter -}() - -// MARK: Previewer - -struct LogsSheet_Previews: PreviewProvider { - static private let logsStub: [ActionLog] = [ - ActionLog(message: "stub action log"), - ActionLog(message: "stub action log"), - ActionLog(message: "stub action log") - ] - - static var previews: some View { - Group { - LogsSheet(logs: [], clearAction: {}) - .previewDisplayName("LogsSheet | empty") - - LogsSheet(logs: logsStub, clearAction: {}) - .previewDisplayName("LogsSheet | non empty") - } - } -} diff --git a/Sources/LogsSheetKit/View+Modified.swift b/Sources/LogsSheetKit/View+Modified.swift deleted file mode 100644 index 2afbe61..0000000 --- a/Sources/LogsSheetKit/View+Modified.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// View+Modified.swift -// -// Created by Dongkyu Kim on 2022/06/30. -// Copyright © 2022 Riiid Inc. All rights reserved. -// - -import SwiftUI - -extension View { - func modified(@ViewBuilder modification: (Self) -> Modified) -> some View { - return modification(self) - } -} diff --git a/Tests/ComposableLogsSheetKitTests/LoggableStateTests.swift b/Tests/ComposableLogsSheetKitTests/LoggableStateTests.swift new file mode 100644 index 0000000..d3b5daa --- /dev/null +++ b/Tests/ComposableLogsSheetKitTests/LoggableStateTests.swift @@ -0,0 +1,42 @@ +// +// LoggableStateTests.swift +// +// +// Created by Yessen Yermukhanbet on 2022/10/05. +// Copyright © 2022 Riiid Inc. All rights reserved. +// + +import XCTest +import LogsSheetKit +@testable import ComposableLogsSheetKit + +final class LoggableStateTests: XCTestCase { + + override func setUp() { + super.setUp() + GlobalLogs.shared.logs = [] // Clean up before each test + } + + private struct StubState: LoggableState { } + + func testLoggableStateAtTheBeginningLogsEmpty() { + let stub: StubState = StubState() + XCTAssertTrue(stub.logs.count == 0) + } + + func testLoggableStateAppendsLogs() { + var stub: StubState = StubState() + + let logs: [ActionLog] = [ + ActionLog(message: "stub"), + ActionLog(message: "stub"), + ActionLog(message: "stub"), + ] + + for log in logs { + stub.logs.append(log) + } + + XCTAssertTrue(stub.logs.count == logs.count) + } +} diff --git a/Tests/LogsSheetKitTests/LogsSheetKitTests.swift b/Tests/LogsSheetKitTests/LogsSheetKitTests.swift deleted file mode 100644 index 317f789..0000000 --- a/Tests/LogsSheetKitTests/LogsSheetKitTests.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest -@testable import LogsSheetKit - -final class LogsSheetKitTests: XCTestCase { - func testExample() throws { - } -}