Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: manually mask swiftui views #202

Merged
merged 12 commits into from
Oct 14, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- add `postHogMask` view modifier to manually mask a SwiftUI view ([#202](https://github.com/PostHog/posthog-ios/pull/202))
ioannisj marked this conversation as resolved.
Show resolved Hide resolved

## 3.12.7 - 2024-10-09

- add appGroupIdentifier in posthog config ([#207](https://github.com/PostHog/posthog-ios/pull/207))
Expand Down
4 changes: 4 additions & 0 deletions PostHog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
69F5181A2BAC81FC00F52C14 /* UITextInputTraits+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F518192BAC81FC00F52C14 /* UITextInputTraits+Util.swift */; };
69F518382BB2BA0100F52C14 /* PostHogSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F518372BB2BA0100F52C14 /* PostHogSwizzler.swift */; };
69F5183A2BB2BA8300F52C14 /* UIApplicationTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F518392BB2BA8300F52C14 /* UIApplicationTracker.swift */; };
DAD5DD0C2CB6DEF30087387B /* PostHogMaskViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -375,6 +376,7 @@
69F518192BAC81FC00F52C14 /* UITextInputTraits+Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextInputTraits+Util.swift"; sourceTree = "<group>"; };
69F518372BB2BA0100F52C14 /* PostHogSwizzler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSwizzler.swift; sourceTree = "<group>"; };
69F518392BB2BA8300F52C14 /* UIApplicationTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationTracker.swift; sourceTree = "<group>"; };
DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogMaskViewModifier.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -557,6 +559,7 @@
69F518372BB2BA0100F52C14 /* PostHogSwizzler.swift */,
693E977A2C625208004B1030 /* PostHogPropertiesSanitizer.swift */,
69ED1A5B2C7F15F300FE7A91 /* PostHogSessionManager.swift */,
DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */,
69ED1A872C89B73100FE7A91 /* PostHogSwiftUIViewModifiers.swift */,
69ED1A9E2C8F451B00FE7A91 /* PostHogPersonProfiles.swift */,
);
Expand Down Expand Up @@ -1108,6 +1111,7 @@
69F517F32BAC734300F52C14 /* UIColor+Util.swift in Sources */,
3AE3FB3F29924F4F00AFFC18 /* PostHogConfig.swift in Sources */,
69F518382BB2BA0100F52C14 /* PostHogSwizzler.swift in Sources */,
DAD5DD0C2CB6DEF30087387B /* PostHogMaskViewModifier.swift in Sources */,
690FF0C52AEFAE8200A0B06B /* PostHogLegacyQueue.swift in Sources */,
3AE3FB332991388500AFFC18 /* PostHogQueue.swift in Sources */,
690FF0B52AEBBD3C00A0B06B /* DictUtils.swift in Sources */,
Expand Down
67 changes: 67 additions & 0 deletions PostHog/PostHogMaskViewModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// PostHogMaskViewModifier.swift
// PostHog
//
// Created by Yiannis Josephides on 09/10/2024.
//

#if os(iOS) && canImport(SwiftUI)

import SwiftUI

public extension View {
func postHogMask(_ value: Bool = true) -> some View {
ioannisj marked this conversation as resolved.
Show resolved Hide resolved
modifier(PostHogMaskViewModifier(enabled: value))
}
}

private struct PostHogMaskViewTagger: UIViewRepresentable {
func makeUIView(context _: Context) -> PostHogMaskViewTaggerView {
PostHogMaskViewTaggerView()
}

func updateUIView(_: PostHogMaskViewTaggerView, context _: Context) {
// nothing
}
ioannisj marked this conversation as resolved.
Show resolved Hide resolved
}

private struct PostHogMaskViewModifier: ViewModifier {
let enabled: Bool

func body(content: Content) -> some View {
content.background(viewTagger)
}

@ViewBuilder
private var viewTagger: some View {
if enabled {
PostHogMaskViewTagger()
}
}
}

private class PostHogMaskViewTaggerView: UIView {
override func didMoveToSuperview() {
marandaneto marked this conversation as resolved.
Show resolved Hide resolved
super.didMoveToSuperview()
superview?.phIsManuallyMasked = true
}
}

private var phIsManuallyMaskedKey: UInt8 = 0
extension UIView {
var phIsManuallyMasked: Bool {
get {
objc_getAssociatedObject(self, &phIsManuallyMaskedKey) as? Bool ?? false
}

set {
objc_setAssociatedObject(
self,
&phIsManuallyMaskedKey,
newValue as Bool?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
#endif
14 changes: 9 additions & 5 deletions PostHog/Replay/PostHogReplayIntegration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@

var data: [String: Any] = ["width": width, "height": height]

if let screenName = screenName {
if let screenName {
data["href"] = screenName
}

Expand Down Expand Up @@ -232,6 +232,11 @@
}
}

// manually masked views through view modifier `PostHogMaskViewModifier`
if view.phIsManuallyMasked {
maskableWidgets.append(view.toAbsoluteRect(parent))
}

if !view.subviews.isEmpty {
for child in view.subviews {
if !child.isVisible() {
Expand Down Expand Up @@ -306,11 +311,11 @@
}

private func hasText(_ text: String?) -> Bool {
if let text = text, !text.isEmpty {
return true
if let text, !text.isEmpty {
true
} else {
// if there's no text, there's nothing to mask
return false
false
}
}

Expand Down Expand Up @@ -529,7 +534,6 @@
private protocol AnyObjectUIHostingViewController: AnyObject {}

extension UIHostingController: AnyObjectUIHostingViewController {}

#endif

// swiftlint:enable cyclomatic_complexity
11 changes: 7 additions & 4 deletions PostHogExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ struct ContentView: View {
ContentView()
} label: {
Text("Infinite navigation")
}.accessibilityIdentifier("ph-no-capture")
}
.postHogMask()

Button("Show Sheet") {
showingSheet.toggle()
Expand All @@ -113,12 +114,14 @@ struct ContentView: View {
RepresentedExampleUIView()
}

Text("Sensitive text!!").accessibilityIdentifier("ph-no-capture")
Text("Sensitive text!!").postHogMask()
ioannisj marked this conversation as resolved.
Show resolved Hide resolved
Button(action: incCounter) {
Text(String(counter))
}.accessibilityIdentifier("ph-no-capture-id").accessibilityLabel("ph-no-capture")
}
.postHogMask()

TextField("Enter your name", text: $name).accessibilityLabel("ph-no-capture")
TextField("Enter your name", text: $name)
.postHogMask()
Text("Hello, \(name)!")
Button(action: triggerAuthentication) {
Text("Trigger fake authentication!")
Expand Down
Loading