Enhance Your SwiftUI Live Preview Workflow with PreviewLogger

Sergey Nes
Level Up Coding
Published in
4 min readMar 31, 2023

--

The Crime Scene

Update on Apr 4 2023

I usually have no problem to switch between different platforms and programming languages. It typically takes me just a few days to get back into the flow of things when transitioning between Swift and Kotlin, Studio and XCode. However, this time was different. After working on an Android project for a month, I returned to Xcode and faced a challenge. The Live Preview wasn’t displaying the logs I needed to debug my project. Though inconvenient, I thought, it’s similar to not being able to see logs in Android Studio Compose Preview, so let’s stay determined to find a solution!

I spent a few hours trying to resolve the issue and eventually found a solution, which I decided to share in this article. However, a few days later, after restarting Xcode, I realized that the preview was actually printing the logs just fine. It turned out to be an Xcode glitch, which was a bit frustrating, but I learned an important lesson. When switching between different tools and platforms, it’s essential to shift your mindset and approach accordingly!

I chose to keep this article published as a testament to the time I spent on unnecessary work and as a friendly reminder to fellow engineers: before you reinvent the wheel, take a moment to look around — maybe it’s already been invented!

“Debugging is like being the detective in a crime movie where you’re also the murderer.” — Filipe Fortes

Developing new components in large projects can be time-consuming, especially when you need to debug issues or monitor the state of your views. Thankfully, SwiftUI introduced live previews, allowing you to see your changes in real-time without needing to run the whole app in a simulator or on a device. But, what if you need to debug an issue or see logs during development? Unfortunately, Xcode’s SwiftUI preview doesn’t provide an out-of-the-box solution for displaying logs or debugging views. In this article, we’ll explore a custom solution called PreviewLogger that enables you to see logs directly in your SwiftUI previews and learn how it can save you time during development.

Benefits of PreviewLogger

  • Saves time by avoiding the need to run the whole app just for testing a single component.
  • Provides live feedback on the state of your component, especially when working with complex logic.
  • Simplifies debugging by displaying logs directly within the preview.

Step-by-step guide to implementing PreviewLogger

  1. Create a new Swift file called PreviewLogger.swift in your project.
  2. Copy and paste the following code into the PreviewLogger.swift file:
import SwiftUI

struct ContentView: View {
@EnvironmentObject var previewLogger: PreviewLogger

var body: some View {
Button("Print Log") {
previewLogger.log("Button tapped in SwiftUI preview!")
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(PreviewLogger.shared)
.previewWrapper()
}
}

struct PreviewWrapper: ViewModifier {
@EnvironmentObject var previewLogger: PreviewLogger

func body(content: Content) -> some View {
VStack {
content
List(previewLogger.logMessages.reversed(), id: \.self) { log in
Text(log)
}
}
}
}

extension View {
func previewWrapper() -> some View {
self.modifier(PreviewWrapper())
.environmentObject(PreviewLogger.shared)
}
}

class PreviewLogger: ObservableObject {
static let shared = PreviewLogger()
@Published private(set) var logMessages: [String] = []

func log(_ message: String) {
logMessages.append(message)
}
}

3. In your component’s preview, use the .previewWrapper() modifier and provide the PreviewLogger.shared environment object:

struct YourComponent_Previews: PreviewProvider {
static var previews: some View {
YourComponent()
.environmentObject(PreviewLogger.shared)
.previewWrapper()
}
}

4. Inject the PreviewLogger instance in your component using the @EnvironmentObject property wrapper:

struct YourComponent: View {
@EnvironmentObject var previewLogger: PreviewLogger

// ...
}

5. Use the previewLogger.log() method to log messages, which will appear directly below the preview:

Button("Print Log") {
previewLogger.log("Button tapped in SwiftUI preview!")
}
Preview Logger in the Actions

The PreviewLogger provides a convenient way to display logs directly in your SwiftUI previews, enabling a faster and more efficient development process. This custom solution lets you quickly test and debug individual components without running the entire app on a simulator or device. Give it a try in your projects and see how it can save you time and improve your workflow.

It’s worth noting that PreviewLogger may not be suitable for use within the rendering cycle of a view. According to Apple’s documentation on SwiftUI, modifying state or calling functions that participate in the rendering cycle from within a preview context can result in undefined behavior and should generally be avoided.

Lastly, I want to emphasize that any feedback on this article or PreviewLogger is greatly appreciated. Whether you have suggestions for improvement, alternative solutions, or just want to share your thoughts, please feel free to do so in the comments section below. I value your input and look forward to hearing from you!

https://gist.github.com/sergenes/538dd76d7b6e247a3b763663cc2ed64b

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job

--

--