Skip to content

Commit

Permalink
Merge pull request #15 from wlsdms0122/develop
Browse files Browse the repository at this point in the history
[RELEASE] Logma/1.7.0
  • Loading branch information
wlsdms0122 authored Aug 18, 2024
2 parents 0dfcf90 + a2fec8d commit dccf4d3
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.8
// swift-tools-version:5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
12 changes: 12 additions & 0 deletions Sources/Logmo/Extension/String+Range.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// String+Range.swift
//
//
// Created by jsilver on 2022/01/31.
//

import Foundation

extension String {
var nsRange: NSRange { (self as NSString).range(of: self) }
}
44 changes: 44 additions & 0 deletions Sources/Logmo/Extension/String+Regex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// String+Regex.swift
//
//
// Created by jsilver on 2022/01/25.
//

import Foundation

extension String {
func regex(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = []
) -> [NSTextCheckingResult] {
guard nsRange.length > 0 else { return [] }

guard let regex = try? NSRegularExpression(
pattern: pattern,
options: regexOptions
) else { return [] }

return regex.matches(
in: self,
options: matchingOptions,
range: nsRange
)
}

func regex(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = []
) -> Bool {
let match = regex(
pattern,
regexOptions: regexOptions,
matchingOptions: matchingOptions
)
.first { $0.range == nsRange }

return match != nil
}
}
9 changes: 4 additions & 5 deletions Sources/Logmo/Logmo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class Logmo {
public static let shared = Logmo()

private let settings: Settings
private var customSettingContent: AnyView?
private var customSetting: () -> AnyView = { AnyView(EmptyView()) }

private var window: UIWindow?

Expand All @@ -38,7 +38,7 @@ public class Logmo {

let viewController = UIHostingController(
rootView: LogmoView(settings: settings) { [weak self] in
self?.customSettingContent
self?.customSetting()
}
)
viewController.view.backgroundColor = .clear
Expand All @@ -55,10 +55,9 @@ public class Logmo {
window = nil
}

public func configure(@ViewBuilder customSetting content: () -> some View) {
customSettingContent = AnyView(content())
public func configure<CustomSetting: View>(@ViewBuilder customSetting content: @escaping () -> CustomSetting?) {
customSetting = { AnyView(content()) }
}


public func setTitle(_ title: String = "") {
settings.setTitle(title)
Expand Down
38 changes: 33 additions & 5 deletions Sources/Logmo/Model/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ protocol SettingsDelegate: AnyObject {

@MainActor
class Settings: ObservableObject {
private static let settingSuite = "logmo"
private static let SUITE_NAME = "logmo"

private static let filterPatternsKey = "filterPatterns"
private static let showFiltersKey = "showFilters"
private static let showSearchBarKey = "showSearchBar"

Expand All @@ -28,22 +29,32 @@ class Settings: ObservableObject {
/// Logs.
@Published
private(set) var logs: [Log] = []
/// Filtered logs.
@Published
private(set) var filteredLogs: [Log] = []
/// Status of log file exporting.
@Published
private(set) var isExporting: Bool = false

/// Setting: logmo regex patterns
@Published
var filterPatterns: [String] = [] {
didSet {
userDefaults?.set(filterPatterns, forKey: Settings.filterPatternsKey)
}
}
/// Setting flag: show log filter bar.
@Published
var showFilters: Bool = true {
didSet {
userDefaults?.set(showFilters, forKey: "showFilters")
userDefaults?.set(showFilters, forKey: Settings.showFiltersKey)
}
}
/// Setting flag: show log search bar.
@Published
var showSearchBar: Bool = false {
didSet {
userDefaults?.set(showSearchBar, forKey: "showSearchBar")
userDefaults?.set(showSearchBar, forKey: Settings.showSearchBarKey)
}
}

Expand All @@ -53,10 +64,13 @@ class Settings: ObservableObject {

// MARK: - Initializer
init() {
let userDefaults = UserDefaults(suiteName: Settings.settingSuite)
let userDefaults = UserDefaults(suiteName: Settings.SUITE_NAME)

self._filterPatterns = .init(
initialValue: userDefaults?.object(forKey: Settings.filterPatternsKey) as? [String] ?? []
)
self._showFilters = .init(
initialValue: userDefaults?.bool(forKey: Settings.settingSuite) ?? true
initialValue: userDefaults?.bool(forKey: Settings.showFiltersKey) ?? true
)
self._showSearchBar = .init(
initialValue: userDefaults?.bool(forKey: Settings.showSearchBarKey) ?? true
Expand All @@ -72,10 +86,24 @@ class Settings: ObservableObject {

func addLog(_ log: Log) {
logs.append(log)

guard filterPatterns.isEmpty || !filterPatterns.contains(where: { pattern in log.message.regex(pattern, regexOptions: [.dotMatchesLineSeparators]) }) else { return }
filteredLogs.append(log)
}

func addFilterPattern(_ pattern: String) {
guard !pattern.isEmpty && !filterPatterns.contains(pattern) else { return }
filterPatterns.append(pattern)
}

func removeFilterPattern(_ pattern: String) {
guard let index = filterPatterns.firstIndex(of: pattern) else { return }
filterPatterns.remove(at: index)
}

func clear() {
logs.removeAll()
filterPatterns.removeAll()
}

func export() async {
Expand Down
7 changes: 3 additions & 4 deletions Sources/Logmo/Util/Iterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import SwiftUI
struct Iterator<Data, Content: View>: View {
// MARK: - View
var body: some View {
let views = data.enumerated().map { content($0, $1) }
ForEach(0..<views.count, id: \.self) {
views[$0]
ForEach(Array(data.enumerated()), id: \.offset) { offset, data in
content(offset, data)
}
}

Expand All @@ -23,7 +22,7 @@ struct Iterator<Data, Content: View>: View {
// MARK: - Initializer
init(
_ data: [Data],
@ViewBuilder content: @escaping (Int, Data) -> Content
@ViewBuilder content: @escaping (_ offset: Int, _ data: Data) -> Content
) {
self.data = data
self.content = content
Expand Down
93 changes: 93 additions & 0 deletions Sources/Logmo/View/LogFilterView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// LogFilterView.swift
//
//
// Created by jsilver on 8/16/24.
//

import SwiftUI

struct LogFilterView: View {
// MARK: - View
var body: some View {
let canAddPattern = !pattern.isEmpty && !settings.filterPatterns.contains(pattern)

List {
Section {
TextField("Regex Pattern", text: $pattern)
.autocorrectionDisabled()
.autocapitalization(.none)
Button {
settings.addFilterPattern(pattern)
pattern = ""
} label: {
Text("Add Filter")
}
.disabled(!canAddPattern)
} footer: {
Text("Filters logs that match the regex pattern.")
}

if !settings.filterPatterns.isEmpty {
Section {
ForEach(settings.filterPatterns.reversed(), id: \.self) { pattern in
HStack {
Text(pattern)
Spacer()
Button {
shouldRemovePattern = pattern
} label: {
Image(systemName: "trash")
.foregroundColor(.primary)
}
}
.alert(isPresented: .init {
shouldRemovePattern != nil
} set: { isShow in
guard !isShow else { return }
shouldRemovePattern = nil
}) { [shouldRemovePattern] in
Alert(
title: Text("Remove filter pattern"),
message: Text("Are you sure you want to remove filter pattern?"),
primaryButton: .cancel(),
secondaryButton: .destructive(Text("Remove")) {
guard let shouldRemovePattern else { return }
settings.removeFilterPattern(shouldRemovePattern)
}
)
}
}
} header: {
Text("Filters (\(settings.filterPatterns.count))")
}
}
}
.navigationTitle("Log Filter")
.animation(.default, value: settings.filterPatterns)
}

// MARK: - Property
@State
private var shouldRemovePattern: String?

@State
private var pattern: String = ""
@StateObject
private var settings: Settings

// MARK: - Initializer
init(_ settings: Settings) {
self._settings = .init(wrappedValue: settings)
}

// MARK: - Public

// MARK: - Private
}

#if DEBUG
#Preview {
LogFilterView(.init())
}
#endif
2 changes: 1 addition & 1 deletion Sources/Logmo/View/LogmoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ struct LogmoView<CustomSetting: View>: View {

@ViewBuilder
private func ContentView() -> some View {
let logs = settings.logs.filter { levelFilter.contains($0.level) }
let logs = settings.filteredLogs.filter { levelFilter.contains($0.level) }
.filter { query.isEmpty || $0.message.contains(query) }
let lastItemID = logs.count - 1

Expand Down
12 changes: 11 additions & 1 deletion Sources/Logmo/View/SettingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ struct SettingView<CustomContent: View>: View {
@ViewBuilder
private func SettingSection() -> some View {
Section {
Item(
symbol: "line.3.horizontal.decrease.circle",
title: "Log Filter"
) {
NavigationLink {
LogFilterView(settings)
} label: {
EmptyView()
}
}
Item(
symbol: "square.and.arrow.up",
title: "Export Logs"
Expand Down Expand Up @@ -117,6 +127,7 @@ struct SettingView<CustomContent: View>: View {
guard let url = Env.licenseURL else { return }
UIApplication.shared.open(url)
}
.foregroundColor(.secondary.opacity(0.6))
}
} header: {
Text("About")
Expand Down Expand Up @@ -147,7 +158,6 @@ struct SettingView<CustomContent: View>: View {
Text(title)
Spacer()
label()
.foregroundColor(.secondary.opacity(0.6))
}
}

Expand Down

0 comments on commit dccf4d3

Please sign in to comment.