69
votes

So I tried to put a print statement while debugging in a SwiftUI View.

print("landmark: \(landmark)")

In the following body.

var body: some View {
    NavigationView {
        List {
            Toggle(isOn: $userData.showFavoritesOnly) {
                Text("Favorite only")
            }
            ForEach(landmarkData) { landmark in
                print("landmark: \(landmark)")
                if !self.userData.showFavoritesOnly || landmark.isFavorite {
                    NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
        }
       .navigationBarTitle(Text("Landmarks"))            
    }
}

Compiler errors out: Xcode compiler error

So, what is the proper way to print to console in SwiftUI?

EDIT: I made Landmark conform to CustomStringConvertible:

struct Landmark: Hashable, Codable, Identifiable, CustomStringConvertible {

var description: String { name+"\(id)" }

var id: Int
var name: String
.....

I still get the "String is not convertible to any" error. Should it work now?

12
Does your landmark conform to CustomStringConvertible?BlueSpud
Your question is about printing but you can't compile since you have an error. Fix the error first and I'm sure the print will work fine.andromedainiative
I edited the question. For some reason I had to clean and build again, then the other error when away.zumzum
Have you tried to add return before NavigationButton?Artem Novichkov
On Xcode 12.4 all prints end up in the debug area only when I use a real device.Johan

12 Answers

88
votes

At least in Xcode 12/Swift 5.3, you can easily add a print statement anywhere in a function builder by simply storing its return value in a wildcard, effectively ignoring it:

let _ = print("hi!")

No setup or other verbosity needed!

48
votes

Here's a helper Print( ... ) View that acts like a print( ... ) function but within a View

Put this in any of your view files

extension View {
    func Print(_ vars: Any...) -> some View {
        for v in vars { print(v) }
        return EmptyView()
    }
}

and use inside of body like so

Print("Here I am", varOne, varTwo ...)

or inside a ForEach {} like so

self.Print("Inside ForEach", varOne, varTwo ...)

Note: you might need to put Print() into a Group {} when combining with existing views

37
votes

Try right-clicking on the live preview play button and selecting 'Debug Preview from the popup

8
votes

You can print in the body structure but to do so you have to explcitly return the view you want to render. Normally in SwiftUI, the body property implicitly returns the view. For example, this will throw an error when you try to print:

struct SomeView: View {
  @State var isOpen = false

  var body: some View {
    print(isOpen) // error thrown here

    VStack {
      // other view code
    |
  }
}

But if we explicitly return the view we want then it will work e.g.

struct SomeView: View {
  @State var isOpen = false

  var body: some View {
    print(isOpen) // this ok because we explicitly returned the view below

     // Notice the added 'return' below
     return VStack {
      // other view code
    }
  }
}

The above will work well if you're looking to view how state or environment objects are changing before returning your view, but if you want to print something deeper down within the view you are trying to return, then I would go with @Rok Krulec answer.

4
votes

It is possible to use print() remembering that all SwiftUI View content are (a) implicit closures and (b) it is highly recommended to decompose views as much as possible to have simple structure, so it might look like the following...

struct Model: Identifiable {
    let value: String
    var id: String {
        value
    }
    init (_ value: String) {
        self.value = value
    }
}

struct TestView: View {
    @State var showFavoritesOnly = false
    @State var listData: [Model] = [Model("one"), Model("two"), Model("three")]

    var body: some View {
        NavigationView {
            List {
                Toggle(isOn: $showFavoritesOnly) {
                    Text("Favorite only")
                }
                ForEach(listData) { data in
                    self.rowView(data: data)
                }
            }
        }
    }

    private func rowView(data: Model) -> some View {
#if DEBUG
        print(">> \(data.value)")
#endif
        return NavigationLink(destination: Text("Details")) {
            Text("Go next from \(data.value)")
        }
    }
}

... and right clicking in Preview to select run as Debug Preview we get:

2019-10-31 14:28:03.467635+0200 Test[65344:11155167] [Agent] Received connection, creating agent
2019-10-31 14:28:04.472314+0200 Test[65344:11155168] [Agent] Received display message
>> one
>> two
>> three
2
votes

Here you go. It will just work like simple print but inside a view.

      func printv( _ data : Any)-> EmptyView{
       print(data)
       return EmptyView()
      }

and use it like that

struct ContentView: View {
var body: some View  {
    VStack() {
        
        Text("hello To SwiftUI")
        
        printv("its easy to code in SwiftUI")
        
        Text("And Good to have you here")
    }
  } 
}
1
votes

It can be generalized to:

extension View {
    func Perform(_ block: () -> Void) -> some View {
        block()
        return EmptyView()
    }
}

So in your example:

ForEach(landmarkData) { landmark in
    Perform { print("landmark: \(landmark)") }
    if !self.userData.showFavoritesOnly || landmark.isFavorite {
        NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
            LandmarkRow(landmark: landmark)
        }
    }
}
1
votes

Very easy way to debug your Preview:

  1. Open your Swift project in Xcode 11.
  2. Right-click (or Control-click) on the Live Preview button in the bottom right corner of the preview.
  3. Select Debug Preview.

How to debug your SwiftUI previews in Xcode

0
votes

You can't because you're in a computed property. You need for example a button and in the action you define the print. Or work with breakpoints

0
votes

You can not print in body structure i.e. a structure which is some view type.For print you need to make function out of body structure and call it using button or something else.

0
votes

// Try this, add a 'return' on a view then the 'print' can stay alive in.

struct ContentView: View {
    var num: Int = 1
    
    var body: some View {
        print(num)
        
        return  Text("hello")
    
    }
}
0
votes

The following extension on View is as intuitive as print because it's made to replicate the default print(_:separator:terminator:) function signature & behavior.

extension View {
    func printUI(_ args: Any..., separator: String = " ", terminator: String = "\n") -> EmptyView {
        let output = args.map(String.init(describing:)).joined(separator: separator)
        print(output, terminator: terminator)
        return EmptyView()
    }
}

Usage Example:

struct ContentView: View {
    var body: some View {
        VStack {
            printUI("ContentView", "1")
            printUI("ContentView", "2", separator: ", ", terminator: "\n.\n.\n")
            printUI("ContentView", "3", separator: "; ")

            Text("Hello, World!")
        }
    }
}
Console Output:

ContentView 1
ContentView, 2
.
.
ContentView; 3