Search code examples
performanceswiftuiswiftui-text

Conditionally rendering Text view with a large string causes performance issue


My SwiftUI app has a network log screen that shows network requests sent and recorded by the app. There is a button to toggle the visibility of each request. Requests are serialized and stored as strings, so they can be displayed on this screen.

Some of the response objects are very large — as response objects tend to be sometimes — which is causing a 1-2 second delay when the “toggle visibility” button is pressed. Is there a way to optimize the performance of the Text view that renders the content?

struct NetworkLogsScreen: View {
    var logs: [NetworkLogEntry]

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(logs.indices.reversed(), id: \.self) { index in
                    LogItem(logItem: logs[index])
                }
            }
        }
        .navigationTitle("Network Requests")
    }
}

struct LogItem: View {
    var logItem: NetworkLogEntry

    @State var isExpanded: Bool = false

    var body: some View {
        VStack {
            HStack {
                Text(logItem.timestamp.formatted())
                Spacer()
                Button {
                    self.isExpanded.toggle()
                } label: {
                    Text("toggle visibility")
                }
            }
            if self.isExpanded {
                Text(logItem.responseBody)
            }
        }
    }
}

Solution

  • The fix:

    Ended up using a substring of the actual content. This fixed the issue. So you can no longer see the entire content. I added a button that copies the entire response to your clipboard and this satisfied my needs. The UIPasteboard functionality hangs sometimes, but I supposed that's what you get for copying 1500+ lines of text.

    I did see some improvement from using a .frame modifier setting the maxHeight and adding a truncationMode to that, but it did not completely fix the issue.

    So the modified code is basically...

    if self.isExpanded {
        Text(logItem.responseBody.prefix(2000))
    }
    

    Would love to figure out if there's a way to fix the issue without modifying the user-facing options.