Skip to content

Instantly share code, notes, and snippets.

@ynagatomo
Created November 25, 2023 06:58
Show Gist options
  • Save ynagatomo/847df66dfdf9d9f35a4ef8687ebbe064 to your computer and use it in GitHub Desktop.
Save ynagatomo/847df66dfdf9d9f35a4ef8687ebbe064 to your computer and use it in GitHub Desktop.
Custom Debug Window in visionOS
//
// ContentView.swift
// DebugWindow
//
// Created by Yasuhito Nagatomo on 2023/11/25.
//
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
#if DEBUG
@Environment(DebugState.self) private var debugState
#endif
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
let x = 100
var body: some View {
VStack {
Model3D(named: "Scene", bundle: realityKitContentBundle)
.padding(.bottom, 50)
Text("Hello, world!")
Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
Button(action: {
debugState.logger("new debug message. x = \(x)")
}, label: {
Text("Add debug message (simple)")
})
.padding()
Button(action: {
debugState.logger2("new debug message. x * 2 = \(x * 2)")
}, label: {
Text("Add debug message (file, line)")
})
.padding()
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}
}
}
#Preview(windowStyle: .automatic) {
ContentView()
}
//
// DebugPanelView.swift
// DebugWindow
//
// Created by Yasuhito Nagatomo on 2023/11/25.
//
import SwiftUI
struct DebugPanelView: View {
let debugState: DebugState
var body: some View {
VStack {
Text("Debug Panel").font(.title).padding()
Form {
Section("Actions") {
Button(action: {}, label: { Text("Do something")})
.buttonStyle(.bordered)
}
Section("App Status") {
VStack {
Text("Status A: xxx")
Text("Status B: yyy")
}
}
}
Text("Logs").font(.title3)
ScrollView {
VStack(alignment: .leading) {
ForEach(debugState.logs.reversed()) { log in
Text("\(log.number): \(log.message)")
}
}
}
.padding()
.background(.ultraThickMaterial,
in: RoundedRectangle(cornerRadius: 10))
Spacer()
}
.padding()
}
}
//
// DebugState.swift
// DebugWindow
//
// Created by Yasuhito Nagatomo on 2023/11/25.
//
import Foundation
@Observable
final class DebugState {
struct Log: Identifiable {
var id: Int { number }
let number: Int
let message: String
}
var logs = [Log]()
func logger(_ message: String) {
logs.append(Log(number: logs.count, message: message))
}
func logger2(_ message: String,
function: String = #function, file: String = #file, line: Int = #line) {
let url = URL(fileURLWithPath: file)
let filename = url.lastPathComponent
let thread = Thread.isMainThread ? "(m)" : "(-)"
logger("\(filename): \(function): L\(line): T\(thread): " + message)
}
}
//
// DebugWindowApp.swift
// DebugWindow
//
// Created by Yasuhito Nagatomo on 2023/11/25.
//
import SwiftUI
@main
struct DebugWindowApp: App {
@Environment(\.openWindow) private var openWindow
#if DEBUG
@State private var debugState = DebugState()
#endif
var body: some Scene {
WindowGroup {
ContentView()
#if DEBUG
.environment(debugState)
.onAppear {
openWindow(id: "debug")
}
#endif
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
#if DEBUG
WindowGroup(id: "debug") {
DebugPanelView(debugState: debugState)
}
.defaultSize(width: 400, height: 600)
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment