About SwiftUI

Make sure you have installed the minimum package version: 1.17.0

If your LogRocket server is self hosted, mobile SDK version 1.17.0 requires a minimum server version of 16.462.0

About Swift UI

The LogRocket SDK will work with SwiftUI in the same way as the existing iOS SDK outlined in these iOS docs, with the following known limitations.

SwiftUI Differences from UIKit

View Names

As with UIKit, LogRocket can automatically infer information about the view hierarchy in SwiftUI. Because views are defined declaratively in SwiftUI, which handles management of the rendered view hierarchy under the hood, automatically detected view selectors contain the more generic names of the underlying views that comprise the rendered interface, rather than custom SwiftUI View names or the SwiftUI Library names for declarative views. Manually tagged view names will be injected into these selectors, and searchable in LogRocket filters.

Custom view tags are added using the lrAddClass modifier, which takes a string value that is treated as the ID for a given View in its selector.

func lrAddClass(_ viewSelector: LocalizedStringKey) -> some View
Parameters

viewSelector
The string to be included in the View's recorded selector

Return Value

A view with an associated string detectable by the LogRocket SDK for selector generation

Navigation

As with UIKit, SwiftUI screens may not be meaningful on their own. As such, we recommend using the Manual Page Identification API to best track navigation events.

Individual view redaction

Because the SwiftUI accessibilityIdentifier modifier does not apply the passed value to the views comprising the rendered application interface, it cannot be used to apply redactionTags to individual private views. Instead, our custom lrHide modifier can be used to mark SwiftUI Views for redaction.

// Definition
func lrHide() -> some View

// Example Usage
Text("Green").foregroundColor(.green).tag(Color.green).lrHide()
Return Value

A view with an associated redaction-indicator detectable by the LogRocket SDK to prevent said view and its contents from being recorded

Individual view allowlisting

Individual subviews of redacted SwiftUI Views can be allowed for view capture using our customer lrShow modifier

// Definition
func lrShow() -> some View

// Example Usage
// Label icon and first button are captured, label text and second button are not
ScrollView {
  Label(title: {
    Text("Hidden Label Text").lrHide()
  }, icon: { 
    Image(systemName: "text.magnifyingglass")
  }).lrShow()

  Button("Allowed Button", action: {}).lrShow()
  Button("Hidden Button", action: {})

}.lrHide()
Return Value

A view with an associated allow-indicator detectable by the LogRocket SDK to allow capture of said view and its contents.

Limitations

🚧

SwiftUI Previews

We are aware of a compatibility issue with SwiftUI View Previews when importing LogRocket, and are working on resolving it. At this time, you can view previews locally by importing LogRocket conditionally on non-debug builds, or you can simply comment out LogRocket code when needing to view previews.

Sanitization and view tagging

While complete masking rules still apply, such as Automatically Sanitize Text and Sanitize Network Data,, there are current limitations with redacting individual views.

Container Views

Because the following SwiftUI view types do not comprise actual views, and are instead strictly function as layout information handlers for constructing the hierarchy of their contained views, tagging and redaction is not currently supported for them. Tags and sanitization must be applied directly to individual non-container views. Container views include:

  • Form
  • ForEach
  • Grids (Grid, LazyHGrid, LazyVGrid)
  • Group
  • LabeledContent
  • List
  • Stacks (HStack, VStack, ZStack, LazyHStack, LazyVStack)
  • ViewThatFits
Custom Views

In order to redact a custom SwiftUI View from session replay, lrHide must be applied inside of the body definition for the custom View. Applying the modifier to the invocation of the custom may not redact all contents of the custom View.

This same limitation also applies to manual view tagging. In order for all contents of a custom view to include the passed string in their recorded selectors, the lrAddClass must be applied inside of the custom view's body definition, not to the invocation of the custom view inside of another view's body

Example

struct LandmarkItem: View {
  var landmarkName: String
  var shouldHide: Bool?
  
  func getBody() -> any View {
    if shouldHide == true {
      return (
        ScrollView{
          Image(landmarkName.lowercased())
          Spacer()
          Text(landmarkName)
        }.padding(.horizontal).lrHide()
      )
    }
    return (
      ScrollView{
        Image(landmarkName.lowercased())
        Spacer()
        Text(landmarkName)
      }.padding(.horizontal)
    )
  }

  var body: some View {
    AnyView(getBody())
  }
}

struct ContentView: View {
  var body: some View {
    VStack {
      // INCORRECT
      // This line redacts only part of the entire LandmarkItem view,
      // the Text portion in this case
      LandmarkItem(landmarkName: "Chincoteague").lrHide()
      
      // CORRECT
      // This line redacts the entire LandmarkItem by applying lrHide
      // to the outermost SwiftUI library View, HStack in this case
      LandmarkItem(landmarkName: "Umbagog", shouldHide: true) 
    }
  }
}


Picker with pickerStyle(.menu)

When a Picker element with the style menu is tagged to be hidden with lrHide, the SDK does not automatically redact the contents of the dropdown. Because the manner in which SwiftUI's rendering of the actual menu piece that appears after tapping on the Picker's target element does not allow for us to programmatically detect the appearance of that menu view, context menus must be redacted altogether from session recordings (meaning all context menus across the application are redacted) or not at all. Redacting context menus requires the additional SDK.initialize configuration value redactMenus: true

Example

In the following view implementation, the target (outlined in teal) for the Color Menu view will always be redacted from session recordings, because the Picker view is modified with lrHide. The target for the Number Menu view will not be redacted from session recordings.

When redactMenus: true is included in the Configuration sent to SDK.initialize, both Context Menus will be redacted from session recordings (regardless of whether the Picker element by which they were generated has the lrHide modifier.

When redactMenus: false or redactMenus is excluded from the Configuration, neither Context Menu will be redacted, despite the Picker element that generates the Color Menu being modified with lrHide.


var body: some View {
  VStack {
    Text("Pick a color, any color of the rainbow")
    Picker("Color Menu", selection: $color) {
      Text("Red").tag(Color.red)
      Text("Orange").tag(Color.orange)
      Text("Yellow").tag(Color.yellow)
      Text("Green").tag(Color.green)
      Text("Blue").tag(Color.blue)
      Text("Indigo").tag(Color.indigo)
      Text("Purple").tag(Color.purple)
    }.pickerStyle(.menu).lrHide()

    Text("Pick a number between 1 and 5")
    Picker("Number Menu", selection: $number) {
      Text("1").tag(1)
      Text("2").tag(2)
      Text("3").tag(3)
      Text("4").tag(4)
      Text("5").tag(5)
    }.pickerStyle(.menu)
  }.onAppear({
    SDK.initialize(
      Configuration(
        appID: 'my-org/my-app',
        redactMenus: true
      )
    )
	})
}

Coming Soon

Clickmap support