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")
}
}
}