Post

Replies

Boosts

Views

Activity

Mobile Analytics in SwiftUI
Hi, I am working on a mobile analytics library which identifies and captures the user interactions in iOS-apps automatically. This is achieved by swizzling the UIApplication's 'sendEvent' method. Every time the user touches a button or makes any other interactions, the swizzled 'sendEvent' is called and with our custom code, we record the event and identify the UI-Control by reading the target view's 'accessibilityLabel' property.  This library works well with UIKit, it captures all the events and identifies the UI-controls that are triggering the event.   But when I use the same approach in SwiftUI, I get the events captured, but not the UI-element identity. For example, If there are three buttons in an app and one is touched by the user, then I get the generic button touch event in swizzled 'sendEvent'.. But I could not figure out which button is touched. It's because the underlying target view sent in 'sendEvent' method is 'CGDrawingView' - this doesn't hold the 'accessibilityLabel' property value which is set in the Button by using a ViewModifier.   Is there a way to read accessibilityLabel property of the Button from its sendEvent in SwiftUI? OR is there any other unique identifier that I can use for SwiftUI views? OR Is there any other standard way to implement mobile analytics in SwiftUI? I have given the sample code here for reference. Thank you. // MARK: Swizzling Code extension UIApplication {   @objc dynamic func newSendEvent(_ event: UIEvent) {     newSendEvent(event)           if (event.allTouches != nil){       let touches: SetUITouch = event.allTouches!       let touch: UITouch = touches.first!               OperationQueue.main.addOperation(){         if let tView = touch.view {           print("------------------------------------------")           print(Mirror(reflecting: tView).subjectType)           print("accessibilityLabel : \(tView.accessibilityLabel ?? "null")")         }       }     }   } } // MARK: - Demo App Code struct ContentView: View {   @State private var segmentValue = 0   @State private var textValue = ""   @State private var sliderValue: Float = 0       var body: some View {     Form {       Section(header: Text("UI CONTROLS").font(.title).padding(.vertical, 0.0)) {                   TextField("TextField", text: $textValue)           .accessibilityLabel("Text1")                   VStack(spacing: 5) {           Text("Segment Control (\(segmentValue))")           Picker(selection: $segmentValue, label: Text("Segment Control").padding(10.0)) {             Text("Red").tag(0)             Text("Green").tag(1)             Text("Blue").tag(2)           }           .pickerStyle(SegmentedPickerStyle())           .padding(10.0)           .accessibilityLabel(Text("SegmentControl1"))         }                   Slider(value: $sliderValue, in: 0...100, step: 1)           .padding(10.0)           .accessibilityLabel("Slider1")                   Button(action: {           print("Button1 pressed")         }, label: {           Text("Button 1")         })         .accessibilityLabel("Button1")       }     }     .onAppear(){       let uiAppClass = UIApplication.self       let currentSendEvent = class_getInstanceMethod(uiAppClass, #selector(uiAppClass.sendEvent))       let newSendEvent = class_getInstanceMethod(uiAppClass, #selector(uiAppClass.newSendEvent))       method_exchangeImplementations(currentSendEvent!, newSendEvent!)       print("sendEvent Swizzled")     }   } }
4
3
3.4k
Mar ’21