Skip to content

Commit

Permalink
Fix rebase and tune the install screen
Browse files Browse the repository at this point in the history
  • Loading branch information
bamx23 committed Jul 20, 2024
1 parent 63227a5 commit 474a5df
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 111 deletions.
121 changes: 82 additions & 39 deletions Samples/Common/Sources/LibraryBridge/InstallBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,54 +38,33 @@ public enum BasePath: String, CaseIterable {
public class InstallBridge: ObservableObject {
public typealias MonitorType = KSCrashRecording.MonitorType

public enum InstallationError: Error, LocalizedError {
case kscrashError(String)
case unexpectedError(String)
case alreadyInstalled

public var errorDescription: String? {
switch self {
case .kscrashError(let message), .unexpectedError(let message):
return message
case .alreadyInstalled:
return "KSCrash is already installed"
}
}
}

private static func setBasePath(_ value: BasePath) {
let basePath = value.basePaths.first.flatMap { $0 + "/KSCrash" }
print("Setting KSCrash base path to: \(basePath ?? "<default>")")
KSCrash.setBasePath(basePath)
}

public static let allRawMonitorTypes: [(String, MonitorType)] = [
("machException", .machException),
("signal", .signal),
("cppException", .cppException),
("nsException", .nsException),
("mainThreadDeadlock", .mainThreadDeadlock),
("userReported", .userReported),
("system", .system),
("applicationState", .applicationState),
("zombie", .zombie),
("memoryTermination", .memoryTermination),
]

public static let allCompositeMonitorTypes: [(String, MonitorType)] = [
("all", .all),
("fatal", .fatal),
("experimental", .experimental),
("debuggerUnsafe", .debuggerUnsafe),
("asyncSafe", .asyncSafe),
("optional", .optional),
("asyncUnsafe", .asyncUnsafe),
("debuggerSafe", .debuggerSafe),
("productionSafe", .productionSafe),
("productionSafeMinimal", .productionSafeMinimal),
("required", .required),
("manual", .manual),
]

private var config: KSCrashConfiguration
private var disposables = Set<AnyCancellable>()

@Published public var basePath: BasePath = .default

public func configBinding<T>(for keyPath: WritableKeyPath<KSCrashConfiguration, T>) -> Binding<T> {
.init { [config] in
config[keyPath: keyPath]
} set: { [weak self] val in
self?.objectWillChange.send()
self?.config[keyPath: keyPath] = val
}

}
@Published public var installed: Bool = false
@Published public var error: InstallationError?

public init() {
config = .init()
Expand All @@ -97,10 +76,74 @@ public class InstallBridge: ObservableObject {
}

public func install() {
KSCrash.shared().install(with: config)
guard !installed else {
error = .alreadyInstalled
return
}

do {
try KSCrash.shared.install(with: config)
installed = true
} catch let error as KSCrashInstallError {
let message = error.localizedDescription
print("Failed to install KSCrash: \(message)")
self.error = .kscrashError(message)
} catch {
let message = error.localizedDescription
print("Unexpected error during KSCrash installation: \(message)")
self.error = .unexpectedError(message)
}
}
}

// An utility method to simplify binding of config fields
extension InstallBridge {
public func configBinding<T>(for keyPath: WritableKeyPath<KSCrashConfiguration, T>) -> Binding<T> {
.init { [config] in
config[keyPath: keyPath]
} set: { [weak self] val in
self?.objectWillChange.send()
self?.config[keyPath: keyPath] = val
}
}
}

// Monitor types are specified here
extension InstallBridge {
public static let allRawMonitorTypes: [(MonitorType, String, String)] = [
(.machException, "Mach Exception", "Low-level system exceptions"),
(.signal, "Signal", "UNIX-style signals indicating abnormal program termination"),
(.cppException, "C++ Exception", "Unhandled exceptions in C++ code"),
(.nsException, "NSException", "Unhandled Objective-C exceptions"),
(.mainThreadDeadlock, "Main Thread Deadlock", "Situations where the main thread becomes unresponsive"),
(.memoryTermination, "Memory Termination", "Termination due to excessive memory usage"),
(.zombie, "Zombie", "Attempts to access deallocated objects"),
(.userReported, "User Reported", "Custom crash reports"),
(.system, "System", "Additional system information added to reports"),
(.applicationState, "Application State", "Application lifecycle added to report"),
]

public static let allCompositeMonitorTypes: [(MonitorType, String)] = [
(.all, "All"),
(.fatal, "Fatal"),

(.productionSafe, "Production-safe"),
(.productionSafeMinimal, "Production-safe Minimal"),
(.experimental, "Experimental"),

(.required, "Required"),
(.optional, "Optional"),

(.debuggerSafe, "Debugger-safe"),
(.debuggerUnsafe, "Debugger-unsafe"),

(.asyncSafe, "Async-safe"),
(.asyncUnsafe, "Async-unsafe"),

(.manual, "Manual"),
]
}

extension BasePath {
var basePaths: [String] {
switch self {
Expand Down
59 changes: 0 additions & 59 deletions Samples/Common/Sources/LibraryBridge/RecordingSample.swift

This file was deleted.

11 changes: 8 additions & 3 deletions Samples/Common/Sources/SampleUI/Components/MonitorTypeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,19 @@ struct MonitorTypeView: View {
var body: some View {
List {
Section(header: Text("Monitors")) {
ForEach(InstallBridge.allRawMonitorTypes, id: \.1.rawValue) { (name, monitor) in
ForEach(InstallBridge.allRawMonitorTypes, id: \.0.rawValue) { (monitor, name, description) in
Toggle(isOn: monitorBinding(monitor)) {
Text(name)
VStack(alignment: .leading) {
Text(name)
Text(description)
.font(.caption)
.foregroundStyle(Color.secondary)
}
}
}
}
Section(header: Text("Composite")) {
ForEach(InstallBridge.allCompositeMonitorTypes, id: \.0) { (name, monitor) in
ForEach(InstallBridge.allCompositeMonitorTypes, id: \.1) { (monitor, name) in
HStack {
Text(name)
Spacer()
Expand Down
26 changes: 22 additions & 4 deletions Samples/Common/Sources/SampleUI/SampleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,37 @@ import CrashTriggers
public struct SampleView: View {
public init() { }

@State var installed = false
@ObservedObject var installBridge = InstallBridge()

@State private var installSkipped = false

public var body: some View {
NavigationView {
if installed {
if installBridge.installed || installSkipped {
List {
Section {
if installSkipped {
Button("Back to Install") {
installSkipped = false
}
} else {
Text("KSCrash is installed successfully")
.foregroundStyle(Color.secondary)
}
}

NavigationLink("Crash", destination: CrashView())
NavigationLink("Report", destination: ReportingView())
}
.navigationTitle("KSCrash Sample")
.transition(.slide)
} else {
InstallView(installed: $installed)
.navigationTitle("Install")
InstallView(
bridge: installBridge,
installSkipped: $installSkipped
)
.navigationTitle("Install KSCrash")
.transition(.slide)
}
}
}
Expand Down
20 changes: 14 additions & 6 deletions Samples/Common/Sources/SampleUI/Screens/InstallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ import SwiftUI
import LibraryBridge

struct InstallView: View {
@ObservedObject var bridge = InstallBridge()
@ObservedObject var bridge: InstallBridge

@Binding var installed: Bool
@Binding var installSkipped: Bool

@State private var showingInstallAlert = false

var body: some View {
List {
Button("Install") {
bridge.install()
installed = true
}
.foregroundColor(.primary)

Section(header: Text("Static Config")) {
Picker("Base path", selection: $bridge.basePath) {
Expand Down Expand Up @@ -68,9 +68,17 @@ struct InstallView: View {
}

Button("Skip install") {
installed = true
installSkipped = true
}
.foregroundColor(.red)
.foregroundStyle(Color.red)
}
.alert(isPresented: $showingInstallAlert) {
Alert(
title: Text("Installation Failed"),
message: Text(bridge.error?.errorDescription ?? ""),
dismissButton: .default(Text("OK"))
)
}
.onReceive(bridge.$error) { if $0 != nil { showingInstallAlert = true } }
}
}

0 comments on commit 474a5df

Please sign in to comment.